diff --git a/browser/common.py b/browser/common.py index 215657a..caa35bf 100644 --- a/browser/common.py +++ b/browser/common.py @@ -140,6 +140,13 @@ class BaseView(GenericView): def value(self): return self.context + @Lazy + def uniqueId(self): + return util.getUidForObject(self.context) + #return zapi.getUtility(IRelationRegistry).getUniqueIdForObject(self.context) + + # type stuff + @Lazy def type(self): return IType(self.context) @@ -174,35 +181,47 @@ class BaseView(GenericView): for o in objs: yield BaseView(o, request) + # type listings + + def listTypes(self, include=None, exclude=None, sortOn='title'): + types = [dict(token=t.token, title=t.title) + for t in ITypeManager(self.context).listTypes(include, exclude)] + if sortOn: + types.sort(key=lambda x: x[sortOn]) + return types + def resourceTypes(self): - return util.KeywordVocabulary([(t.token, t.title) - for t in ITypeManager(self.context).listTypes(('resource',)) - if t.factory == Resource]) + return util.KeywordVocabulary(self.listTypes(('resource',) ('hidden',))) + #if t.factory == Resource]) # ? if necessary -> type.qualifiers def conceptTypes(self): - return util.KeywordVocabulary([(t.token, t.title) - for t in ITypeManager(self.context).listTypes(('concept',))]) + return util.KeywordVocabulary(self.listTypes(('concept',), ('hidden',))) + + def listTypesForSearch(self, include=None, exclude=None, sortOn='title'): + types = [dict(token=t.tokenForSearch, title=t.title) + for t in ITypeManager(self.context).listTypes(include, exclude)] + if sortOn: + types.sort(key=lambda x: x[sortOn]) + return types def typesForSearch(self): general = [('loops:resource:*', 'Any Resource'), ('loops:concept:*', 'Any Concept'),] - return util.KeywordVocabulary(general + sorted([(t.tokenForSearch, t.title) - for t in ITypeManager(self.context).types]) - + [('loops:*', 'Any')]) + return util.KeywordVocabulary(general + + self.listTypesForSearch(exclude=('system', 'hidden',)) + + [('loops:*', 'Any')]) def conceptTypesForSearch(self): general = [('loops:concept:*', 'Any Concept'),] - return util.KeywordVocabulary(general + [(t.tokenForSearch, t.title) - for t in ITypeManager(self.context).listTypes(('concept',))]) + return util.KeywordVocabulary(general + + self.listTypesForSearch(('concept',), ('system', 'hidden',),)) def resourceTypesForSearch(self): general = [('loops:resource:*', 'Any Resource'),] - return util.KeywordVocabulary(general + [(t.tokenForSearch, t.title) - for t in ITypeManager(self.context).listTypes(('resource',))]) + return util.KeywordVocabulary(general + + self.listTypesForSearch(('resource',), ('system', 'hidden'),)) - @Lazy - def uniqueId(self): - return zapi.getUtility(IRelationRegistry).getUniqueIdForObject(self.context) + # controllling editing @Lazy def editable(self): diff --git a/common.py b/common.py index 5f97ccc..f1ab5e4 100644 --- a/common.py +++ b/common.py @@ -42,29 +42,31 @@ class AdapterBase(object): adapts(IConcept) - _attributes = ('context', '__parent__', ) - _schemas = list(IConcept) + _adapterAttributes = ('context', '__parent__', ) + _contextAttributes = list(IConcept) def __init__(self, context): - self.context = context # to get the permission stuff right - self.__parent__ = context + self.context = context + self.__parent__ = context # to get the permission stuff right def __getattr__(self, attr): self.checkAttr(attr) return getattr(self.context, '_' + attr, None) def __setattr__(self, attr, value): - if attr in self._attributes: + if attr in self._adapterAttributes: object.__setattr__(self, attr, value) else: self.checkAttr(attr) setattr(self.context, '_' + attr, value) def checkAttr(self, attr): - if attr not in self._schemas: + if attr not in self._contextAttributes: raise AttributeError(attr) def __eq__(self, other): + if other is None: + return False return self.context == other.context @@ -72,7 +74,7 @@ class ResourceAdapterBase(AdapterBase): adapts(IResource) - _schemas = list(IResourceAdapter) + _contextAttributes = list(IResourceAdapter) # other adapters diff --git a/concept.py b/concept.py index 9e47451..4889ee9 100644 --- a/concept.py +++ b/concept.py @@ -149,10 +149,6 @@ class Concept(Contained, Persistent): def deassignChild(self, child, predicates=None): registry = zapi.getUtility(IRelationRegistry) - #relations = [] - #for rs in relationships: - # relations.extend(registry.query(first=self, second=concept, - # relationship=rs)) for rel in self.getChildRelations(predicates, child): registry.unregister(rel) @@ -192,6 +188,7 @@ class ConceptManager(BTreeContainer): typeConcept = None typePredicate = None defaultPredicate = None + predicateType = None def getLoopsRoot(self): return zapi.getParent(self) @@ -209,19 +206,27 @@ class ConceptManager(BTreeContainer): self.defaultPredicate = self['standard'] return self.defaultPredicate + def getPredicateType(self): + if self.predicateType is None: + dp = self.getDefaultPredicate() + self.predicateType = dp.conceptType + return self.predicateType + def getViewManager(self): return self.getLoopsRoot().getViewManager() # adapters and similar components -class ConceptSourceList(object): +class xxxConceptSourceList(object): + + # seems to be obsolete implements(schema.interfaces.IIterableSource) def __init__(self, context): - #self.context = context - self.context = removeSecurityProxy(context) + self.context = context + #self.context = removeSecurityProxy(context) root = self.context.getLoopsRoot() self.concepts = root.getConceptManager() diff --git a/configure.zcml b/configure.zcml index 5941aa5..83e1b68 100644 --- a/configure.zcml +++ b/configure.zcml @@ -78,7 +78,8 @@ + attributes="getTypePredicate getDefaultPredicate getTypeConcept + getPredicateType" /> diff --git a/helpers.txt b/helpers.txt index c006d56..1ff4f22 100755 --- a/helpers.txt +++ b/helpers.txt @@ -130,7 +130,7 @@ So let's check the type of the type object: >>> type_type.tokenForSearch 'loops:concept:type' >>> type_type.qualifiers - ('concept',) + ('concept', 'system') Now we register another type ('topic') and assign it to cc1: @@ -151,7 +151,7 @@ lazy properties, one should always get a new adapter: >>> cc1_type.typeProvider == topic True >>> topic_type.qualifiers - ('concept',) + ('concept', 'system') >>> topic_type.defaultContainer >>> topic_type.factory diff --git a/interfaces.py b/interfaces.py index f406283..061ce98 100644 --- a/interfaces.py +++ b/interfaces.py @@ -155,23 +155,30 @@ class IConceptView(Interface): """ Used for accessing a concept via a node's target attribute""" -class IConceptManager(ILoopsObject, IContainer): +#class IConceptManager(ILoopsObject, IContainer): +class IConceptManager(ILoopsObject): """ A manager/container for concepts. """ contains(IConcept) + def getTypeConcept(): + """ Return the concept that provides the type object, + i.e. the type of all types. + """ + def getTypePredicate(): """ Return the concept that provides the type predicate. """ - def getTypeConcept(): - """ Return the concept that provides the type object. - """ - def getDefaultPredicate(): """ Return the concept that provides the default (standard) predicate. """ + def getPredicateType(): + """ Return the concept that provides the predicate type object, + i.e. the type of all predicates. + """ + class IConceptManagerContained(Interface): containers(IConceptManager) diff --git a/knowledge/knowledge.py b/knowledge/knowledge.py index b6d5189..680becb 100644 --- a/knowledge/knowledge.py +++ b/knowledge/knowledge.py @@ -100,7 +100,7 @@ class Topic(AdapterBase, KnowledgeAdapterMixin): """ implements(IKnowledgeElement) - _attributes = ('context', '__parent__', 'parent') + _adapterAttributes = ('context', '__parent__', 'parent') def getParent(self): parents = self.context.getParents((self.standardPred,)) diff --git a/organize/party.py b/organize/party.py index 3a39d42..9349c29 100644 --- a/organize/party.py +++ b/organize/party.py @@ -72,8 +72,8 @@ class Person(AdapterBase, BasePerson): implements(IPerson) - _attributes = ('context', '__parent__', 'userId', 'phoneNumbers') - _schemas = list(IPerson) + list(IConcept) + _adapterAttributes = ('context', '__parent__', 'userId', 'phoneNumbers') + _contextAttributes = list(IPerson) + list(IConcept) def getUserId(self): return getattr(self.context, '_userId', None) @@ -162,8 +162,8 @@ class Address(AdapterBase): implements(IAddress) - _attributes = ('context', '__parent__', 'lines') - _schemas = list(IAddress) + list(IConcept) + _adapterAttributes = ('context', '__parent__', 'lines') + _contextAttributes = list(IAddress) + list(IConcept) def getLines(self): return getattr(self.context, '_lines', []) diff --git a/organize/task.py b/organize/task.py index b0ba59b..3203bfd 100644 --- a/organize/task.py +++ b/organize/task.py @@ -39,6 +39,6 @@ class Task(AdapterBase): implements(ITask) - _attributes = ('context', '__parent__',) - _schemas = list(ITask) + list(IConcept) + _adapterAttributes = ('context', '__parent__',) + _contextAttributes = list(ITask) + list(IConcept) diff --git a/query.py b/query.py index 0155ccb..95d2bd3 100644 --- a/query.py +++ b/query.py @@ -28,6 +28,7 @@ from zope import traversing from zope.app.catalog.interfaces import ICatalog from zope.cachedescriptors.property import Lazy +from cybertools.typology.interfaces import IType from loops.interfaces import IConcept from loops.common import AdapterBase from loops.type import TypeInterfaceSourceList @@ -59,7 +60,7 @@ class BaseQuery(object): def loopsRoot(self): return self.context.context.getLoopsRoot() - def queryConcepts(self, title=None, type=None): + def queryConcepts(self, title=None, type=None, **kw): if type.endswith('*'): start = type[:-1] end = start + '\x7f' @@ -71,16 +72,23 @@ class BaseQuery(object): else: result = cat.searchResults(loops_type=(start, end)) result = set(r for r in result if r.getLoopsRoot() == self.loopsRoot) + if 'exclude' in kw: + r1 = set() + for r in result: + qur = IType(r).qualifiers + if not [qux for qux in kw['exclude'] if qux in qur]: + r1.add(r) + result = r1 return result - def queryConceptsWithChildren(self, title=None, type=None, uid=None): + def queryConceptsWithChildren(self, title=None, type=None, uid=None, **kw): if title: # there are a few characters that the index doesn't like title = title.replace('(', ' ').replace(')', ' ') if not title and not uid and (type is None or '*' in type): return None result = set() if not uid: - queue = list(self.queryConcepts(title=title, type=type)) + queue = list(self.queryConcepts(title=title, type=type, **kw)) else: queue = [util.getObjectForUid(uid)] concepts = [] @@ -100,7 +108,7 @@ class BaseQuery(object): class FullQuery(BaseQuery): def query(self, text=None, type=None, useTitle=True, useFull=False, - conceptTitle=None, conceptUid=None, conceptType=None): + conceptTitle=None, conceptUid=None, conceptType=None, **kw): result = set() rc = self.queryConceptsWithChildren(title=conceptTitle, uid=conceptUid, type=conceptType) @@ -136,10 +144,10 @@ class ConceptQuery(BaseQuery): """ Find concepts of type `type` whose title starts with `title`. """ - def query(self, title=None, type=None): + def query(self, title=None, type=None, **kw): if title and not title.endswith('*'): title += '*' - return self.queryConcepts(title=title, type=type) + return self.queryConcepts(title=title, type=type, **kw) # QueryConcept: concept objects that allow querying the database. @@ -161,7 +169,7 @@ class QueryConcept(AdapterBase): implements(IQueryConcept) - _schemas = list(IQueryConcept) + list(IConcept) + _contextAttributes = list(IQueryConcept) + list(IConcept) TypeInterfaceSourceList.typeInterfaces += (IQueryConcept,) diff --git a/resource.py b/resource.py index 32429e4..6f071b1 100644 --- a/resource.py +++ b/resource.py @@ -200,10 +200,9 @@ class FileAdapter(ResourceAdapterBase): """ implements(IFile) - _schemas = list(IFile) + list(IBaseResource) - # let the adapter handle the data attribute: - _attributes = ResourceAdapterBase._attributes + ('data',) + _contextAttributes = list(IFile) + list(IBaseResource) + _adapterAttributes = ResourceAdapterBase._adapterAttributes + ('data',) def setData(self, data): self.context.data = data def getData(self): return self.context.data @@ -251,7 +250,7 @@ class DocumentAdapter(ResourceAdapterBase): """ # let the adapter handle the data attribute: - _attributes = ResourceAdapterBase._attributes + ('data',) + _adapterAttributes = ResourceAdapterBase._adapterAttributes + ('data',) def setData(self, data): self.context._data = data.replace('\r', '') def getData(self): return self.context._data @@ -263,7 +262,7 @@ class TextDocumentAdapter(DocumentAdapter): """ implements(IDocument) - _schemas = list(IDocument) + list(IBaseResource) + _contextAttributes = list(IDocument) + list(IBaseResource) class NoteAdapter(DocumentAdapter): @@ -271,7 +270,7 @@ class NoteAdapter(DocumentAdapter): """ implements(INote) - _schemas = list(INote) + list(IBaseResource) + _contextAttributes = list(INote) + list(IBaseResource) # other adapters diff --git a/search/README.txt b/search/README.txt index 9eaa82c..d4d61df 100755 --- a/search/README.txt +++ b/search/README.txt @@ -78,13 +78,13 @@ zcml in real life: >>> t = searchView.typesForSearch() >>> len(t) - 11 + 9 >>> t.getTermByToken('loops:resource:*').title 'Any Resource' >>> t = searchView.conceptTypesForSearch() >>> len(t) - 5 + 3 >>> t.getTermByToken('loops:concept:*').title 'Any Concept' diff --git a/search/browser.py b/search/browser.py index a86d682..7093edd 100644 --- a/search/browser.py +++ b/search/browser.py @@ -77,11 +77,11 @@ class Search(BaseView): request.response.setHeader('Content-Type', 'text/plain; charset=UTF-8') title = request.get('searchString', '').replace('(', ' ').replace(')', ' ') type = request.get('searchType') or 'loops:concept:*' - result = ConceptQuery(self).query(title=title, type=type) - registry = component.getUtility(IRelationRegistry) + result = ConceptQuery(self).query(title=title, type=type, exclude=('system',)) + #registry = component.getUtility(IRelationRegistry) # simple way to provide JSON format: return str(sorted([[`o.title`[2:-1] + ' (%s)' % `o.conceptType.title`[2:-1], - `registry.getUniqueIdForObject(o)`] + `int(util.getUidForObject(o))`] for o in result if o.getLoopsRoot() == self.loopsRoot])).replace('\\\\x', '\\x') #return str(sorted([[`o.title`[2:-1], `traversing.api.getName(o)`[2:-1]] diff --git a/type.py b/type.py index a810395..e7138bb 100644 --- a/type.py +++ b/type.py @@ -22,13 +22,14 @@ Type management stuff. $Id$ """ -from zope.app import zapi +from zope import component, schema from zope.component import adapts from zope.interface import implements from zope.cachedescriptors.property import Lazy from zope.dottedname.resolve import resolve -from zope import schema from zope.security.proxy import removeSecurityProxy +from zope.traversing.api import getName + from cybertools.typology.type import BaseType, TypeManager from cybertools.typology.interfaces import ITypeManager from loops.interfaces import ILoopsObject, IConcept, IResource @@ -44,7 +45,8 @@ class LoopsType(BaseType): adapts(ILoopsObject) - factoryMapping = dict(concept=Concept, resource=Resource, document=Document) + factoryMapping = dict(concept=Concept, resource=Resource) + #document=Document) containerMapping = dict(concept='concepts', resource='resources') @Lazy @@ -60,27 +62,31 @@ class LoopsType(BaseType): @Lazy def tokenForSearch(self): tp = self.typeProvider - typeName = tp is None and 'unknown' or str(zapi.getName(tp)) + typeName = tp is None and 'unknown' or str(getName(tp)) return ':'.join(('loops', self.qualifiers[0], typeName,)) @Lazy def typeInterface(self): - adapter = zapi.queryAdapter(self.typeProvider, ITypeConcept) + adapter = component.queryAdapter(self.typeProvider, ITypeConcept) if adapter is not None: return removeSecurityProxy(adapter.typeInterface) else: - conceptType = self.typeProvider - typeConcept = self.root.getConceptManager().getTypeConcept() - if conceptType is typeConcept: - return ITypeConcept + if self.typeProvider is self.typeConcept: + return ITypeConcept # typeConcept always is a type concept return None @Lazy def qualifiers(self): ti = self.typeInterface if ti is None or not issubclass(ti, IResourceAdapter): - return ('concept',) - return ('resource',) + qu = ['concept',] + else: + qu = ['resource',] + # check typeProvider for additional qualifiers: + if self.typeProvider in (self.typeConcept, self.predicateType,): + qu.append('system') + # how to set a type to 'hidden'? + return tuple(qu) @Lazy def factory(self): @@ -91,10 +97,6 @@ class LoopsType(BaseType): def defaultContainer(self): return self.root[self.containerMapping.get(self.qualifiers[0], 'concept')] - @Lazy - def root(self): - return self.context.getLoopsRoot() - @Lazy def typeProvider(self): # TODO: unify this type attribute naming... @@ -115,6 +117,20 @@ class LoopsType(BaseType): result['default'].append(opt) return result + # general infos + + @Lazy + def root(self): + return self.context.getLoopsRoot() + + @Lazy + def typeConcept(self): + return self.root.getConceptManager().getTypeConcept() + + @Lazy + def predicateType(self): + return self.root.getConceptManager().getPredicateType() + class LoopsTypeInfo(LoopsType): """ The type info class used by the type manager for listing types. @@ -144,13 +160,10 @@ class ConceptTypeInfo(LoopsTypeInfo): class ResourceType(LoopsType): """ The 'old-style' resource type - different classes (Document, MediaAsset) - per type. Will be replaced by new style types that are governed by + per type. Are replaced by new style types that are governed by type concepts as is already the case for concepts. """ - #adapts(IResource) - - #typeTitles = {'MediaAsset': u'Media Asset'} typeTitles = {} typeInterface = None @@ -165,8 +178,6 @@ class ResourceType(LoopsType): @Lazy def token(self): return '.'.join((self.factory.__module__, self.className)) - #cn = self.className - #return '/'.join(('.loops/resources', cn.lower(),)) @Lazy def tokenForSearch(self): @@ -240,7 +251,7 @@ class TypeConcept(AdapterBase): implements(ITypeConcept) - _schemas = list(ITypeConcept) + list(IConcept) + _contextAttributes = list(ITypeConcept) + list(IConcept) def getTypeInterface(self): ti = getattr(self.context, '_typeInterface', None) @@ -255,10 +266,8 @@ class TypeConcept(AdapterBase): def getOptions(self): return getattr(self.context, '_options', []) - #return super(TypeConcept, self).options or [] def setOptions(self, value): self.context._options = value - #super(TypeConcept, self).options = value options = property(getOptions, setOptions) diff --git a/util.py b/util.py index 4e0c09f..07c6f86 100644 --- a/util.py +++ b/util.py @@ -36,8 +36,16 @@ _ = MessageFactory('zope') # it's easier not use a special i18n domain... class KeywordVocabulary(vocabulary.SimpleVocabulary): def __init__(self, items, *interfaces): - terms = [vocabulary.SimpleTerm(token, token, title) - for token, title in items] + """ ``items`` may be a tuple of (token, title) or a dictionary + with corresponding elements named 'token' and 'title'. + """ + terms = [] + for t in items: + if type(t) is dict: + token, title = t['token'], t['title'] + else: + token, title = t + terms.append(vocabulary.SimpleTerm(token, token, title)) super(KeywordVocabulary, self).__init__(terms, *interfaces)