Skip to content

Commit

Permalink
Merge branch 'djay/5.0.x_integration' of github.com:collective/collec…
Browse files Browse the repository at this point in the history
…tive.collectionfilter into djay/5.0.x_integration
  • Loading branch information
djay committed Jul 9, 2019
2 parents e687169 + d5a633c commit ab6b364
Show file tree
Hide file tree
Showing 11 changed files with 392 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Expand Up @@ -5,6 +5,7 @@ Changelog
----------------

Features:
- Backport for @petschki: #42 Incorrect count on "all" when "Narrow down filter options" enabled and viewing a filtered result. [pigeonflight, jensens]

- Restore 5.0.x compatibility
[djay, quang]
Expand Down Expand Up @@ -147,6 +148,9 @@ New:
- Make backwards compatible with Plone 5.0
[nngu6036, instification]

- Add Location Filter portlet. Allows to filter results based on location in site.
[instification]

Bug fixes:

- When reloading the collection in JavaScript, use the content selector's parent as base to trigger events on.
Expand Down
2 changes: 2 additions & 0 deletions README.rst
Expand Up @@ -61,6 +61,8 @@ Simply do this somewhere in your buildout::
collective.collectionfilter[geolocation]
...

Besides the "Collection Filter" portlet/tile there is also a "Collection Search" portlet/tile for doing a fulltext search on the collection results and a "Location Filter" for filtering results based on their location in the site.


Overloading GroupByCriteria
---------------------------
Expand Down
13 changes: 13 additions & 0 deletions src/collective/collectionfilter/baseviews.py
Expand Up @@ -3,6 +3,7 @@

from collective.collectionfilter.filteritems import get_filter_items
from collective.collectionfilter.query import make_query
from collective.collectionfilter.filteritems import get_location_filter_items
from collective.collectionfilter.utils import base_query
from collective.collectionfilter.utils import safe_decode
from collective.collectionfilter.utils import safe_encode
Expand Down Expand Up @@ -106,6 +107,18 @@ def results(self):
return results


class BaseLocationView(BaseView):

def results(self):
results = get_location_filter_items(
target_collection=self.settings.target_collection,
view_name=self.settings.view_name,
cache_enabled=self.settings.cache_enabled,
request_params=self.top_request.form or {}
)
return results


class BaseSearchView(BaseView):

@property
Expand Down
193 changes: 193 additions & 0 deletions src/collective/collectionfilter/filteritems.py
Expand Up @@ -58,6 +58,25 @@ def _results_cachekey(
return cachekey


def _location_results_cachekey(
method,
target_collection,
view_name,
cache_enabled,
request_params):
if not cache_enabled:
raise DontCache
cachekey = (
target_collection,
view_name,
request_params,
' '.join(plone.api.user.get_roles()),
plone.api.portal.get_current_language(),
str(plone.api.portal.get_tool('portal_catalog').getCounter()),
)
return cachekey


@ram.cache(_results_cachekey)
def get_filter_items(
target_collection,
Expand Down Expand Up @@ -235,3 +254,177 @@ def get_filter_items(
ret += grouped_results

return ret


@ram.cache(_location_results_cachekey)
def get_location_filter_items(
target_collection,
view_name='',
cache_enabled=True,
request_params=None
):
request_params = request_params or {}
custom_query = {} # Additional query to filter the collection

collection = uuidToObject(target_collection)
if not collection:
return None
collection_url = collection.absolute_url()
collection_layout = collection.getLayout()
default_view = collection.restrictedTraverse(collection_layout)

# Recursively transform all to unicode
request_params = safe_decode(request_params)

# Support for the Event Listing view from plone.app.event
if isinstance(default_view, EventListing):
mode = request_params.get('mode', 'future')
date = request_params.get('date', None)
date = guess_date_from(date) if date else None
start, end = start_end_from_mode(mode, date, collection)
start, end = _prepare_range(collection, start, end)
custom_query.update(start_end_query(start, end))
# TODO: expand events. better yet, let collection.results
# do that

current_path_value = request_params.get('path')
urlquery = base_query(request_params, ['path'])

# Get all collection results with additional filter defined by urlquery
custom_query.update(urlquery)
custom_query = make_query(custom_query)
catalog_results = ICollection(collection).results(
batch=False,
brains=True,
custom_query=custom_query
)

# Entry to clear all filters
urlquery_all = {
k: v for k, v in urlquery.items() if k != 'path'
}
ret = [{
'title': translate(
_('location_home', default=u'Home'), context=getRequest()
),
'url': u'{0}/?{1}'.format(
collection_url,
urlencode(safe_encode(urlquery_all), doseq=True)
),
'value': 'all',
'css_class': 'navTreeItem filterItem filter-all selected',
'count': len(catalog_results),
'level': 0,
'contenttype': 'folder'
}]

if not catalog_results:
return ret

portal = plone.api.portal.get()
portal_path = portal.getPhysicalPath()
qspaths = current_path_value and current_path_value.split('/') or []
filtered_path = '/'.join(list(portal_path) + qspaths)
grouped_results = {}
level = len(qspaths) + 1
for brain in catalog_results:

# Get path, remove portal root from path, remove leading /
path = brain.getPath()
if path.startswith(filtered_path):
path = path[len(filtered_path) + 1:]
else:
continue

# If path is in the root, don't add anything to path
paths = path.split('/')
if len(paths) == 1:
continue

first_path = paths[0]
if first_path in grouped_results:
# Add counter, if path is already present
grouped_results[first_path]['count'] += 1
continue

title = first_path
ctype = 'folder'
container = portal.portal_catalog.searchResults({'path': {
'query': '/'.join(list(portal.getPhysicalPath()) +
qspaths +
[first_path]),
'depth': 0,
}})
if len(container) > 0:
title = container[0].Title
ctype = container[0].portal_type.lower()

# Build filter url query
_urlquery = urlquery.copy()
_urlquery['path'] = '/'.join(qspaths + [first_path])
query_param = urlencode(safe_encode(_urlquery), doseq=True)
url = u'/'.join([it for it in [
collection_url,
view_name,
'?' + query_param if query_param else None
] if it])

css_class = 'navTreeItem filterItem {0}'.format(
'filter-' + idnormalizer.normalize(first_path)
)

grouped_results[first_path] = {
'title': title,
'url': url,
'value': first_path,
'css_class': css_class,
'count': 1,
'level': level,
'contenttype': ctype,
}

# Add the selected paths
item = portal
level = 0
for path in qspaths:
item = item.get(path, None)
if not item:
break

# Get the results just in this subfolder
item_path = '/'.join(item.getPhysicalPath())[
len('/'.join(portal_path)) + 1:]
custom_query = {'path': item_path}
custom_query.update(urlquery)
custom_query = make_query(custom_query)
catalog_results = ICollection(collection).results(
batch=False,
brains=True,
custom_query=custom_query
)
level += 1
_urlquery = urlquery.copy()
_urlquery['path'] = '/'.join(qspaths[:qspaths.index(path) + 1])
query_param = urlencode(safe_encode(_urlquery), doseq=True)
url = u'/'.join([it for it in [
collection_url,
view_name,
'?' + query_param if query_param else None
] if it])
ret.append({'title': item.Title(),
'url': url,
'value': path,
'css_class': 'filter-%s selected' % idnormalizer.normalize(path),
'count': len(catalog_results),
'level': level,
'contenttype': item.portal_type.lower()})

grouped_results = grouped_results.values()

# TODO: sortable option, probably should just sort by position in container
# if callable(sort_key_function):
# grouped_results = sorted(grouped_results, key=sort_key_function)

ret += grouped_results

return ret
25 changes: 25 additions & 0 deletions src/collective/collectionfilter/interfaces.py
Expand Up @@ -166,6 +166,31 @@ class ICollectionSearchSchema(ICollectionFilterBaseSchema):
"""


class ICollectionLocationFilterSchema(ICollectionFilterBaseSchema):
"""Schema for the navigation filter.
"""

show_count = schema.Bool(
title=_(u'label_show_count', default=u'Show count'),
description=_(
u'help_show_count',
default=u'Show the result count for each filter group.'),
default=False,
required=False
)

cache_enabled = schema.Bool(
title=_(u"label_cache_enabled", default=u"Enable Cache"),
description=_(
u'help_cache_enabled',
default=u"Enable caching of filter items. The cache is cleared as"
u" soon as the database has any changes."
),
default=True,
required=False,
)


class IGroupByCriteria(Interface):
"""Interface for the GroupByCriteria utility.
Expand Down
12 changes: 12 additions & 0 deletions src/collective/collectionfilter/portlets/configure.zcml
Expand Up @@ -43,6 +43,18 @@
edit_permission="plone.app.portlets.ManagePortlets"
/>

<plone:portlet
name="collective.collectionfilter.portlets.CollectionLocationFilter"
interface=".locationfilter.ICollectionLocationPortlet"
assignment=".locationfilter.Assignment"
renderer=".locationfilter.Renderer"
addview=".locationfilter.AddForm"
editview=".locationfilter.EditForm"
view_permission="zope2.View"
edit_permission="plone.app.portlets.ManagePortlets"
/>


<genericsetup:registerProfile
name="default"
title="collective.collectionfilter portlets"
Expand Down
34 changes: 34 additions & 0 deletions src/collective/collectionfilter/portlets/locationfilter.pt
@@ -0,0 +1,34 @@
<aside class="portlet portletNavigationTree ${view/filterClassName} locationFilter ${view/settings/patCollectionFilter}"
data-pat-collectionfilter='{
"collectionUUID": "${view/settings/target_collection}",
"reloadURL": "${view/reload_url}",
"contentSelector": "${view/settings/content_selector}"
}'
tal:condition="view/available"
i18n:domain="plone">

<tal:if condition="view/settings/target_collection">
<nav class="portletContent" tal:define="results view/results">

<header class="portletHeader" tal:condition="view/title" tal:content="view/title">Title</header>
<ul class="navTree navTreeLevel0">
<tal:items repeat="item results">
<li class="navTreeItem">
<ul class="navTree navTreeLevel${item/level}">
<li class="${item/css_class}">
<a
rel="nofollow"
href="${item/url}"
title="Count: ${item/count}"
class="contenttype-${item/contenttype}">
<span tal:content="item/title">Item</span>
<tal:show_count condition="view/settings/show_count">(<span tal:replace="item/count" />)</tal:show_count>
</a>
</li>
</ul>
</li>
</tal:items>
</ul>
</nav>
</tal:if>
</aside>

0 comments on commit ab6b364

Please sign in to comment.