def _updateFacetedFilters(self): """Update vocabulary used for "Taken over by". Make sure the default for contacts 'c5' widget is not a list.""" logger.info( "Updating faceted filter \"Taken over by\" for every MeetingConfigs..." ) for cfg in self.tool.objectValues('MeetingConfig'): obj = cfg.searches.searches_items # update vocabulary for relevant filters criteria = ICriteria(obj) criteria.edit( 'c12', **{ 'vocabulary': 'Products.PloneMeeting.vocabularies.creatorswithnobodyforfacetedfiltervocabulary' }) criteria.edit( 'c27', **{ 'vocabulary': 'Products.PloneMeeting.vocabularies.associatedgroupsvocabulary' }) logger.info('Done.') logger.info("Updating faceted filter \"Defined in\" for 'c5' " "criterion of contacts/orgs-searches...") obj = self.portal.contacts.get('orgs-searches') # as default is not correct (a string instead a list, we can not use edit or it fails to validate) criteria = ICriteria(obj) criterion = criteria.get('c5') criterion.default = u'collective.contact.plonegroup.interfaces.IPloneGroupContact' logger.info('Done.')
def upgrade_to_1004_daterange_widget(context): from eea.facetednavigation.subtypes.interfaces import IFacetedNavigable from eea.facetednavigation.layout.interfaces import IFacetedLayout from eea.facetednavigation.interfaces import ICriteria from eea.facetednavigation.widgets.storage import Criterion brains = api.content.find(object_provides=IFacetedNavigable.__identifier__) layouts = ("faceted-agenda-ungrouped-view-items", "faceted-agenda-view-items") for brain in brains: obj = brain.getObject() if IFacetedLayout(obj).layout not in layouts: continue criterion = ICriteria(obj) for key, criteria in criterion.items(): if criteria.get("widget") != "daterange": continue if criteria.get("usePloneDateFormat") is True: continue logger.info("Upgrade daterange widget for faceted {0}".format(obj)) position = criterion.criteria.index(criteria) values = criteria.__dict__ values["usePloneDateFormat"] = True criterion.criteria[position] = Criterion(**values) criterion.criteria._p_changed = 1
def edit(self, cid, **kwargs): """ See IFacetedCriterionHandler """ criteria = ICriteria(self.context) widget = criteria.widget(cid=cid) criterion = criteria.get(cid) if kwargs.pop('__new__', False): criterion = criterion.__class__(cid='c0') widget = widget(self.context, self.request, criterion) wid = kwargs.pop('widget', None) properties = self.extractData(widget, **kwargs) if wid: properties['widget'] = wid update = {} for prop, value in properties.items(): form_key = 'faceted.%s.%s' % (cid, prop) if form_key not in kwargs and value is None: continue update[prop] = value if update: criteria.edit(cid, **update) if widget.hidden: notify(FacetedGlobalSettingsChangedEvent(self.context)) elif set(['hidden', 'operator']).intersection(update.keys()): notify(FacetedGlobalSettingsChangedEvent(self.context)) return self._redirect('Changes saved')
def test_get_generation_context_filtered_query(self): """ If a filter is used in the facetedQuery, elements displayed in the dashboard are correctly given to the template. """ faceted_query = self.folder.restrictedTraverse('@@faceted_query') # for now 3 elements self.assertEquals(len(faceted_query.query()), 3) # filter on text, 'Folder 2' self.assertEquals(ICriteria(self.folder).get('c2').index, u'SearchableText') self.request.form['c2[]'] = 'Folder 2' self.assertEquals(len(faceted_query.query()), 1) # generation context respect query self.request.form['facetedQuery'] = '' gen_context = self.view._get_generation_context(self.helper, self.dashboardtemplate) self.assertEquals(len(gen_context['uids']), 1) # facetedQuery is passed to the generation context as json # reset query, back to 3 elements found self.request.form = {} self.assertEquals(len(faceted_query.query()), 3) self.request.form['facetedQuery'] = '' gen_context = self.view._get_generation_context(self.helper, self.dashboardtemplate) self.assertEquals(len(gen_context['uids']), 3) # 'facetedQuery' is received as a serialized JSON of query criteria self.request.form['facetedQuery'] = '{"c2":"Folder 2"}' gen_context = self.view._get_generation_context(self.helper, self.dashboardtemplate) self.assertEquals(len(gen_context['uids']), 1)
def add_sorting_widget(context): """ As in version 7.1 we removed default sorting by effective date, in order to maintain backward compatibility we will add a sorting widget, hidden for all faceted navigable items where this widget is not present """ ctool = getToolByName(context, 'portal_catalog') iface = interfaceToName(context, IFacetedNavigable) brains = ctool.unrestrictedSearchResults(object_provides=iface) count = 0 for brain in brains: try: doc = brain.getObject() settings = ICriteria(doc) sorting = [ criterion for criterion in settings.values() if criterion.widget == 'sorting' ] if sorting: continue settings.add('sorting', 'right', title='Sort on', default='effective(reverse)', hidden=True) except Exception, err: logger.exception(err) else: logger.info('Added sorting widget for: %s', doc.absolute_url()) count += 1
def delete(self, cid, **kwargs): """ See IFacetedCriterionHandler """ try: ICriteria(self.context).delete(cid) except (TypeError, KeyError), err: msg = err
def _exportNode(self): """Export the object as a DOM node. """ node = self._getObjectNode('object') criteria = ICriteria(self.context) exporter = queryMultiAdapter((criteria, self.environ), IBody) node.appendChild(exporter.node) return node
def update(self, **kwargs): """ Update position by given slots """ logger.debug(kwargs) kwargs = self._request_form(kwargs) ICriteria(self.context).position(**kwargs) return self._redirect('Position changed')
def _importNode(self, node): """Import the object from the DOM node. """ criteria = ICriteria(self.context) for child in node.childNodes: if child.nodeName != 'criteria': continue importer = queryMultiAdapter((criteria, self.environ), IBody) importer.node = child
def language_present(self): """ Is there any widget for Language index? """ criteria = ICriteria(self.context) for criterion in criteria.values(): if criterion.get('index', None) == 'Language': if not criterion.hidden: return True return False
def add(self, **kwargs): """ See IFacetedCriteriaHandler """ wid = kwargs.pop('wtype', None) position = kwargs.pop('wposition', 'right') section = kwargs.pop('wsection', 'default') try: ICriteria(self.context).add(wid, position, section) except NameError, err: msg = err
def delete(self, cid, **kwargs): """ See IFacetedCriterionHandler """ try: ICriteria(self.context).delete(cid) except (TypeError, KeyError) as err: msg = err else: msg = _(u'Filter deleted') return self._redirect(msg=msg)
def defaultForVocabulary(self, vocabulary): """ return the faceted nav default for a particular criteria """ for criterion in ICriteria(self.context).criteria: if criterion.vocabulary == vocabulary: return criterion.default return None
def test_enableFacetedDashboardFor(self): """This method will enable the faceted navigation for a given folder.""" # faceted can be enabled using the default widgets catalog = self.portal.portal_catalog folder2_id = self.portal.invokeFactory('Folder', 'folder2', title='Folder2') folder2 = getattr(self.portal, folder2_id) folder2.reindexObject() folder2UID = folder2.UID() # not enabled for now self.assertFalse(IFacetedNavigable.providedBy(folder2)) self.assertTrue(catalog(UID=folder2UID)) self.assertFalse(catalog(UID=folder2UID, object_provides=IFacetedNavigable.__identifier__)) enableFacetedDashboardFor(folder2) # several things are done : # faceted is enabled self.assertTrue(IFacetedNavigable.providedBy(folder2)) # used faceted layout is 'faceted-table-items' self.assertEquals(IFacetedLayout(folder2).layout, 'faceted-table-items') # left portlets are shown self.assertFalse(IHidePloneLeftColumn.providedBy(folder2)) # folder2 was reindexed, especially provided interfaces self.assertTrue(catalog(UID=folder2UID, object_provides=IFacetedNavigable.__identifier__)) # redirect is swallowed, indeed enabling faceted on a folder redirects to it self.assertEquals(self.portal.REQUEST.RESPONSE.status, 200) # a xmlpath parameter can be passed to use a specific widgets xml file # calling this on an already enabled faceted will do nothing xmlpath = os.path.dirname(__file__) + '/faceted_conf/testing_widgets.xml' enableFacetedDashboardFor(folder2, xmlpath=xmlpath) # only one 'c44' widget in testing_widgets.xml, not added here self.assertFalse(ICriteria(folder2).get('c44')) # create a new folder and apply faceted with xmlpath to it folder3_id = self.portal.invokeFactory('Folder', 'folder3', title='Folder3') folder3 = getattr(self.portal, folder3_id) folder3.reindexObject() # an Exception is raised if xmlpath does not exist wrong_xmlpath = os.path.dirname(__file__) + '/faceted_conf/wrong_testing_widgets.xml' self.assertRaises(Exception, enableFacetedDashboardFor, folder3, wrong_xmlpath) # apply correct xmlpath enableFacetedDashboardFor(folder3, xmlpath=xmlpath) # same things are done except that the widgets are taken from the given xmlpath self.assertEquals(len(ICriteria(folder3).criteria), 1) self.assertTrue(ICriteria(folder3).get('c44'))
def test_get_generation_context(self): """ Changes are about 'uids' and 'brains' that are added to the pod template generation context if possible if nothing particular is done, every elements of the displayed dashboard are added to the template generation context. """ # document-generator view is called outside dashboard from base viewlet gen_context = self.view._get_generation_context(self.helper, self.dashboardtemplate) self.assertIn('view', gen_context) self.assertNotIn('facetedQuery', gen_context) self.assertIn('details', gen_context) # document-generator view is called from dashboard viewlet self.request.form['facetedQuery'] = '' # order is respected so sort_on created # Date catalog queries are 1 minute sensitive... # make sure self.folder created is really older than self.folder2 self.folder.creation_date = DateTime('2015/01/01 12:00') self.folder.reindexObject() self.assertEquals(ICriteria(self.folder).get('c0').widget, u'sorting') self.request.form['c0[]'] = 'created' self.assertEqual(self.dashboardtemplate.max_objects, 500) gen_context = self.view._get_generation_context(self.helper, self.dashboardtemplate) self.assertTrue('uids' in gen_context) self.assertEquals(len(gen_context['uids']), 3) self.assertTrue('brains' in gen_context) self.assertEquals(len(gen_context['brains']), 3) self.dashboardtemplate.max_objects = 2 gen_context = self.view._get_generation_context(self.helper, self.dashboardtemplate) self.assertEquals(len(gen_context['uids']), 2) self.assertEquals(len(gen_context['brains']), 2) self.dashboardtemplate.max_objects = 3 gen_context = self.view._get_generation_context(self.helper, self.dashboardtemplate) self.assertEquals(len(gen_context['uids']), 3) self.assertEquals(len(gen_context['brains']), 3) self.assertEqual(gen_context['details'], '1') # brains are sorted according to uids list self.assertEquals(gen_context['uids'], [brain.UID for brain in gen_context['brains']]) # we have 3 elements in the dashboard : self.folder and self.folder2 self.assertListEqual(['Folder', 'Folder 2', 'Dashboard template'], [brain.Title for brain in gen_context['brains']]) # order of query is kept in brains self.request.form['reversed'] = 'on' gen_context = self.view._get_generation_context(self.helper, self.dashboardtemplate) self.assertListEqual(['Dashboard template', 'Folder 2', 'Folder'], [brain.Title for brain in gen_context['brains']])
def perPage(self): num_per_page = 20 criteria = ICriteria(self.context) for cid, criterion in criteria.items(): widgetclass = criteria.widget(cid=cid) widget = widgetclass(self.context, self.request, criterion) if widget.widget_type == 'resultsperpage': kwargs = dict((key.replace('[]', ''), val) for key, val in self.request.form.items()) num_per_page = widget.results_per_page(kwargs) return num_per_page
def get_widgets(self, position='', section=''): """ Get all widgets """ criteria = ICriteria(self.context) for criterion in criteria.values(): if position and criterion.get('position', 'right') != position: continue if section and criterion.get('section', 'default') != section: continue widget = criteria.widget(wid=criterion.get('widget')) yield widget(self.context, self.request, criterion)
def add(self, **kwargs): """ See IFacetedCriterionHandler """ wid = kwargs.pop('wtype', self.request.get('wtype', None)) position = kwargs.pop('wposition', self.request.get('wposition', 'right')) section = kwargs.pop('wsection', self.request.get('wsection', 'default')) criteria = ICriteria(self.context) cid = criteria.add(wid, position, section) return self.edit(cid, __new__=True, **kwargs)
def query(self, batch=True, sort=False, **kwargs): """ Search using given criteria """ if self.request: kwargs.update(self.request.form) kwargs.pop('sort[]', None) kwargs.pop('sort', None) # jQuery >= 1.4 adds type to params keys # $.param({ a: [2,3,4] }) // "a[]=2&a[]=3&a[]=4" # Let's fix this kwargs = dict( (key.replace('[]', ''), val) for key, val in kwargs.items()) #fix for unicode error in indexes for key, val in kwargs.items(): if isinstance(val, str): kwargs[key] = val.decode('utf-8') query = self.criteria(sort=sort, **kwargs) # We don't want to do an unnecessary sort for a counter query counter_query = kwargs.pop('counter_query', False) if counter_query: query.pop('sort_on', None) query.pop('sort_order', None) catalog = getUtility(IFacetedCatalog) num_per_page = 20 criteria = ICriteria(self.context) brains_filters = [] for cid, criterion in criteria.items(): widgetclass = criteria.widget(cid=cid) widget = widgetclass(self.context, self.request, criterion) if widget.widget_type == 'resultsperpage': num_per_page = widget.results_per_page(kwargs) brains_filter = queryAdapter(widget, IWidgetFilterBrains) if brains_filter: brains_filters.append(brains_filter) b_start = safeToInt(kwargs.get('b_start', 0)) orphans = num_per_page * 20 / 100 # orphans = 20% of items per page if batch and not brains_filters: # add b_start and b_size to query to use better sort algorithm query['b_start'] = b_start query['b_size'] = num_per_page + orphans try: brains = catalog(self.context, **query) except Exception, err: logger.exception(err) return Batch([], 20, 0)
def add(self, **kwargs): """ See IFacetedCriterionHandler """ kwargs = self._request_form(kwargs) wid = kwargs.pop('wtype', None) position = kwargs.pop('wposition', 'right') section = kwargs.pop('wsection', 'default') criteria = ICriteria(self.context) cid = criteria.add(wid, position, section) return self.edit(cid, **kwargs)
def criteria(self, sort=False, **kwargs): """ Process catalog query """ if self.request: kwargs.update(self.request.form) # jQuery >= 1.4 adds type to params keys # $.param({ a: [2,3,4] }) // "a[]=2&a[]=3&a[]=4" # Let's fix this kwargs = dict((key.replace('[]', ''), val) for key, val in kwargs.items()) logger.debug("REQUEST: %r", kwargs) # Generate the catalog query criteria = ICriteria(self.context) query = {} for cid, criterion in criteria.items(): widget = criteria.widget(cid=cid) widget = widget(self.context, self.request, criterion) widget_query = widget.query(kwargs) if getattr(widget, 'faceted_field', False): widget_index = widget.data.get('index', '') if ('facet.field' in query and widget_index not in query['facet.field']): query['facet.field'].append(widget_index) else: query['facet.field'] = [widget_index] query.update(widget_query) # Handle language widgets if criterion.get('index', '') == 'Language': language_widget = queryMultiAdapter((widget, self.context), ILanguageWidgetAdapter) if not language_widget: continue query.update(language_widget(kwargs)) # Add default sorting criteria if sort and 'sort_on' not in query: query['sort_on'] = 'effective' query['sort_order'] = 'reverse' # Add default language. # Also make sure to return language-independent content. lang = self.language if lang: lang = [lang, ''] query.setdefault('Language', lang) logger.debug('QUERY: %s', query) return query
def add(self, **kwargs): """ See IFacetedCriteriaHandler """ wid = kwargs.pop('wtype', None) position = kwargs.pop('wposition', 'right') section = kwargs.pop('wsection', 'default') try: ICriteria(self.context).add(wid, position, section) except NameError as err: msg = err else: msg = _(u'Filter added') return self._redirect(msg=msg, to=self.redirect)
def _updateFacetedFilters(self): """Update vocabulary used for "Taken over by".""" logger.info( "Updating faceted filter \"Taken over by\" for every MeetingConfigs..." ) for cfg in self.tool.objectValues('MeetingConfig'): criteria = ICriteria(cfg.searches.searches_items) criteria.edit( 'c12', **{ 'vocabulary': 'Products.PloneMeeting.vocabularies.creatorswithnobodyforfacetedfiltervocabulary' }) logger.info('Done.')
def edit(self, **kwargs): """ See IFacetedCriteriaHandler """ criteria = ICriteria(self.context) handler = getMultiAdapter((self.context, self.request), name=u'faceted_update_criterion') for cid in criteria.keys(): properties = {} for key, value in kwargs.items(): if not key.startswith(cid): continue key = key[len(cid) + 1:] properties[key] = value handler.edit(cid, **properties) return self._redirect('Changes saved', to=self.redirect)
def query(self, cid, **kwargs): """ Count catalog items """ # Cleanup query kwargs.pop('sort_on', None) kwargs.pop('sort_order', None) kwargs.pop(cid, None) self.request.form.pop(cid, None) criteria = ICriteria(self.context) criterion = criteria.get(cid) # Query catalog handler = getMultiAdapter((self.context, self.request), name=u'faceted_query') if criterion.get('index', '') == 'Language': kwargs['_language_count_'] = True brains = handler.query(batch=False, sort=False, **kwargs) # Get index widget = criteria.widget(cid=cid)(self.context, self.request, criterion) vocabulary = dict((key, value) for key, value, count in widget.vocabulary(oll=True) if key not in ("", "all")) # Count count = getattr(widget, 'count', lambda brains, sequence: {}) res = count(brains, sequence=vocabulary.keys()) res.pop("", 0) oll = res.pop('all', 0) res = res.items() res.sort(key=operator.itemgetter(1), reverse=True) maxitems = widget.maxitems if maxitems: res = res[:maxitems] res.sort(key=operator.itemgetter(0), cmp=compare) # Return a of list of three items tuples (key, label, count) res = [(key, vocabulary.get(key, key), value) for key, value in res] res.insert(0, ('all', 'All', oll)) for item in res: yield item
def __init__(self, context, request): # TODO: update this to new workings of collection facet (now that we have a custom portal_type) super(DatasetsCollectionListView, self).__init__(context, request) self.datasets_url = self.context.absolute_url() self.criteria = ICriteria(self.context, {}) self.defaults = {} self.criterion = None for criterion in self.criteria.values(): if criterion.widget == 'pathselect': self.criterion = criterion if criterion.widget == 'sorting': default = criterion.default if not default: continue if '(reverse)' in default: default = default.replace('(reverse)', '', 1) self.defaults['reversed'] = True self.defaults[criterion.getId()] = default
def __call__(self, **kwargs): if self.request: kwargs.update(self.request.form) # Calling self.index() will set cache headers for varnish self.index() cid = kwargs.pop('cid', None) if not cid: return {} res = self.query(cid, **kwargs) criteria = ICriteria(self.context) criterion = criteria.get(cid) widget = criteria.widget(cid=cid)(self.context, self.request, criterion) return widget(vocabulary=res)
def query(self, form): """ Get value from form and return a catalog dict query """ query = {} # import ipdb; ipdb.set_trace() index = self.data.get("index", "") moreorless = self.data.get("moreorless", "") index = index.encode("utf-8", "replace") value = None if not index: return query if self.hidden: value = self.default else: value = form.get(self.data.getId(), "") if not value: return query # check if there are other criteria with same index second_value = None second_moreorless = None criteria = ICriteria(self.context) for cid, criterion in criteria.items(): if cid in form.keys() and cid != self.data.getId(): if criterion.index == index: second_value = form.get(cid) second_moreorless = criterion.moreorless value = int(value) # portal_catalog({'price':{'query':[2,1000],'range':'min:max'}}) if moreorless == u"more" and not second_value: range = "min" elif moreorless == u"less" and not second_value: range = "max" else: range = "min:max" if second_moreorless == u"more": value = [int(second_value), value] query[index] = {"query": value, "range": range} return query
def setFacetedNavigation(folder, request, force=False): subtyper = getMultiAdapter((folder, request), name=u'faceted_subtyper') if (subtyper.is_faceted or not subtyper.can_enable) and not force: return subtyper.enable() urlTool = plone.api.portal.get_tool(name='portal_url') path = '/' + '/'.join(urlTool.getRelativeContentPath(folder)) criteria = ICriteria(folder) for cid in criteria.keys(): criteria.delete(cid) criteria.add( 'checkbox', 'left', 'default', title=u'Organ', hidden=False, index='indicatedBodySystems', operator='or', vocabulary=u'eke.biomarker.IndicatedOrgansVocabulary', count=False, maxitems=0, sortreversed=False, hidezerocount=False, ) criteria.add('resultsperpage', 'bottom', 'default', title='Results per page', hidden=True, start=0, end=50, step=5, default=20) criteria.add('sorting', 'bottom', 'default', title='Sort on', hidden=True, default='sortable_title') criteria.add( 'checkbox', 'bottom', 'default', title='Obj provides', hidden=True, index='object_provides', operator='or', vocabulary=u'eea.faceted.vocabularies.ObjectProvides', default=[IBiomarker.__identifier__], count=False, maxitems=0, sortreversed=False, hidezerocount=False ) criteria.add('path', 'bottom', 'default', title='Path Search', hidden=True, index='path', default=path) criteria.add('text', 'top', 'default', title=u'Search', hidden=False, index='SearchableText', count=False, onlyallelements=True) IFacetedLayout(folder).update_layout('faceted_biomarkers_view') noLongerProvides(folder, IHidePloneLeftColumn) noLongerProvides(folder, IHidePloneRightColumn)
def _removeGroupsOfMatter(self): """Remove MeetingCategory.groupsOfMatter field and related.""" logger.info('Removing MeetingCategory.groupsOfMatter...') brains = self.portal.portal_catalog(meta_type='MeetingCategory') for brain in brains: cat = brain.getObject() if hasattr(cat, 'groupsOfMatter'): cat.setGroupsInCharge(cat.groupsOfMatter) delattr(cat, 'groupsOfMatter') else: self._already_migrated() return # remove portal_catalog index 'groupsOfMatter' self.removeUnusedIndexes(indexes=['groupsOfMatter']) # remove faceted filter for cfg in self.tool.objectValues('MeetingConfig'): # enable includeGroupsInChargeDefinedOnCategory so indexed groupsInCharge is correct cfg.setIncludeGroupsInChargeDefinedOnCategory(True) obj = cfg.searches.searches_items # update vocabulary for relevant filters criteria = ICriteria(obj) if criteria.get('c50'): criteria.delete('c50') # unselect 'c50' from dashboard filters and select 'c23' dashboardItemsListingsFilters = list(cfg.getDashboardItemsListingsFilters()) if 'c50' in dashboardItemsListingsFilters: dashboardItemsListingsFilters.remove('c50') dashboardItemsListingsFilters.append('c23') cfg.setDashboardItemsListingsFilters(dashboardItemsListingsFilters) dashboardMeetingAvailableItemsFilters = list(cfg.getDashboardMeetingAvailableItemsFilters()) if 'c50' in dashboardMeetingAvailableItemsFilters: dashboardMeetingAvailableItemsFilters.remove('c50') dashboardMeetingAvailableItemsFilters.append('c23') cfg.setDashboardMeetingAvailableItemsFilters(dashboardMeetingAvailableItemsFilters) dashboardMeetingLinkedItemsFilters = list(cfg.getDashboardMeetingLinkedItemsFilters()) if 'c50' in dashboardMeetingLinkedItemsFilters: dashboardMeetingLinkedItemsFilters.remove('c50') dashboardMeetingLinkedItemsFilters.append('c23') cfg.setDashboardMeetingLinkedItemsFilters(dashboardMeetingLinkedItemsFilters) logger.info('Done.')