# HG changeset patch # User Thierry Florac # Date 1512726092 -3600 # Node ID 14b2ae98f78c7c238e06efeaba651beab787a49b # Parent 4109e6d2991b254c77a0573a46e0b0b66ec0d5e6 Added global search engine diff -r 4109e6d2991b -r 14b2ae98f78c src/pyams_content/root/zmi/search.py --- a/src/pyams_content/root/zmi/search.py Mon Dec 04 15:36:40 2017 +0100 +++ b/src/pyams_content/root/zmi/search.py Fri Dec 08 10:41:32 2017 +0100 @@ -16,5 +16,263 @@ # import standard library # import interfaces +from hypatia.interfaces import ICatalog +from pyams_content.profile.interfaces import IAdminProfile +from pyams_content.root import ISiteRoot +from pyams_content.shared.common.interfaces.zmi import ISiteRootDashboardTable +from pyams_content.zmi.interfaces import IAllContentsMenu +from pyams_form.search import ISearchFields, SearchForm, SearchView, SearchResultsView +from pyams_i18n.interfaces import INegotiator +from pyams_sequence.interfaces import ISequentialIntIds +from pyams_skin.interfaces import IInnerPage, IContentSearch, IPageHeader +from pyams_skin.layer import IPyAMSLayer +from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION +from pyams_workflow.interfaces import IWorkflowVersions +from pyams_zmi.layer import IAdminLayer +from z3c.table.interfaces import IValues +from zope.intid import IIntIds # import packages +from hypatia.catalog import CatalogQuery +from hypatia.query import Eq, Any, Contains, Ge, Le +from pyams_catalog.query import CatalogResultSet +from pyams_content.shared.common import CONTENT_TYPES +from pyams_pagelet.interfaces import PageletCreatedEvent +from pyams_pagelet.pagelet import pagelet_config +from pyams_security.schema import Principal +from pyams_skin.page import DefaultPageHeaderAdapter +from pyams_skin.skin import apply_skin +from pyams_skin.table import BaseTable +from pyams_skin.viewlet.menu import MenuItem +from pyams_template.template import template_config +from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter +from pyams_utils.list import unique +from pyams_utils.registry import get_utility +from pyams_utils.url import absolute_url +from pyams_viewlet.viewlet import viewlet_config +from pyams_zmi.view import AdminView +from pyramid.view import view_config +from z3c.form import field +from zope.interface import implementer +from zope.schema import Datetime, Choice, List + +from pyams_content import _ + + +# +# Quick search adapters +# + +@view_config(name='quick-search.html', context=ISiteRoot, request_type=IPyAMSLayer, + permission=VIEW_SYSTEM_PERMISSION, renderer='json', xhr=True) +def site_root_quick_search_view(request): + """Site root quick search view""" + results = SiteRootQuickSearchResults(request.context, request) + if len(results.values) == 1: + result = results.values[0] + return {'status': 'redirect', + 'location': absolute_url(result, request, 'admin')} + else: + results.update() + return {'status': 'info', + 'content': {'html': results.render()}} + + +@implementer(ISiteRootDashboardTable) +class SiteRootQuickSearchResults(BaseTable): + """Site root quick search results table""" + + title = _("Quick search results") + + sortOn = None + + @property + def data_attributes(self): + attributes = super(SiteRootQuickSearchResults, self).data_attributes + attributes['table'] = {'data-ams-datatable-sorting': '[]', + 'data-ams-datatable-display-length': + IAdminProfile(self.request.principal).table_page_length} + return attributes + + +@adapter_config(context=(ISiteRoot, IPyAMSLayer, SiteRootQuickSearchResults), provides=IValues) +class SiteRootQuickSearchValues(ContextRequestViewAdapter): + """Site root quick search results view values adapter""" + + @property + def values(self): + intids = get_utility(IIntIds) + catalog = get_utility(ICatalog) + params = Eq(catalog['parents'], intids.register(self.context)) & \ + Any(catalog['content_type'], CONTENT_TYPES.keys()) + query = self.request.params.get('query') + if query: + sequence = get_utility(ISequentialIntIds) + if query.startswith('+'): + params &= Eq(catalog['oid'], sequence.get_full_oid(query)) + else: + query_params = Eq(catalog['oid'], sequence.get_full_oid(query)) + negotiator = get_utility(INegotiator) + for lang in {self.request.registry.settings.get('pyramid.default_locale_name', 'en'), + self.request.locale_name, + negotiator.server_language} | negotiator.offered_languages: + index_name = 'title:{0}'.format(lang) + if index_name in catalog: + index = catalog[index_name] + if index.check_query(query): + query_params |= Contains(index, ' and '.join((w+'*' for w in query.split()))) + params &= query_params + return unique(map(lambda x: IWorkflowVersions(x).get_last_versions()[0], + CatalogResultSet(CatalogQuery(catalog).query(params, + sort_index='modified_date', + reverse=True)))) + + +# +# Advanced search adapters +# + +@viewlet_config(name='advanced-search.menu', context=ISiteRoot, layer=IAdminLayer, + manager=IAllContentsMenu, permission=VIEW_SYSTEM_PERMISSION, weight=90) +class SiteRootAdvancedSearchMenu(MenuItem): + """Site root advanced search menu""" + + label = _("Advanced search") + icon_class = None + url = '#advanced-search.html' + + +class ISiteRootAdvancedSearchFields(ISearchFields): + """Site root advanced search fields""" + + content_type = List(title=_("Content types"), + value_type=Choice(vocabulary='PyAMS content types'), + required=False) + + owner = Principal(title=_("Owner"), + required=False) + + created_after = Datetime(title=_("Created after..."), + required=False) + + created_before = Datetime(title=_("Created before..."), + required=False) + + modified_after = Datetime(title=_("Modified after..."), + required=False) + + modified_before = Datetime(title=_("Modified before..."), + required=False) + + +@template_config(template='templates/advanced-search.pt', layer=IPyAMSLayer) +@implementer(IInnerPage) +class SiteRootAdvancedSearchForm(SearchForm): + """Site root advanced search form""" + + legend = _("Advanced search") + + def __init__(self, context, request): + super(SiteRootAdvancedSearchForm, self).__init__(context, request) + request.registry.notify(PageletCreatedEvent(self)) + apply_skin(self.request, 'PyAMS admin skin') + + fields = field.Fields(ISiteRootAdvancedSearchFields) + ajax_handler = 'advanced-search-results.html' + + +@adapter_config(context=(ISiteRoot, IPyAMSLayer, SiteRootAdvancedSearchForm), provides=IContentSearch) +class SiteRootAdvancedSearchFormSearchAdapter(ContextRequestViewAdapter): + """Site root advanced search form search adapter""" + + def get_search_results(self, data): + intids = get_utility(IIntIds) + catalog = get_utility(ICatalog) + params = Eq(catalog['parents'], intids.register(self.context)) + if data.get('content_type'): + params &= Any(catalog['content_type'], data['content_type']) + else: + params &= Any(catalog['content_type'], CONTENT_TYPES.keys()) + query = data.get('query') + if query: + sequence = get_utility(ISequentialIntIds) + if query.startswith('+'): + params &= Eq(catalog['oid'], sequence.get_full_oid(query)) + else: + query_params = Eq(catalog['oid'], sequence.get_full_oid(query)) + negotiator = get_utility(INegotiator) + for lang in {self.request.registry.settings.get('pyramid.default_locale_name', 'en'), + self.request.locale_name, + negotiator.server_language} | negotiator.offered_languages: + index_name = 'title:{0}'.format(lang) + if index_name in catalog: + index = catalog[index_name] + if index.check_query(query): + query_params |= Contains(index, ' and '.join((w+'*' for w in query.split()))) + params &= query_params + if data.get('owner'): + params &= Eq(catalog['role:owner'], data['owner']) + if data.get('created_after'): + params &= Ge(catalog['created_date'], data['created_after']) + if data.get('created_before'): + params &= Le(catalog['created_date'], data['created_before']) + if data.get('modified_after'): + params &= Ge(catalog['modified_date'], data['modified_after']) + if data.get('modified_before'): + params &= Le(catalog['modified_date'], data['modified_before']) + return unique(map(lambda x: IWorkflowVersions(x).get_last_versions()[0], + CatalogResultSet(CatalogQuery(catalog).query(params, + sort_index='modified_date', + reverse=True)))) + + +@pagelet_config(name='advanced-search.html', context=ISiteRoot, layer=IPyAMSLayer, + permission=VIEW_SYSTEM_PERMISSION) +class SiteRootAdvancedSearchView(SearchView): + """Site root advanced search view""" + + search_form_factory = SiteRootAdvancedSearchForm + + +@adapter_config(context=(ISiteRoot, IPyAMSLayer, SiteRootAdvancedSearchView), provides=IPageHeader) +class SiteRootAdvancedSearchHeaderAdapter(DefaultPageHeaderAdapter): + """Site root advanced search header adapter""" + + back_url = '#dashboard.html' + back_target = None + + icon_class = 'fa fa-fw fa-search' + + +@view_config(name='advanced-search-results.html', context=ISiteRoot, request_type=IPyAMSLayer, + permission=VIEW_SYSTEM_PERMISSION) +@implementer(ISiteRootDashboardTable) +class SiteRootAdvancedSearchResultsView(AdminView, SearchResultsView): + """Site root advanced search results view""" + + title = _("Advanced search results") + search_form_factory = SiteRootAdvancedSearchForm + + sortOn = None + + def __init__(self, context, request): + super(SiteRootAdvancedSearchResultsView, self).__init__(context, request) + request.registry.notify(PageletCreatedEvent(self)) + + @property + def data_attributes(self): + attributes = super(SiteRootAdvancedSearchResultsView, self).data_attributes + attributes['table'] = {'data-ams-datatable-sorting': '[]', + 'data-ams-datatable-display-length': + IAdminProfile(self.request.principal).table_page_length} + return attributes + + +@adapter_config(context=(ISiteRoot, IPyAMSLayer, SiteRootAdvancedSearchResultsView), provides=IValues) +class SearchResultsViewValuesAdapter(ContextRequestViewAdapter): + """Search results view values adapter""" + + @property + def values(self): + form = self.view.search_form_factory(self.context, self.request) + return form.get_search_results() or () diff -r 4109e6d2991b -r 14b2ae98f78c src/pyams_content/root/zmi/sites.py --- a/src/pyams_content/root/zmi/sites.py Mon Dec 04 15:36:40 2017 +0100 +++ b/src/pyams_content/root/zmi/sites.py Fri Dec 08 10:41:32 2017 +0100 @@ -23,20 +23,25 @@ from pyams_content.zmi.interfaces import ISiteTreeMenu, ISiteTreeTable from pyams_sequence.interfaces import ISequentialIdInfo from pyams_skin.interfaces import IInnerPage, IPageHeader +from pyams_skin.interfaces.container import ITableWithActions from pyams_skin.layer import IPyAMSLayer from pyams_utils.interfaces import VIEW_SYSTEM_PERMISSION from pyams_workflow.interfaces import IWorkflowPublicationInfo from pyams_zmi.interfaces.menu import ISiteManagementMenu from pyams_zmi.layer import IAdminLayer from z3c.table.interfaces import IColumn, IValues +from zope.intid.interfaces import IIntIds # import packages +from pyams_content.skin import pyams_content from pyams_pagelet.pagelet import pagelet_config from pyams_skin.container import ContainerView from pyams_skin.page import DefaultPageHeaderAdapter from pyams_skin.table import BaseTable, TrashColumn, ActionColumn, I18nColumn from pyams_skin.viewlet.menu import MenuItem from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter +from pyams_utils.fanstatic import get_resource_path +from pyams_utils.registry import get_utility from pyams_utils.url import absolute_url from pyams_viewlet.manager import viewletmanager_config from pyams_viewlet.viewlet import viewlet_config @@ -52,7 +57,7 @@ # Sites and blogs view # -@viewlet_config(name='site-tree.menu', layer=IAdminLayer, context=ISiteRoot, manager=ISiteManagementMenu, +@viewlet_config(name='site-tree.menu', context=ISiteRoot, layer=IAdminLayer, manager=ISiteManagementMenu, permission=VIEW_SYSTEM_PERMISSION, weight=5) @viewletmanager_config(name='site-tree.menu', layer=IAdminLayer, context=ISiteRoot, provides=ISiteTreeMenu) @implementer(ISiteTreeMenu) @@ -64,7 +69,7 @@ url = '#site-tree.html' -@implementer(IDashboardTable, ISiteTreeTable) +@implementer(IDashboardTable, ITableWithActions, ISiteTreeTable) class SiteTreeTable(BaseTable): """Site tree table""" @@ -76,8 +81,19 @@ @property def data_attributes(self): attributes = super(SiteTreeTable, self).data_attributes - attributes['table'] = {'data-ams-location': absolute_url(self.context, self.request), - 'data-ams-delete-target': 'delete-shared-site.json'} + intids = get_utility(IIntIds) + attributes.setdefault('table', {}).update({ + 'data-ams-plugins': 'pyams_content', + 'data-ams-plugin-pyams_content-src': get_resource_path(pyams_content), + 'data-ams-location': absolute_url(self.context, self.request), + 'data-ams-delete-target': 'delete-shared-site.json' + }) + attributes.setdefault('tr', {}).update({ + 'id': lambda x, col: '{0}::{1}'.format(self.id, intids.queryId(x)), + 'data-ams-location': lambda x, col: absolute_url(x.__parent__, self.request), + 'data-ams-tree-node-id': lambda x, col: intids.queryId(x), + 'data-ams-tree-node-parent-id': lambda x, col: intids.queryId(x.__parent__) + }) return attributes @@ -123,7 +139,7 @@ return '--' else: try: - return sequence.get_short_oid() + return sequence.get_base_oid() except TypeError: return '--' diff -r 4109e6d2991b -r 14b2ae98f78c src/pyams_content/root/zmi/templates/advanced-search.pt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_content/root/zmi/templates/advanced-search.pt Fri Dec 08 10:41:32 2017 +0100 @@ -0,0 +1,223 @@ +
+
+ + +

+ + +
+
+
Form prefix
+ +
+ +
+ +
+
+
Form suffix
+
+
diff -r 4109e6d2991b -r 14b2ae98f78c src/pyams_content/root/zmi/templates/dashboard.pt --- a/src/pyams_content/root/zmi/templates/dashboard.pt Mon Dec 04 15:36:40 2017 +0100 +++ b/src/pyams_content/root/zmi/templates/dashboard.pt Fri Dec 08 10:41:32 2017 +0100 @@ -13,7 +13,30 @@
- +
+
+

SEARCH - Between all contents

+
+
+
+
+
+ +
+ +
+
+
+
+
@@ -23,6 +46,6 @@
You are not actually concerned by any content.
-
+