diff --git a/README.rst b/README.rst
index ca2210c5..29961c66 100644
--- a/README.rst
+++ b/README.rst
@@ -33,6 +33,8 @@ There are three portlets/tiles available for filtering:
is activated on a contenttype. See installation notes below)
``Collection Result Listing Sort``
a list of indexes where the user can sort the filtered result listing
+``Filter Info``
+ Displays customisable information about the current search or current context
Filter Results of Collections
@@ -90,6 +92,15 @@ Simply do this somewhere in your buildout::
collective.collectionfilter[geolocation]
...
+Filter Info support
+-------------------
+
+Info display (tile or portlet) can be used to
+- display the current search or filters.
+- as a title replacement when using mosaic with information about the current search
+- an alternative form of navigation with links to reset the search to a single option
+- a form of navigation on content using values of the currently viewed object to search
+ for other similar content
Overloading GroupByCriteria
---------------------------
diff --git a/base.cfg b/base.cfg
index 89d11bfb..e154b19b 100644
--- a/base.cfg
+++ b/base.cfg
@@ -1,6 +1,16 @@
[buildout]
# extensions = mr.developer
versions = versions
+# parts += vscode
+
+
+[vscode]
+recipe = collective.recipe.vscode
+eggs = ${test:eggs} ${instance:eggs}
+enable-flake8 = true
+enable-black = true
+generate-envfile = true
+
[code-analysis]
directory = ${buildout:directory}/src/collective/collectionfilter
diff --git a/src/collective/collectionfilter/baseviews.py b/src/collective/collectionfilter/baseviews.py
index 8be003ca..14111df9 100644
--- a/src/collective/collectionfilter/baseviews.py
+++ b/src/collective/collectionfilter/baseviews.py
@@ -1,8 +1,13 @@
# -*- coding: utf-8 -*-
+import weakref
from Acquisition import aq_inner
from collective.collectionfilter import PLONE_VERSION
-from collective.collectionfilter.filteritems import get_filter_items
-from collective.collectionfilter.filteritems import ICollectionish
+from collective.collectionfilter.filteritems import (
+ get_filter_items,
+ ICollectionish,
+ _build_url,
+ _build_option,
+)
from collective.collectionfilter.interfaces import IGroupByCriteria
from collective.collectionfilter.query import make_query
from collective.collectionfilter.utils import base_query
@@ -10,12 +15,14 @@
from collective.collectionfilter.utils import safe_encode
from collective.collectionfilter.utils import safe_iterable
from collective.collectionfilter.vocabularies import TEXT_IDX
-from plone import api
+from collective.collectionfilter.vocabularies import get_conditions
+from collective.collectionfilter.vocabularies import EMPTY_MARKER
from plone.api.portal import get_registry_record as getrec
from plone.app.uuid.utils import uuidToCatalogBrain
from plone.app.uuid.utils import uuidToObject
+from plone.dexterity.utils import iterSchemata
from plone.i18n.normalizer.interfaces import IIDNormalizer
-from plone.memoize import instance
+from plone.memoize import instance, ram
from plone.uuid.interfaces import IUUID
from Products.CMFPlone.utils import get_top_request
from Products.CMFPlone.utils import safe_unicode
@@ -23,8 +30,10 @@
from zope.component import getUtility
from zope.component import queryUtility
from zope.i18n import translate
+from zope.schema import getFieldsInOrder
from zope.schema.interfaces import IVocabularyFactory
-
+from Products.CMFCore.Expression import Expression, getExprContext
+from plone import api
import json
@@ -37,11 +46,15 @@
HAS_GEOLOCATION = False
+def empty_ref(self):
+ return None
+
+
class BaseView(object):
"""Abstract base filter view class."""
_collection = None
- _top_request = None
+ _top_request = empty_ref # prevent loops
@property
def settings(self):
@@ -74,9 +87,9 @@ def reload_url(self):
@property
def top_request(self):
- if not self._top_request:
- self._top_request = get_top_request(self.request)
- return self._top_request
+ if self._top_request() is None:
+ self._top_request = weakref.ref(get_top_request(self.request))
+ return self._top_request()
@property
def collection_uuid(self):
@@ -278,6 +291,222 @@ def ajax_url(self):
return ajax_url
+def _exp_cachekey(method, self, target_collection, request):
+ return (
+ target_collection,
+ json.dumps(request),
+ self.settings.view_name,
+ self.settings.as_links,
+ )
+
+
+def _field_title_cache_key(method, self, field_id):
+ return (
+ field_id[0],
+ self.context.getTypeInfo().id
+ )
+
+
+class BaseInfoView(BaseView):
+
+ # TODO: should just cache on request?
+ # @instance.memoize
+ # @ram.cache(_exp_cachekey)
+ def get_expression_context(self, collection, request_params):
+ count_query = {}
+ query = base_query(request_params)
+ collection_url = collection.absolute_url()
+ # TODO: take out the search
+ # TODO: match them to indexes and get proper names
+ # TODO: format values properly
+ # TODO: do we want to read out sort too?
+ count_query.update(query)
+ # TODO: delay evaluating this unless its needed
+ # TODO: This could be cached as same result total appears in other filter counts
+ catalog_results_fullcount = ICollectionish(collection).results(
+ make_query(count_query), request_params
+ )
+ results = len(catalog_results_fullcount)
+
+ # Clean up filters and values
+ if "collectionfilter" in query:
+ del query["collectionfilter"]
+ groupby_criteria = getUtility(IGroupByCriteria).groupby
+ q = []
+ for group_by, value in query.items():
+ if group_by not in groupby_criteria:
+ continue
+ # TODO: we actually have idx not group_by
+ idx = groupby_criteria[group_by]['index']
+ value = safe_decode(value)
+ current_idx_value = safe_iterable(value)
+ # Set title from filter value with modifications,
+ # e.g. uuid to title
+ display_modifier = groupby_criteria[group_by].get("display_modifier", None)
+ titles = []
+ for filter_value in current_idx_value:
+ title = filter_value
+ if filter_value is not EMPTY_MARKER and callable(display_modifier):
+ title = safe_decode(display_modifier(filter_value, idx))
+ # TODO: still no nice title for filter indexes? Should be able to get from query builder
+ # TODO: we don't know if filter is AND/OR to display that detail
+ # TODO: do we want no follow always?
+ # TODO: should support clearing filter? e.g. if single value, click to remove?
+ # Build filter url query
+ query_param = urlencode(
+ safe_encode({group_by: filter_value}), doseq=True
+ )
+ url = "/".join(
+ [
+ it
+ for it in [
+ collection_url,
+ self.settings.view_name,
+ "?" + query_param if query_param else None,
+ ]
+ if it
+ ]
+ )
+ # TODO: should have option for nofollow?
+ if self.settings.as_links:
+ titles.append(u'{}'.format(url, title))
+ else:
+ titles.append(title)
+ q.append((group_by, titles))
+
+ # Set up context for running templates
+ expression_context = getExprContext(
+ collection,
+ )
+ expression_context.setLocal("results", results)
+ expression_context.setLocal("query", q)
+ expression_context.setLocal("search", query.get("SearchableText", ""))
+ return expression_context
+
+ def info_contents(self):
+ request_params = self.top_request.form or {}
+ expression_context = self.get_expression_context(
+ self.collection.getObject(), request_params
+ )
+
+ parts_vocabulary_factory = getUtility(
+ IVocabularyFactory, "collective.collectionfilter.TemplateParts"
+ )
+ parts_vocabulary = parts_vocabulary_factory(self.context)
+
+ parts = []
+ for template in self.settings.template_type:
+ text = None
+ try:
+ # This vocab term lookup will throw a LookupError if the term doesn't exist in the built-ins
+ template_definition_term = parts_vocabulary.getTerm(template)
+ exp = template_definition_term.value
+ # TODO: precompile templates
+ text = Expression(exp)(expression_context)
+
+ if isinstance(text, list):
+ text = u", ".join(text)
+
+ except LookupError:
+ text = template
+
+ if text:
+ parts.append(text)
+
+ line = u" ".join(parts)
+ # TODO: should be more generic i18n way to do this?
+ line = line.replace(u" ,", u",").replace(" :", ":")
+ return line
+
+ def is_content_context(self):
+ if not self.settings.context_aware:
+ return False
+
+ if self.collection.getObject() == self.context:
+ return False
+
+ return True
+
+ def get_fields_to_display(self):
+ fields = self.settings.context_aware_fields
+ # TODO: Get the friendly name for the group_by instead of the id
+ return [
+ (field, getattr(self.context, field))
+ for field in fields
+ if hasattr(self.context, field)
+ ]
+
+ # Cache this function based on the index id and the dexterity type
+ @ram.cache(_field_title_cache_key)
+ def get_field_title(self, field):
+ """
+ Field is a tuple, where the first index is the name of the field and the second the values for that field
+ Returns the friendly version of the title when possible, falling
+ back to the ID if a firiendly name can't be found.
+ """
+ index_id = field[0]
+
+ for schema in iterSchemata(context=self.context):
+ for field_id, field_object in getFieldsInOrder(schema=schema):
+ if field_id == index_id:
+ return field_object.title
+
+ return index_id
+
+ def get_field_values(self, field):
+ content_value = field[1]
+ index = field[0]
+ groupby_criteria = getUtility(IGroupByCriteria).groupby
+ request_params = self.top_request.form
+ request_params = safe_decode(request_params)
+ extra_ignores = [index, index + "_op"]
+ urlquery = base_query(request_params, extra_ignores)
+ collection = self.collection.getObject()
+
+ # TODO: Refactor the following copied lines from collective.collectionfitler.filteritems into a function
+ field_value = content_value() if callable(content_value) else content_value
+ # decode it to unicode
+ field_value = safe_decode(field_value)
+ # Make sure it's iterable, as it's the case for e.g. the subject index.
+ field_values = safe_iterable(field_value)
+ # allow excluding or extending the field_valueue per index
+ groupby_modifier = groupby_criteria[index].get("groupby_modifier", None)
+
+ if not groupby_modifier:
+ groupby_modifier = lambda values, cur, narrow: values # noqa: E731
+
+ field_values = groupby_modifier(field_values, field_value, False)
+
+ field_data = []
+ for value in field_values:
+ url = _build_url(collection_url=collection.absolute_url(), urlquery=urlquery, filter_value=value, current_idx_value=[], idx=index, filter_type="single")
+ data = _build_option(filter_value=value, url=url, current_idx_value=[value], groupby_options=groupby_criteria[index])
+ field_data.append(data)
+
+ return field_data
+
+ @property
+ def is_available(self):
+ target_collection = self.collection
+ if target_collection is None:
+ return False
+ request_params = self.top_request.form or {}
+ expression_context = self.get_expression_context(
+ target_collection.getObject(), request_params
+ )
+
+ conditions = dict((k, (t, e)) for k, t, e in get_conditions())
+ if not self.settings.hide_when:
+ return True
+ for cond in self.settings.hide_when:
+ _, exp = conditions.get(cond)
+ # TODO: precompile templates
+ res = Expression(exp)(expression_context)
+ if not res:
+ return True
+ return False
+
+
if HAS_GEOLOCATION:
class BaseMapsView(BaseView):
diff --git a/src/collective/collectionfilter/configure.zcml b/src/collective/collectionfilter/configure.zcml
index 1871ad93..aec5ba23 100644
--- a/src/collective/collectionfilter/configure.zcml
+++ b/src/collective/collectionfilter/configure.zcml
@@ -47,6 +47,14 @@
component=".vocabularies.SortOnIndexesVocabulary"
name="collective.collectionfilter.SortOnIndexes"
/>
+
+
+
+
+
+
+
+
+
+
+ - ${python:view.get_field_title(field)}
+
+
+ -
+
+ ${field_value/title}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/collective/collectionfilter/portlets/info.py b/src/collective/collectionfilter/portlets/info.py
new file mode 100644
index 00000000..58490b7e
--- /dev/null
+++ b/src/collective/collectionfilter/portlets/info.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
+from collective.collectionfilter import _
+from collective.collectionfilter.baseviews import BaseInfoView
+from collective.collectionfilter.interfaces import ICollectionFilterInfo # noqa
+from collective.collectionfilter.portlets import BasePortletRenderer
+from plone.app.portlets.portlets import base
+from plone.portlets.interfaces import IPortletDataProvider
+from zope.interface import implementer
+
+
+class ICollectionFilterInfoPortlet(ICollectionFilterInfo, IPortletDataProvider): # noqa
+ """Portlet interface based on ICollectionFilterSchema"""
+
+
+@implementer(ICollectionFilterInfoPortlet)
+class Assignment(base.Assignment):
+
+ header = u""
+ target_collection = None
+ view_name = None
+ content_selector = "#content-core"
+ template_type = []
+ hide_when = []
+ as_links = True
+ context_aware = False
+
+ def __init__(
+ self,
+ header=u"",
+ target_collection=None,
+ view_name=None,
+ content_selector="#content-core",
+ template_type=[],
+ hide_when=[],
+ as_links=True,
+ context_aware=False,
+ context_aware_fields=[]
+ ):
+ self.header = header
+ self.target_collection = target_collection
+ self.view_name = view_name
+ self.content_selector = content_selector
+ self.template_type = template_type
+ self.hide_when = hide_when
+ self.as_links = as_links
+ self.context_aware = context_aware
+ self.context_aware_fields = context_aware_fields
+
+ @property
+ def portlet_id(self):
+ """Return the portlet assignment's unique object id."""
+ return id(self)
+
+ @property
+ def title(self):
+ if self.header:
+ return self.header
+ else:
+ return _(u"Collection Filter Search Info")
+
+
+class Renderer(BasePortletRenderer, BaseInfoView):
+ render = ViewPageTemplateFile("info.pt")
+
+
+class AddForm(base.AddForm):
+
+ schema = ICollectionFilterInfoPortlet
+ label = _(u"Add Collection Filter Info Portlet")
+ description = _(u"This portlet shows information about the filter selected")
+
+ def create(self, data):
+ return Assignment(**data)
+
+
+class EditForm(base.EditForm):
+
+ schema = ICollectionFilterInfoPortlet
+ label = _(u"Edit Collection Filter Info Portlet")
+ description = _(u"This portlet shows information about the filter selected")
diff --git a/src/collective/collectionfilter/portlets/profiles/default/portlets.xml b/src/collective/collectionfilter/portlets/profiles/default/portlets.xml
index e5de5618..f354794a 100644
--- a/src/collective/collectionfilter/portlets/profiles/default/portlets.xml
+++ b/src/collective/collectionfilter/portlets/profiles/default/portlets.xml
@@ -15,4 +15,9 @@
title="Collection Filter Result Sorting"
description="This portlet allows sorting of the filtered result listing."
/>
+
diff --git a/src/collective/collectionfilter/portlets/profiles/portlets_with_maps/portlets.xml b/src/collective/collectionfilter/portlets/profiles/portlets_with_maps/portlets.xml
index a14ab667..9c238b4d 100644
--- a/src/collective/collectionfilter/portlets/profiles/portlets_with_maps/portlets.xml
+++ b/src/collective/collectionfilter/portlets/profiles/portlets_with_maps/portlets.xml
@@ -15,6 +15,11 @@
title="Collection Filter Result Sorting"
description="This portlet allows sorting of the filtered result listing."
/>
+
header {\n font-weight: bold;\n }\n .edit-link {\n right: unset;\n left: 0;\n }\n }\n}\n\n.collectionSearch .searchContent,\n.portlet.collectionSearch .searchContent,\n.collectionFilter .filterContent,\n.portlet.collectionFilter .filterContent,\n.collectionSortOn .filterContent,\n.portlet.collectionSortOn .filterContent {\n padding: 1em;\n > ul {\n padding: 0;\n list-style: none;\n position: relative;\n > li {\n list-style-type: none;\n padding: 2px;\n\n input[type=\"radio\"], input[type=\"checkbox\"] {\n margin: 0.3em 0.5em 0 0;\n line-height: normal;\n position: absolute;\n }\n label {\n display: inline-block;\n .filterControl, .filterLabel{\n display: inline-block;\n vertical-align: top;\n }\n .filterControl {\n margin-right: 0.5em;\n }\n .filterLabel{\n -webkit-hyphens: auto;\n -moz-hyphens: auto;\n hyphens: auto;\n padding-left: 1.5em;\n }\n }\n > a {\n padding: 0;\n display: block;\n border: 0;\n position: relative;\n z-index: 1;\n &:before {\n // left: 2px;\n font-size: 1.75em;\n line-height: 0.5;\n top: 0.15em;\n left: 0.15em;\n content: \"•\";\n display: inline;\n position: absolute;\n }\n &:hover {\n text-decoration: none;\n border-bottom: 0;\n }\n > span {\n padding-left: 1.5em;\n display: inline-block;\n hyphens: auto;\n }\n }\n &:hover {\n background: transparent;\n }\n }\n\n\n li.selected a {\n font-weight: bold;\n }\n ul {\n padding-left:15px;\n list-style: none;\n }\n }\n label {\n display: block;\n margin: 0;\n }\n select {\n padding: 0 0.5em;\n }\n}\n\n@barcelonetaPath:'/Volumes/WORKSPACE2/sozmap/src/plonetheme.barceloneta/plonetheme/barceloneta/theme';@bootstrap-badges:'badges.less';@bootstrap-basic:'navbar.less';@bootstrap-button-groups:'button-groups.less';@bootstrap-buttons:'close.less';@bootstrap-dropdown:'dropdowns.less';@bootstrap-glyphicons:'glyphicons.less';@bootstrap-mixins:'mixins.less';@bootstrap-modal:'modals.less';@bootstrap-progress-bars:'progress-bars.less';@bootstrap-variables:'variables.less';@bowerPath:'/Volumes/WORKSPACE/.buildout/eggs/plone.staticresources-1.1.0-py3.7.egg/plone/staticresources/static/components/';@bundle-leaflet:'bundle-leaflet.less';@collectionfilter:'collectionfilter.less';@collectionfilter-bundle:'collectionfilter.less';@collective-venue:'styles.less';@collective-venue-bundle:'styles.less';@dropzone:'dropzone.css';@font-awesome:'font-awesome-integration.less';@icon-font-path:\"../fonts/\";@isMockup:false;@isPlone:false;@jqtree:'jqtree.css';@jquery_recurrenceinput:'jquery.recurrenceinput.css';@layouts-editor:'layouts-editor.less';@leaflet:'leaflet.css';@leaflet-awesomemarkers:'leaflet.awesome-markers.css';@leaflet-fullscreen:'leaflet.fullscreen.css';@leaflet-geosearch:'l.geosearch.css';@leaflet-locatecontrol:'L.Control.Locate.css';@leaflet-markercluster:'MarkerCluster.Default.css';@leaflet-minimap:'Control.MiniMap.min.css';@leaflet-simplemarkers:'Control.SimpleMarkers.css';@mockup-patterns-autotoc:'pattern.autotoc.less';@mockup-patterns-datatables:'pattern.datatables.less';@mockup-patterns-filemanager:'pattern.filemanager.less';@mockup-patterns-livesearch:'pattern.livesearch.less';@mockup-patterns-markspeciallinks:'pattern.markspeciallinks.less';@mockup-patterns-modal:'pattern.modal.less';@mockup-patterns-pickadate:'pattern.pickadate.less';@mockup-patterns-querystring:'pattern.querystring.less';@mockup-patterns-recurrence:'pattern.recurrence.less';@mockup-patterns-relateditems:'pattern.relateditems.less';@mockup-patterns-resourceregistry:'pattern.resourceregistry.less';@mockup-patterns-select2:'pattern.select2.less';@mockup-patterns-structure:'pattern.structure.less';@mockup-patterns-thememapper:'pattern.thememapper.less';@mockup-patterns-tinymce:'pattern.tinymce.less';@mockup-patterns-tooltip:'pattern.tooltip.less';@mockup-patterns-tree:'pattern.tree.less';@mockup-patterns-upload:'pattern.upload.less';@mockup-popover:'popover.less';@mockupPath:'/Volumes/WORKSPACE/.buildout/eggs/mockup-3.0.2-py3.7.egg/mockup/patterns/';@mockuplessPath:'/Volumes/WORKSPACE/.buildout/eggs/mockup-3.0.2-py3.7.egg/mockup/less/';@mosaic:'mosaic.pattern.less';@pat-leaflet:'pat-leaflet.css';@photoswipe:'photoswipe.css';@photoswipe-ui:'default-skin.css';@picker:'classic.css';@picker_date:'classic.date.css';@picker_time:'classic.time.css';@pikaday:'pikaday.css';@plone:'plone.less';@plone-container-lg:1170px;@plone-container-md:970px;@plone-container-sm:750px;@plone-gray-light:lighten(#000, 46.5%);@plone-gray-lighter:lighten(#000, 80%);@plone-left-toolbar:60px;@plone-left-toolbar-expanded:120px;@plone-link-color:rgba(0,123,179,1);@plone-logged-in:'plone-logged-in.less';@plone-patterns-toolbar:'pattern.toolbar.less';@plone-screen-lg-min:1200px;@plone-screen-md-max:(@plone-screen-lg-min - 1);@plone-screen-md-min:992px;@plone-screen-sm-max:(@plone-screen-md-min - 1);@plone-screen-sm-min:768px;@plone-screen-xs-max:(@plone-screen-sm-min - 1);@plone-screen-xs-min:480px;@plone-toolbar-bg:rgba(0,0,0,.9);@plone-toolbar-draft-color:rgb(250,184,42);@plone-toolbar-font-primary:Roboto, \"Helvetica Neue\", Helvetica, Arial, sans-serif;@plone-toolbar-font-secondary:Roboto, \"Helvetica Neue\", Helvetica, Arial, sans-serif;@plone-toolbar-internal-color:rgb(250,184,42);@plone-toolbar-internally-published-color:rgb(136,61,250);@plone-toolbar-link:rgba(0,123,179,1);@plone-toolbar-pending-color:rgb(226,231,33);@plone-toolbar-private-color:rgb(196,24,60);@plone-toolbar-published-color:rgba(0,123,179,1);@plone-toolbar-separator-color:rgba(255,255,255,.17);@plone-toolbar-submenu-bg:rgba(45,45,45,.96);@plone-toolbar-submenu-header-color:lighten(#000, 80%);@plone-toolbar-submenu-text-color:lighten(#000, 90%);@plone-toolbar-submenu-width:180px;@plone-toolbar-text-color:rgba(255,255,255,1);@plone_app_imagecropping:'bundle.less';@plone_app_imagecropping_cropper:'cropper.css';@plone_app_imagecropping_cropscaleselect:'cropscaleselect.less';@resource-plone-app-jquerytools-dateinput-js:'jquery.tools.dateinput.css';@resource-plone-app-jquerytools-js:'jquery.tools.overlay.css';@resourceregistry:'resourceregistry.less';@select2:'select2.css';@sitePath:'/';@staticPath:'/Volumes/WORKSPACE/.buildout/eggs/plone.staticresources-1.1.0-py3.7.egg/plone/staticresources/static';@thememapper:'thememapper.less';@tinymce:'content.min.css';@tinymce-default-styles:'tinymce-styles.css';@tinymce-visualblocks:'visualblocks.css';\n@barcelonetaPath: '/Volumes/WORKSPACE2/sozmap/src/plonetheme.barceloneta/plonetheme/barceloneta/theme';@bootstrap-badges: 'badges.less';@bootstrap-basic: 'navbar.less';@bootstrap-button-groups: 'button-groups.less';@bootstrap-buttons: 'close.less';@bootstrap-dropdown: 'dropdowns.less';@bootstrap-glyphicons: 'glyphicons.less';@bootstrap-mixins: 'mixins.less';@bootstrap-modal: 'modals.less';@bootstrap-progress-bars: 'progress-bars.less';@bootstrap-variables: 'variables.less';@bowerPath: '/Volumes/WORKSPACE/.buildout/eggs/plone.staticresources-1.1.0-py3.7.egg/plone/staticresources/static/components/';@bundle-leaflet: 'bundle-leaflet.less';@collectionfilter: 'collectionfilter.less';@collectionfilter-bundle: 'collectionfilter.less';@collective-venue: 'styles.less';@collective-venue-bundle: 'styles.less';@dropzone: 'dropzone.css';@font-awesome: 'font-awesome-integration.less';@icon-font-path: \"../fonts/\";@isMockup: false;@isPlone: false;@jqtree: 'jqtree.css';@jquery_recurrenceinput: 'jquery.recurrenceinput.css';@layouts-editor: 'layouts-editor.less';@leaflet: 'leaflet.css';@leaflet-awesomemarkers: 'leaflet.awesome-markers.css';@leaflet-fullscreen: 'leaflet.fullscreen.css';@leaflet-geosearch: 'l.geosearch.css';@leaflet-locatecontrol: 'L.Control.Locate.css';@leaflet-markercluster: 'MarkerCluster.Default.css';@leaflet-minimap: 'Control.MiniMap.min.css';@leaflet-simplemarkers: 'Control.SimpleMarkers.css';@mockup-patterns-autotoc: 'pattern.autotoc.less';@mockup-patterns-datatables: 'pattern.datatables.less';@mockup-patterns-filemanager: 'pattern.filemanager.less';@mockup-patterns-livesearch: 'pattern.livesearch.less';@mockup-patterns-markspeciallinks: 'pattern.markspeciallinks.less';@mockup-patterns-modal: 'pattern.modal.less';@mockup-patterns-pickadate: 'pattern.pickadate.less';@mockup-patterns-querystring: 'pattern.querystring.less';@mockup-patterns-recurrence: 'pattern.recurrence.less';@mockup-patterns-relateditems: 'pattern.relateditems.less';@mockup-patterns-resourceregistry: 'pattern.resourceregistry.less';@mockup-patterns-select2: 'pattern.select2.less';@mockup-patterns-structure: 'pattern.structure.less';@mockup-patterns-thememapper: 'pattern.thememapper.less';@mockup-patterns-tinymce: 'pattern.tinymce.less';@mockup-patterns-tooltip: 'pattern.tooltip.less';@mockup-patterns-tree: 'pattern.tree.less';@mockup-patterns-upload: 'pattern.upload.less';@mockup-popover: 'popover.less';@mockupPath: '/Volumes/WORKSPACE/.buildout/eggs/mockup-3.0.2-py3.7.egg/mockup/patterns/';@mockuplessPath: '/Volumes/WORKSPACE/.buildout/eggs/mockup-3.0.2-py3.7.egg/mockup/less/';@mosaic: 'mosaic.pattern.less';@pat-leaflet: 'pat-leaflet.css';@photoswipe: 'photoswipe.css';@photoswipe-ui: 'default-skin.css';@picker: 'classic.css';@picker_date: 'classic.date.css';@picker_time: 'classic.time.css';@pikaday: 'pikaday.css';@plone: 'plone.less';@plone-container-lg: 1170px;@plone-container-md: 970px;@plone-container-sm: 750px;@plone-gray-light: lighten(#000, 46.5%);@plone-gray-lighter: lighten(#000, 80%);@plone-left-toolbar: 60px;@plone-left-toolbar-expanded: 120px;@plone-link-color: rgba(0,123,179,1);@plone-logged-in: 'plone-logged-in.less';@plone-patterns-toolbar: 'pattern.toolbar.less';@plone-screen-lg-min: 1200px;@plone-screen-md-max: (@plone-screen-lg-min - 1);@plone-screen-md-min: 992px;@plone-screen-sm-max: (@plone-screen-md-min - 1);@plone-screen-sm-min: 768px;@plone-screen-xs-max: (@plone-screen-sm-min - 1);@plone-screen-xs-min: 480px;@plone-toolbar-bg: rgba(0,0,0,.9);@plone-toolbar-draft-color: rgb(250,184,42);@plone-toolbar-font-primary: Roboto, \"Helvetica Neue\", Helvetica, Arial, sans-serif;@plone-toolbar-font-secondary: Roboto, \"Helvetica Neue\", Helvetica, Arial, sans-serif;@plone-toolbar-internal-color: rgb(250,184,42);@plone-toolbar-internally-published-color: rgb(136,61,250);@plone-toolbar-link: rgba(0,123,179,1);@plone-toolbar-pending-color: rgb(226,231,33);@plone-toolbar-private-color: rgb(196,24,60);@plone-toolbar-published-color: rgba(0,123,179,1);@plone-toolbar-separator-color: rgba(255,255,255,.17);@plone-toolbar-submenu-bg: rgba(45,45,45,.96);@plone-toolbar-submenu-header-color: lighten(#000, 80%);@plone-toolbar-submenu-text-color: lighten(#000, 90%);@plone-toolbar-submenu-width: 180px;@plone-toolbar-text-color: rgba(255,255,255,1);@plone_app_imagecropping: 'bundle.less';@plone_app_imagecropping_cropper: 'cropper.css';@plone_app_imagecropping_cropscaleselect: 'cropscaleselect.less';@resource-plone-app-jquerytools-dateinput-js: 'jquery.tools.dateinput.css';@resource-plone-app-jquerytools-js: 'jquery.tools.overlay.css';@resourceregistry: 'resourceregistry.less';@select2: 'select2.css';@sitePath: '/';@staticPath: '/Volumes/WORKSPACE/.buildout/eggs/plone.staticresources-1.1.0-py3.7.egg/plone/staticresources/static';@thememapper: 'thememapper.less';@tinymce: 'content.min.css';@tinymce-default-styles: 'tinymce-styles.css';@tinymce-visualblocks: 'visualblocks.css';"]}
\ No newline at end of file
+{"version":3,"sources":["src/collective/collectionfilter/resources/collectionfilter.less"],"names":[],"mappings":"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","sourcesContent":[".collectionSearch,\n.portlet.collectionSearch,\n.collectionFilter,\n.portlet.collectionFilter,\n.collectionSortOn,\n.portlet.collectionSortOn {\n position: relative;\n background: white;\n .edit-link {\n position: absolute;\n right: 0;\n bottom: -0.5em;\n label {\n display: none;\n }\n }\n}\n.horizontal {\n .collectionSearch,\n .portlet.collectionSearch,\n .collectionFilter,\n .portlet.collectionFilter,\n .collectionSortOn,\n .portlet.collectionSortOn {\n display: flex !important;\n justify-content: space-between;\n align-items: center;\n & > header {\n font-weight: bold;\n }\n .edit-link {\n right: unset;\n left: 0;\n }\n }\n}\n\n.collectionSearch .searchContent,\n.portlet.collectionSearch .searchContent,\n.collectionFilter .filterContent,\n.portlet.collectionFilter .filterContent,\n.collectionSortOn .filterContent,\n.portlet.collectionSortOn .filterContent {\n padding: 1em;\n > ul {\n padding: 0;\n list-style: none;\n position: relative;\n > li {\n list-style-type: none;\n padding: 2px;\n\n input[type=\"radio\"], input[type=\"checkbox\"] {\n margin: 0.3em 0.5em 0 0;\n line-height: normal;\n position: absolute;\n }\n label {\n display: inline-block;\n .filterControl, .filterLabel{\n display: inline-block;\n vertical-align: top;\n }\n .filterControl {\n margin-right: 0.5em;\n }\n .filterLabel{\n -webkit-hyphens: auto;\n -moz-hyphens: auto;\n hyphens: auto;\n padding-left: 1.5em;\n }\n }\n > a {\n padding: 0;\n display: block;\n border: 0;\n position: relative;\n z-index: 1;\n &:before {\n // left: 2px;\n font-size: 1.75em;\n line-height: 0.5;\n top: 0.15em;\n left: 0.15em;\n content: \"•\";\n display: inline;\n position: absolute;\n }\n &:hover {\n text-decoration: none;\n border-bottom: 0;\n }\n > span {\n padding-left: 1.5em;\n display: inline-block;\n hyphens: auto;\n }\n }\n &:hover {\n background: transparent;\n }\n }\n\n\n li.selected a {\n font-weight: bold;\n }\n ul {\n padding-left:15px;\n list-style: none;\n }\n }\n label {\n display: block;\n margin: 0;\n }\n select {\n padding: 0 0.5em;\n }\n}\n\n@barcelonetaPath:'/Users/dylanjay/Projects/download-cache/eggs/plonetheme.barceloneta-2.1.10-py2.7.egg/plonetheme/barceloneta/theme';@bootstrap-badges:'badges.less';@bootstrap-basic:'navbar.less';@bootstrap-button-groups:'button-groups.less';@bootstrap-buttons:'close.less';@bootstrap-dropdown:'dropdowns.less';@bootstrap-glyphicons:'glyphicons.less';@bootstrap-mixins:'mixins.less';@bootstrap-modal:'modals.less';@bootstrap-progress-bars:'progress-bars.less';@bootstrap-variables:'variables.less';@bowerPath:'/Users/dylanjay/Projects/download-cache/eggs/plone.staticresources-1.4.2-py2.7.egg/plone/staticresources/static/components/';@collectionfilter:'collectionfilter.less';@collectionfilter-bundle:'collectionfilter.less';@dropzone:'dropzone.css';@filemanager:'filemanager.less';@icon-font-path:\"../fonts/\";@isMockup:false;@isPlone:false;@jqtree:'jqtree.css';@jquery_recurrenceinput:'jquery.recurrenceinput.css';@mockup-patterns-autotoc:'pattern.autotoc.less';@mockup-patterns-datatables:'pattern.datatables.less';@mockup-patterns-filemanager:'pattern.filemanager.less';@mockup-patterns-livesearch:'pattern.livesearch.less';@mockup-patterns-markspeciallinks:'pattern.markspeciallinks.less';@mockup-patterns-modal:'pattern.modal.less';@mockup-patterns-pickadate:'pattern.pickadate.less';@mockup-patterns-querystring:'pattern.querystring.less';@mockup-patterns-recurrence:'pattern.recurrence.less';@mockup-patterns-relateditems:'pattern.relateditems.less';@mockup-patterns-resourceregistry:'pattern.resourceregistry.less';@mockup-patterns-select2:'pattern.select2.less';@mockup-patterns-structure:'pattern.structure.less';@mockup-patterns-thememapper:'pattern.thememapper.less';@mockup-patterns-tinymce:'pattern.tinymce.less';@mockup-patterns-tooltip:'pattern.tooltip.less';@mockup-patterns-tree:'pattern.tree.less';@mockup-patterns-upload:'pattern.upload.less';@mockup-popover:'popover.less';@mockupPath:'/Users/dylanjay/Projects/download-cache/eggs/mockup-3.2.5-py2.7.egg/mockup/patterns/';@mockuplessPath:'/Users/dylanjay/Projects/download-cache/eggs/mockup-3.2.5-py2.7.egg/mockup/less/';@picker:'classic.css';@picker_date:'classic.date.css';@picker_time:'classic.time.css';@plone:'plone.less';@plone-container-lg:1170px;@plone-container-md:970px;@plone-container-sm:750px;@plone-datatables:'plone-datatables.less';@plone-editor-tools:'plone-editor-tools.less';@plone-gray-light:lighten(#000, 46.5%);@plone-gray-lighter:lighten(#000, 80%);@plone-left-toolbar:60px;@plone-left-toolbar-expanded:120px;@plone-link-color:rgba(0,123,179,1);@plone-logged-in:'plone-logged-in.less';@plone-patterns-toolbar:'pattern.toolbar.less';@plone-screen-lg-min:1200px;@plone-screen-md-max:(@plone-screen-lg-min - 1);@plone-screen-md-min:992px;@plone-screen-sm-max:(@plone-screen-md-min - 1);@plone-screen-sm-min:768px;@plone-screen-xs-max:(@plone-screen-sm-min - 1);@plone-screen-xs-min:480px;@plone-tinymce:'plone-tinymce.less';@plone-toolbar-bg:rgba(0,0,0,.9);@plone-toolbar-draft-color:rgb(250,184,42);@plone-toolbar-font-primary:Roboto, \"Helvetica Neue\", Helvetica, Arial, sans-serif;@plone-toolbar-font-secondary:Roboto, \"Helvetica Neue\", Helvetica, Arial, sans-serif;@plone-toolbar-internal-color:rgb(250,184,42);@plone-toolbar-internally-published-color:rgb(136,61,250);@plone-toolbar-link:rgba(0,123,179,1);@plone-toolbar-pending-color:rgb(226,231,33);@plone-toolbar-private-color:rgb(196,24,60);@plone-toolbar-published-color:rgba(0,123,179,1);@plone-toolbar-separator-color:rgba(255,255,255,.17);@plone-toolbar-submenu-bg:rgba(45,45,45,.96);@plone-toolbar-submenu-header-color:lighten(#000, 80%);@plone-toolbar-submenu-text-color:lighten(#000, 90%);@plone-toolbar-submenu-width:180px;@plone-toolbar-text-color:rgba(255,255,255,1);@resource-plone-app-jquerytools-dateinput-js:'jquery.tools.dateinput.css';@resource-plone-app-jquerytools-js:'jquery.tools.overlay.css';@resourceregistry:'resourceregistry.less';@select2:'select2.css';@sitePath:'/';@staticPath:'/Users/dylanjay/Projects/download-cache/eggs/plone.staticresources-1.4.2-py2.7.egg/plone/staticresources/static';@thememapper:'thememapper.less';@tinymce:'content.min.css';@tinymce-default-styles:'tinymce-styles.css';@tinymce-visualblocks:'visualblocks.css';\n@barcelonetaPath: '/Users/dylanjay/Projects/download-cache/eggs/plonetheme.barceloneta-2.1.10-py2.7.egg/plonetheme/barceloneta/theme';@bootstrap-badges: 'badges.less';@bootstrap-basic: 'navbar.less';@bootstrap-button-groups: 'button-groups.less';@bootstrap-buttons: 'close.less';@bootstrap-dropdown: 'dropdowns.less';@bootstrap-glyphicons: 'glyphicons.less';@bootstrap-mixins: 'mixins.less';@bootstrap-modal: 'modals.less';@bootstrap-progress-bars: 'progress-bars.less';@bootstrap-variables: 'variables.less';@bowerPath: '/Users/dylanjay/Projects/download-cache/eggs/plone.staticresources-1.4.2-py2.7.egg/plone/staticresources/static/components/';@collectionfilter: 'collectionfilter.less';@collectionfilter-bundle: 'collectionfilter.less';@dropzone: 'dropzone.css';@filemanager: 'filemanager.less';@icon-font-path: \"../fonts/\";@isMockup: false;@isPlone: false;@jqtree: 'jqtree.css';@jquery_recurrenceinput: 'jquery.recurrenceinput.css';@mockup-patterns-autotoc: 'pattern.autotoc.less';@mockup-patterns-datatables: 'pattern.datatables.less';@mockup-patterns-filemanager: 'pattern.filemanager.less';@mockup-patterns-livesearch: 'pattern.livesearch.less';@mockup-patterns-markspeciallinks: 'pattern.markspeciallinks.less';@mockup-patterns-modal: 'pattern.modal.less';@mockup-patterns-pickadate: 'pattern.pickadate.less';@mockup-patterns-querystring: 'pattern.querystring.less';@mockup-patterns-recurrence: 'pattern.recurrence.less';@mockup-patterns-relateditems: 'pattern.relateditems.less';@mockup-patterns-resourceregistry: 'pattern.resourceregistry.less';@mockup-patterns-select2: 'pattern.select2.less';@mockup-patterns-structure: 'pattern.structure.less';@mockup-patterns-thememapper: 'pattern.thememapper.less';@mockup-patterns-tinymce: 'pattern.tinymce.less';@mockup-patterns-tooltip: 'pattern.tooltip.less';@mockup-patterns-tree: 'pattern.tree.less';@mockup-patterns-upload: 'pattern.upload.less';@mockup-popover: 'popover.less';@mockupPath: '/Users/dylanjay/Projects/download-cache/eggs/mockup-3.2.5-py2.7.egg/mockup/patterns/';@mockuplessPath: '/Users/dylanjay/Projects/download-cache/eggs/mockup-3.2.5-py2.7.egg/mockup/less/';@picker: 'classic.css';@picker_date: 'classic.date.css';@picker_time: 'classic.time.css';@plone: 'plone.less';@plone-container-lg: 1170px;@plone-container-md: 970px;@plone-container-sm: 750px;@plone-datatables: 'plone-datatables.less';@plone-editor-tools: 'plone-editor-tools.less';@plone-gray-light: lighten(#000, 46.5%);@plone-gray-lighter: lighten(#000, 80%);@plone-left-toolbar: 60px;@plone-left-toolbar-expanded: 120px;@plone-link-color: rgba(0,123,179,1);@plone-logged-in: 'plone-logged-in.less';@plone-patterns-toolbar: 'pattern.toolbar.less';@plone-screen-lg-min: 1200px;@plone-screen-md-max: (@plone-screen-lg-min - 1);@plone-screen-md-min: 992px;@plone-screen-sm-max: (@plone-screen-md-min - 1);@plone-screen-sm-min: 768px;@plone-screen-xs-max: (@plone-screen-sm-min - 1);@plone-screen-xs-min: 480px;@plone-tinymce: 'plone-tinymce.less';@plone-toolbar-bg: rgba(0,0,0,.9);@plone-toolbar-draft-color: rgb(250,184,42);@plone-toolbar-font-primary: Roboto, \"Helvetica Neue\", Helvetica, Arial, sans-serif;@plone-toolbar-font-secondary: Roboto, \"Helvetica Neue\", Helvetica, Arial, sans-serif;@plone-toolbar-internal-color: rgb(250,184,42);@plone-toolbar-internally-published-color: rgb(136,61,250);@plone-toolbar-link: rgba(0,123,179,1);@plone-toolbar-pending-color: rgb(226,231,33);@plone-toolbar-private-color: rgb(196,24,60);@plone-toolbar-published-color: rgba(0,123,179,1);@plone-toolbar-separator-color: rgba(255,255,255,.17);@plone-toolbar-submenu-bg: rgba(45,45,45,.96);@plone-toolbar-submenu-header-color: lighten(#000, 80%);@plone-toolbar-submenu-text-color: lighten(#000, 90%);@plone-toolbar-submenu-width: 180px;@plone-toolbar-text-color: rgba(255,255,255,1);@resource-plone-app-jquerytools-dateinput-js: 'jquery.tools.dateinput.css';@resource-plone-app-jquerytools-js: 'jquery.tools.overlay.css';@resourceregistry: 'resourceregistry.less';@select2: 'select2.css';@sitePath: '/';@staticPath: '/Users/dylanjay/Projects/download-cache/eggs/plone.staticresources-1.4.2-py2.7.egg/plone/staticresources/static';@thememapper: 'thememapper.less';@tinymce: 'content.min.css';@tinymce-default-styles: 'tinymce-styles.css';@tinymce-visualblocks: 'visualblocks.css';"]}
\ No newline at end of file
diff --git a/src/collective/collectionfilter/resources/collectionfilter-bundle-compiled.js b/src/collective/collectionfilter/resources/collectionfilter-bundle-compiled.js
index 6606810b..b3b712b8 100644
--- a/src/collective/collectionfilter/resources/collectionfilter-bundle-compiled.js
+++ b/src/collective/collectionfilter/resources/collectionfilter-bundle-compiled.js
@@ -1,2 +1,2 @@
-define("collectionfilter",["jquery","pat-base","mockup-patterns-contentloader"],function(n,t,e){return t.extend({name:"collectionfilter",trigger:".pat-collectionfilter",parser:"mockup",contentloader:e,defaults:{collectionUUID:"",collectionURL:"",reloadURL:"",ajaxLoad:!0,contentSelector:"#content-core"},_initmap_cycles:2,_zoomed:!1,init:function(){var t;if(this.$el.unbind("collectionfilter:reload"),this.$el.on("collectionfilter:reload",function(t,e){e.noReloadSearch&&this.$el.hasClass("collectionSearch")||e.noReloadMap&&this.$el.hasClass("collectionMaps")||e.collectionUUID===this.options.collectionUUID&&this.reload(e.targetFilterURL)}.bind(this)),this.$el.hasClass("collectionSearch")&&this.options.ajaxLoad)return n('button[type="submit"]',this.$el).hide(),n("form",this.$el).on("submit",function(t){t.preventDefault()}),void n('input[name="SearchableText"]',this.$el).on("keyup",function(o){clearTimeout(t),0 ul {
padding: 0;
list-style: none;
@@ -50,21 +51,23 @@
list-style-type: none;
padding: 2px;
- input[type="radio"], input[type="checkbox"] {
+ input[type="radio"],
+ input[type="checkbox"] {
margin: 0.3em 0.5em 0 0;
line-height: normal;
position: absolute;
}
label {
display: inline-block;
- .filterControl, .filterLabel{
- display: inline-block;
- vertical-align: top;
+ .filterControl,
+ .filterLabel {
+ display: inline-block;
+ vertical-align: top;
}
.filterControl {
- margin-right: 0.5em;
+ margin-right: 0.5em;
}
- .filterLabel{
+ .filterLabel {
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
@@ -102,13 +105,12 @@
}
}
-
li.selected a {
- font-weight: bold;
+ font-weight: bold;
}
ul {
- padding-left:15px;
- list-style: none;
+ padding-left: 15px;
+ list-style: none;
}
}
label {
@@ -119,3 +121,23 @@
padding: 0 0.5em;
}
}
+
+.pat-collectionfilter .filterInfoContent {
+ margin-top: 0.5em;
+
+ .filterInfoContent__term {
+ @media (min-width: 480px) {
+ box-sizing: border-box;
+ float: left;
+ clear: left;
+ padding-right: 5px;
+ }
+ }
+ .filterInfoContent__definition {
+ }
+}
+
+.sectionFilter li.selected a > span {
+ font-weight: bold;
+}
+
diff --git a/src/collective/collectionfilter/tests/robot/keywords.robot b/src/collective/collectionfilter/tests/robot/keywords.robot
index 8060e001..3f4ec474 100644
--- a/src/collective/collectionfilter/tests/robot/keywords.robot
+++ b/src/collective/collectionfilter/tests/robot/keywords.robot
@@ -309,8 +309,8 @@ My collection has a collection sorting
run keyword unless ${USE_TILES} My collection has a collection sorting portlet ${sort_on}
My collection has a collection info
- [Arguments] ${header}="Current Filter" @{templates} ${hide_when}=${None}
- run keyword if ${USE_TILES} My collection has a collection info tile ${header} @{templates} hide_when=${hide_when}
+ [Arguments] ${header}="Current Filter" @{templates} ${hide_when}=${None} ${as_title}=${False}
+ run keyword if ${USE_TILES} My collection has a collection info tile ${header} @{templates} hide_when=${hide_when} as_title=${as_title}
run keyword unless ${USE_TILES} My collection has a collection info portlet ${header} @{templates} hide_when=${hide_when}
I'm viewing the collection
@@ -453,7 +453,7 @@ My collection has a collection search tile
Save mosaic page
My collection has a collection info tile
- [Arguments] ${header} @{templates} ${hide_when}=${None}
+ [Arguments] ${header} @{templates} ${hide_when}=${None} ${as_title}=${False}
Go to ${PLONE_URL}/testdoc/edit
Add info tile @{templates} hide_when=${hide_when}
@@ -544,13 +544,14 @@ Add search tile
Add info tile
- [Arguments] @{templates} ${hide_when}=${None} ${collection_name}=${None}
+ [Arguments] @{templates} ${hide_when}=${None} ${collection_name}=${None} ${as_title}=${False}
Insert tile "Collection Filter Info"
run keyword if $collection_name set relateditem formfield-collective-collectionfilter-tiles-info-target_collection ${collection_name}
# Complete filter form
Set Info Settings collective-collectionfilter-tiles-info @{templates} hide_when=${hide_when}
- # Run Keyword by label Content Selector Input Text .contentlisting-tile
+ Run Keyword by label Content Selector Input Text .contentlisting-tile
+ run keyword if ${as_title} Set Options Display as Title
Click element css=.pattern-modal-buttons #buttons-save
Drag tile
diff --git a/src/collective/collectionfilter/tests/robot/test_infoportlets.robot b/src/collective/collectionfilter/tests/robot/test_infoportlets.robot
new file mode 100644
index 00000000..74c558c8
--- /dev/null
+++ b/src/collective/collectionfilter/tests/robot/test_infoportlets.robot
@@ -0,0 +1,86 @@
+
+*** Settings *****************************************************************
+
+Resource keywords.robot
+
+# Library Remote ${PLONE_URL}/RobotRemote
+
+Test Setup Default Setup
+Test Teardown Default Teardown
+
+
+*** Test Cases ***************************************************************
+
+Scenario: Info on single choice filter
+ Given I've got a site with a collection
+ and my collection has a collection filter Subject
+ and my collection has a collection info Current Filters value_quoted_filter
+ When I'm viewing the collection
+ and Click Input "Süper (2)"
+ and Should be 2 collection results
+ then Should be Info with text: "Süper" Subject
+
+Scenario: Info on multiple choice filter
+ Given I've got a site with a collection
+ and my collection has a collection info Current Filters filter_colon_value
+ and my collection has a collection filter Subject and checkboxes_dropdowns
+ When I'm viewing the collection
+ and Click Input "Süper (2)"
+ and Should be 2 collection results
+ and Click Input "Evänt (1)"
+ and Should be 1 collection results
+ then Should be Info with text: Subject: Süper/Evänt
+
+Scenario: Info on result count
+ Given I've got a site with a collection
+ and my collection has a collection info Current Filters result_count
+ and my collection has a collection filter Subject and checkboxes_dropdowns
+ When I'm viewing the collection
+ and Click Input "Süper (2)"
+ and Should be 2 collection results
+ then Should be Info with text: 2
+
+Scenario: Info on keyword search
+ Given I've got a site with a collection
+ and my collection has a collection info Current Filters search_quoted
+ and my collection has a collection search
+ When I'm viewing the collection
+ and I search for "Document"
+ then Should be Info with text: "Document"
+
+Scenario: Combine info templates
+ Given I've got a site with a collection
+ and my collection has a collection info Current Filters search_for search_quoted filter_colon_value comma with result_count results
+ and my collection has a collection search
+ and my collection has a collection filter Subject and checkboxes_dropdowns
+ When I'm viewing the collection
+ and Click Input "Süper (2)"
+ and Should be 2 collection results
+ and Click Input "Evänt (1)"
+ and Should be 1 collection results
+ and I search for "Event"
+ Then Should be Info with text: Search for "Event" Subject: Süper/Evänt, with 1 result
+
+Scenario: Hide on any filter
+ Given I've got a site with a collection
+ and my collection has a collection info Current Filters result_count hide_when=any_filter
+ and my collection has a collection filter Subject
+ When I'm viewing the collection
+ and Should be Info with text: 3
+ and Click Input "Süper (2)"
+ and Should be 2 collection results
+ then should be no Info
+ and Click Input "All (3)"
+ then Should be Info with text: 3
+ and Click Input "Süper (2)"
+ then should be no Info
+
+Scenario: Display as title
+ Given I've got a site with a collection
+ and my collection has a collection info Current Filters search_for search_quoted filter_colon_value comma with result_count results as_title=${True}
+ and my collection has a collection filter Subject
+ When I'm viewing the collection
+ and Click Input "Süper (2)"
+ and Should be 2 collection results
+ then Should be Info with text: Subject: Süper, with 2 results
+ and run keyword if ${USE_TILES} Page Should Contain Element css=h1 limit=2
\ No newline at end of file
diff --git a/src/collective/collectionfilter/tiles/configure.zcml b/src/collective/collectionfilter/tiles/configure.zcml
index 062c1b43..f6741004 100644
--- a/src/collective/collectionfilter/tiles/configure.zcml
+++ b/src/collective/collectionfilter/tiles/configure.zcml
@@ -63,6 +63,18 @@
template="sorting.pt"
/>
+
+
+
+
+
+
+
+
+ Title
+
+
+
+
+
diff --git a/src/collective/collectionfilter/tiles/info.py b/src/collective/collectionfilter/tiles/info.py
new file mode 100644
index 00000000..10236f21
--- /dev/null
+++ b/src/collective/collectionfilter/tiles/info.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+from collective.collectionfilter.baseviews import BaseInfoView
+from collective.collectionfilter.interfaces import ICollectionFilterInfoTile
+from collective.collectionfilter.tiles import BaseFilterTile
+from plone.supermodel.model import Schema
+from zope.interface import implementer
+
+
+class IInfoTile(Schema, ICollectionFilterInfoTile):
+ pass
+
+
+@implementer(IInfoTile)
+class InfoTile(BaseFilterTile, BaseInfoView):
+ """Info Tile"""
diff --git a/src/collective/collectionfilter/tiles/profiles/default-geolocation/registry.xml b/src/collective/collectionfilter/tiles/profiles/default-geolocation/registry.xml
index 1736d2ed..86074b02 100644
--- a/src/collective/collectionfilter/tiles/profiles/default-geolocation/registry.xml
+++ b/src/collective/collectionfilter/tiles/profiles/default-geolocation/registry.xml
@@ -5,6 +5,7 @@
collective.collectionfilter.tiles.filter
collective.collectionfilter.tiles.search
collective.collectionfilter.tiles.sortOn
+ collective.collectionfilter.tiles.info
collective.collectionfilter.tiles.maps
@@ -51,6 +52,20 @@
10
+
+ collective.collectionfilter.tiles.info
+ Collection Filter Info
+ advanced
+ app
+
+ false
+ true
+ false
+ false
+ 10
+
+
collective.collectionfilter.tiles.maps
diff --git a/src/collective/collectionfilter/tiles/profiles/default/registry.xml b/src/collective/collectionfilter/tiles/profiles/default/registry.xml
index 556f052e..0369ba23 100644
--- a/src/collective/collectionfilter/tiles/profiles/default/registry.xml
+++ b/src/collective/collectionfilter/tiles/profiles/default/registry.xml
@@ -5,6 +5,7 @@
collective.collectionfilter.tiles.filter
collective.collectionfilter.tiles.search
collective.collectionfilter.tiles.sortOn
+ collective.collectionfilter.tiles.info
@@ -50,4 +51,19 @@
10
+
+ collective.collectionfilter.tiles.info
+ Collection Filter Info
+ advanced
+ app
+
+ false
+ true
+ false
+ false
+ 10
+
+
+
diff --git a/src/collective/collectionfilter/tiles/profiles/uninstall/registry.xml b/src/collective/collectionfilter/tiles/profiles/uninstall/registry.xml
index a4f7173c..7a4608e6 100644
--- a/src/collective/collectionfilter/tiles/profiles/uninstall/registry.xml
+++ b/src/collective/collectionfilter/tiles/profiles/uninstall/registry.xml
@@ -5,6 +5,7 @@
collective.collectionfilter.tiles.filter
collective.collectionfilter.tiles.search
collective.collectionfilter.tiles.sortOn
+ collective.collectionfilter.tiles.info
collective.collectionfilter.tiles.maps
@@ -24,6 +25,11 @@
prefix="plone.app.mosaic.app_tiles.collective_collectionfilter_tiles_sort_on"
interface="plone.app.mosaic.interfaces.ITile"/>
+
+
0:
+ title = container[0].Title
+ # ctype = container[0].portal_type.lower()
+ else:
+ title = path.split("/")[-1]
+ return title
+
+
+def path_indent(path):
+ level = max(0, len(path.split("/")) - 1) # Put Top level at same level as All as easy enough it distiguish
+ css_class = u"pathLevel{level}".format(level=level)
+ return css_class
+
+
+def relative_to_absolute_path(path):
+ # Ensure query string only needs relative path. Internal search needs full path
+ return '/'.join(list(plone.api.portal.get().getPhysicalPath()) + path.split("/"))
+
+
+def sort_path(it):
+ return it["url"]
+
+
+def sort_title(it):
+ return it["title"].lower()
+
+
@implementer(IGroupByCriteria)
class GroupByCriteria:
"""Global utility for retrieving and manipulating groupby criterias.
@@ -233,3 +298,105 @@ def SortOnIndexesVocabulary(context):
SimpleTerm(title=_(v["title"]), value=k) for k, v in sortable_indexes.items()
] # noqa
return SimpleVocabulary(items)
+
+
+DEFAULT_TEMPLATES = OrderedDict(
+ [
+ ("search_for", (u"Search for", u"string:Search for")),
+ (
+ "filter_colon_value",
+ (
+ u"{Filter}: {value}, ...",
+ u'python: u", ".join(u"{}: {}".format(k,u"/".join(v)) for k, v in query)',
+ ),
+ ),
+ (
+ "value_comma",
+ (
+ u"{value}, ...",
+ u'python: ", u".join(u"{}".format(v) for _,values in query for v in values)',
+ ),
+ ),
+ (
+ "value_quoted_filter",
+ (
+ u'"{value}" {Filter}, ...',
+ u"""python: u", ".join(u'"{}" {}'.format(u"/".join(v),k) for k, v in query)""",
+ ),
+ ),
+ ("with_keywords", (u"with keywords", u"string:with keywords")),
+ (
+ "search_quoted",
+ (u'"{search}"', u"""python: u'"{}"'.format(search) if search else '' """),
+ ),
+ ("hyphen", (u" - ", u"string:-")),
+ ("comma", (u", ", u"string:, ")),
+ ("has_returned", (u"has returned", u"string:has returned")),
+ ("with", (u"with", u"string:with")),
+ ("result_count", (u"{results}", u"python:str(results)")),
+ ("results", (u"results", u"python: 'result' if results == 1 else 'results'")),
+ (
+ "documents",
+ (u"documents", u"python: 'document' if results == 1 else 'documents'"),
+ ),
+ ]
+)
+
+
+@provider(IVocabularyFactory)
+def TemplatePartsVocabulary(context):
+ """
+ The vocabulary consists of a tuple containing the id of the template part
+ and a tuple within that tuple containing the title viewed by in the interface and the TAL expression for the template part.
+ """
+ items = [
+ SimpleTerm(token=k, title=v[0], value=v[1])
+ for k, v in DEFAULT_TEMPLATES.items()
+ ]
+ groupby = getUtility(IGroupByCriteria).groupby
+ for groupby_criteria in groupby.keys():
+ items.append(SimpleTerm(
+ token=u"value_{}".format(groupby_criteria),
+ title=u"{{Value of {}}}".format(_(groupby_criteria)),
+ value=u"""python:dict(query).get('{0}', '')""".format(groupby_criteria),
+ ))
+ items.append(SimpleTerm(
+ token=u"name_{}".format(groupby_criteria),
+ title=u"{}".format(_(groupby_criteria)),
+ value=u"""string:{}""".format(groupby_criteria),
+ ))
+
+ return SimpleVocabulary(items)
+
+
+def get_conditions():
+ items = [
+ ("any_filter", u"Any filter", "query"),
+ ("no_filter", u"No filter", "not:query"),
+ ("search", u"Keyword search", "search"),
+ ("no_search", u"Keyword search", "not:search"),
+ ("results", u"Results", "results"),
+ ("no_results", u"No Results", "not:results"),
+ ]
+ groupby = getUtility(IGroupByCriteria).groupby
+ for it in groupby.keys():
+ i = (
+ "filter_{}".format(it),
+ u"Filtered by {}".format(_(it)),
+ "python:'{}' in query".format(it),
+ )
+ items.append(i)
+ i = (
+ "no_filter_{}".format(it),
+ u"Not Filtered by {}".format(_(it)),
+ "python:'{}' not in query".format(it),
+ )
+ items.append(i)
+ return items
+
+
+@provider(IVocabularyFactory)
+def InfoConditionsVocabulary(context):
+
+ items = [SimpleTerm(title=title, value=id) for id, title, _ in get_conditions()]
+ return SimpleVocabulary(items)
diff --git a/test-5.2.x.cfg b/test-5.2.x.cfg
index 4f68bd41..dc219058 100644
--- a/test-5.2.x.cfg
+++ b/test-5.2.x.cfg
@@ -2,7 +2,7 @@
extends =
https://raw.githubusercontent.com/collective/buildout.plonetest/master/test-5.2.x.cfg
https://raw.githubusercontent.com/collective/buildout.plonetest/master/qa.cfg
- https://raw.githubusercontent.com/plone/plone.app.mosaic/master/versions.cfg
+# https://raw.githubusercontent.com/plone/plone.app.mosaic/master/versions.cfg
base.cfg
parts +=
@@ -22,3 +22,4 @@ extensions +=
#setuptools =
#zc.buildout =
coverage = >=3.7
+plone.formwidget.geolocation = < 3.0.0