diff --git a/base.py b/base.py index 9b9b03c..c401bb1 100644 --- a/base.py +++ b/base.py @@ -59,7 +59,7 @@ class Loops(Folder): def getLoopsRoot(self): return self - def getAllParents(self, collectGrants=False): + def getAllParents(self, collectGrants=False, ignoreTypes=False): return Jeep() def getConceptManager(self): diff --git a/browser/concept.py b/browser/concept.py index daaa383..be05b75 100644 --- a/browser/concept.py +++ b/browser/concept.py @@ -281,6 +281,8 @@ class ConceptView(BaseView): for r in self.context.getChildRelations(sort=None)) if sort: rels = sorted(rels, key=lambda r: (r.order, r.title.lower())) + from loops.organize.personal.browser.filter import FilterView + fv = FilterView(self.context, self.request) for r in rels: if criteria: if not self.checkCriteria(r, criteria): @@ -294,7 +296,8 @@ class ConceptView(BaseView): break if skip: continue - yield r + if fv.check(r.context): + yield r def checkCriteria(self, relation, criteria): result = True @@ -352,11 +355,14 @@ class ConceptView(BaseView): yield self.childViewFactory(r, self.request) def resources(self): + from loops.browser.resource import ResourceRelationView + from loops.organize.personal.browser.filter import FilterView + fv = FilterView(self.context, self.request) rels = self.context.getResourceRelations() for r in rels: #yield self.childViewFactory(r, self.request, contextIsSecond=True) - from loops.browser.resource import ResourceRelationView - yield ResourceRelationView(r, self.request, contextIsSecond=True) + if fv.check(r.first): + yield ResourceRelationView(r, self.request, contextIsSecond=True) def unique(self, rels): result = Jeep() diff --git a/browser/form.py b/browser/form.py index 214f9d4..83fd72c 100644 --- a/browser/form.py +++ b/browser/form.py @@ -49,14 +49,15 @@ from cybertools.composer.schema.browser.common import schema_macros, schema_edit from cybertools.composer.schema.schema import FormState from cybertools.stateful.interfaces import IStateful from cybertools.typology.interfaces import IType, ITypeManager +from loops.browser.node import NodeView +from loops.browser.concept import ConceptRelationView from loops.common import adapted from loops.concept import Concept, ConceptRelation, ResourceRelation from loops.interfaces import IConcept, IConceptSchema from loops.interfaces import IResource, IResourceManager, IDocument from loops.interfaces import IFile, IExternalFile, INote, ITextDocument -from loops.browser.node import NodeView -from loops.browser.concept import ConceptRelationView from loops.i18n.browser import I18NView +from loops.organize.personal.browser.filter import FilterView from loops.query import ConceptQuery, IQueryConcept from loops.resource import Resource from loops.schema.field import relation_macros @@ -196,8 +197,11 @@ class ObjectForm(NodeView): return [dict(title=t.title, token=t.tokenForSearch) for t in types] def conceptsForType(self, token): + result = ConceptQuery(self).query(type=token) + fv = FilterView(self.context, self.request) + result = fv.apply(result) + result.sort(key=lambda x: x.title) noSelection = dict(token='none', title=u'not selected') - result = sorted(ConceptQuery(self).query(type=token), key=lambda x: x.title) predicateUid = self.defaultPredicateUid return ([noSelection] + [dict(title=o.title, diff --git a/concept.py b/concept.py index efc4654..caaa172 100644 --- a/concept.py +++ b/concept.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2010 Helmut Merz helmutm@cy55.de +# Copyright (c) 2011 Helmut Merz helmutm@cy55.de # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -151,16 +151,19 @@ class Concept(Contained, Persistent): def getConceptManager(self): return self.getLoopsRoot().getConceptManager() - def getAllParents(self, collectGrants=False, result=None): + def getAllParents(self, collectGrants=False, result=None, ignoreTypes=False): if result is None: result = Jeep() for rel in self.getParentRelations(): + if (ignoreTypes and + rel.predicate == self.getConceptManager().getTypePredicate()): + continue obj = rel.first uid = util.getUidForObject(obj) pi = result.get(uid) if pi is None: result[uid] = ParentInfo(obj, [rel]) - obj.getAllParents(collectGrants, result) + obj.getAllParents(collectGrants, result, ignoreTypes) elif rel not in pi.relations: pi.relations.append(rel) return result diff --git a/expert/browser/search.py b/expert/browser/search.py index 49e2666..dd756d3 100644 --- a/expert/browser/search.py +++ b/expert/browser/search.py @@ -30,6 +30,7 @@ from zope.traversing.api import getName, getParent from loops.browser.node import NodeView from loops.expert.concept import ConceptQuery, FullQuery +from loops.organize.personal.browser.filter import FilterView from loops import util from loops.util import _ @@ -59,4 +60,7 @@ class SearchResults(NodeView): type = self.globalOptions('expert.quicksearch') result = FullQuery(self).query(text=text, type=type, useTitle=True, useFull=True,) + fv = FilterView(self.context, self.request) + result = fv.apply(result) + result.sort(key=lambda x: x.title) return self.viewIterator(result) diff --git a/organize/personal/README.txt b/organize/personal/README.txt index 56af6f5..ed19349 100644 --- a/organize/personal/README.txt +++ b/organize/personal/README.txt @@ -14,6 +14,9 @@ and set up a simple loops site with a concept manager and some concepts (with all the type machinery, what in real life is done via standard ZCML setup): + >>> from loops.concept import Concept + >>> from loops.setup import addAndConfigureObject + >>> from loops.organize.setup import SetupManager >>> component.provideAdapter(SetupManager, name='organize') >>> from loops.organize.personal.setup import SetupManager @@ -114,6 +117,46 @@ Let's now trigger the saving of a favorite. 1 +Filters - Show only Certain Parts of the Concept Map +==================================================== + + >>> baseFilters = records['filters'] + >>> from loops.organize.personal.filter import Filters + >>> component.provideAdapter(Filters) + +Let's prepare some concepts and assignments to be used for filtering. + + >>> dGeneral = addAndConfigureObject(concepts, Concept, 'general', + ... conceptType=concepts['domain']) + >>> dProjects = concepts['projects'] # created in global setup + + >>> dGeneral.assignResource(resources['d001.txt']) + >>> dProjects.assignResource(resources['d002.txt']) + +Now we can define a simple filter that blocks certain concepts and resources. + + >>> from loops.organize.personal.interfaces import IFilters + >>> filters = IFilters(baseFilters) + >>> filters.add(dProjects, johnC) + '0000001' + +We access the filters via a filter view. + + >>> from loops.organize.personal.browser.filter import FilterView + >>> fv = FilterView(home, TestRequest()) + >>> fv.person = johnC + + >>> fv.check(resources['d001.txt']) + False + >>> fv.check(resources['d002.txt']) + True + >>> fv.check(resources['d003.txt']) + True + + >>> [r.__name__ for r in fv.apply(resources.values())] + [u'd002.txt', u'd003.txt'] + + Fin de partie ============= diff --git a/organize/personal/browser/configurator.py b/organize/personal/browser/configurator.py index 4951432..8ce2f9e 100644 --- a/organize/personal/browser/configurator.py +++ b/organize/personal/browser/configurator.py @@ -94,6 +94,6 @@ class PortletConfigurator(ViewConfigurator): title=_(u'Filters'), subMacro=personal_macros.macros['filters_portlet'], priority=195, - url=absoluteURL(self.context, self.request) + '/@@filters.html', + #url=absoluteURL(self.context, self.request) + '/@@filters.html', )) return [filters] diff --git a/organize/personal/browser/filter.py b/organize/personal/browser/filter.py index 8bcabf3..38d21d9 100644 --- a/organize/personal/browser/filter.py +++ b/organize/personal/browser/filter.py @@ -28,6 +28,7 @@ from zope.cachedescriptors.property import Lazy from cybertools.browser.configurator import ViewConfigurator, MacroViewProperty from loops.browser.node import NodeView +from loops.concept import Concept from loops.organize.party import getPersonForUser from loops.organize.personal.interfaces import IFilters from loops import util @@ -46,6 +47,16 @@ class FilterView(NodeView): def person(self): return getPersonForUser(self.context, self.request) + @Lazy + def targetUid(self): + target = self.virtualTargetObject + if (target and isinstance(target, Concept) and + target.conceptType != self.conceptManager.getTypeConcept()): + return util.getUidForObject(target) + else: + return None + + @Lazy def filters(self): records = self.loopsRoot.getRecordManager() @@ -64,6 +75,7 @@ class FilterView(NodeView): yield dict(url=self.getUrlForTarget(obj), uid=uid, title=obj.title, + typeTitle=obj.getType().title, description=obj.description, object=obj) @@ -83,5 +95,40 @@ class FilterView(NodeView): id = self.request.get('id') if not id: return - self.filters.deactivate(id) + #self.filters.deactivate(id) + self.filters.remove(id, self.person) self.request.response.redirect(self.virtualTargetUrl) + + @Lazy + def filterStructure(self): + result = {} + for item in self.listFilters(): + obj = item['object'] + result.setdefault(obj.getType(), set([])).add(obj) + return result + + def check(self, obj): + fs = self.filterStructure + if not fs: + return True + for objs in fs.values(): + if obj in objs: # the filtered object itself is always shown + return True + checked = {} + parents = [pi.object for pi in obj.getAllParents(ignoreTypes=True)] + if isinstance(obj, Concept): + parents.insert(0, obj) + for p in parents: + for t, v in fs.items(): + if checked.get(t): # type already present and set to True + continue + if p.conceptType == t: # type has to be checked + checked[t] = (p in v) + for x in checked.values(): + if not x: + return False + return True + + def apply(self, seq): + return [obj for obj in seq if self.check(obj)] + diff --git a/organize/personal/browser/personal_macros.pt b/organize/personal/browser/personal_macros.pt index 4ed1250..d9471e0 100644 --- a/organize/personal/browser/personal_macros.pt +++ b/organize/personal/browser/personal_macros.pt @@ -7,15 +7,13 @@ string:${view/virtualTargetUrl}/removeFavorite.html?id=${item/uid}; title string:Remove from favorites" i18n:attributes="title">X - Some object