diff --git a/README.txt b/README.txt
index 9338387..9201734 100755
--- a/README.txt
+++ b/README.txt
@@ -37,7 +37,7 @@ top-level loops container and a concept manager:
>>> #sorted(concepts)
>>> #sorted(resources)
>>> len(concepts) + len(resources)
- 14
+ 13
>>> loopsRoot = site['loops']
@@ -121,7 +121,7 @@ type manager.
>>> from loops.concept import ConceptTypeSourceList
>>> types = ConceptTypeSourceList(cc1)
>>> sorted(t.title for t in types)
- [u'Customer', u'Domain', u'Predicate', u'Query', u'Topic', u'Type', u'Unknown Type']
+ [u'Customer', u'Domain', u'Predicate', u'Topic', u'Type', u'Unknown Type']
Using a PredicateSourceList we can retrieve a list of the available
predicates.
@@ -197,7 +197,6 @@ types and predicates.
[(u'Customer', '.loops/concepts/customer'),
(u'Domain', '.loops/concepts/domain'),
(u'Predicate', '.loops/concepts/predicate'),
- (u'Query', '.loops/concepts/query'),
(u'Topic', '.loops/concepts/topic'),
(u'Type', '.loops/concepts/type'),
(u'Unknown Type', '.loops/concepts/unknown')]
@@ -487,7 +486,7 @@ view; these views we have to provide as multi-adapters:
>>> view = ConfigureView(m111, TestRequest(form = form))
>>> tt = view.targetTypes()
>>> len(tt)
- 10
+ 9
>>> sorted((t.token, t.title) for t in view.targetTypes())[1]
('.loops/concepts/domain', u'Domain')
>>> view.update()
@@ -580,7 +579,7 @@ is ``NodeView.getUrlForTarget()`` that expects a ConceptView, ResourceView,
or ConceptRelationView as its argument.
>>> view.getUrlForTarget(childRels[0])
- 'http://127.0.0.1/loops/views/m1/m11/m112/.target39'
+ 'http://127.0.0.1/loops/views/m1/m11/m112/.target37'
Actions
-------
diff --git a/browser/concept_macros.pt b/browser/concept_macros.pt
index 396f7e6..7dfa370 100644
--- a/browser/concept_macros.pt
+++ b/browser/concept_macros.pt
@@ -121,9 +121,10 @@
tal:define="resources python: list(item.resources())"
tal:condition="resources">
Resources
-
+
-
+
Title
Type
-
+
+
+
+
+
diff --git a/configure.zcml b/configure.zcml
index 5e04180..3c7541f 100644
--- a/configure.zcml
+++ b/configure.zcml
@@ -436,6 +436,7 @@
+
diff --git a/data/loops_std.dmp b/data/loops_std.dmp
index 5477d9c..2cd4bc2 100644
--- a/data/loops_std.dmp
+++ b/data/loops_std.dmp
@@ -9,7 +9,7 @@ type(u'note', u'Note', options=u'', typeInterface='loops.interfaces.INote', view
type(u'person', u'Person', options=u'', typeInterface='loops.knowledge.interfaces.IPerson', viewName=u'')
type(u'predicate', u'Predicate', options=u'', typeInterface=u'', viewName=u'')
type(u'process', u'Prozess', options=u'', typeInterface=u'', viewName=u'')
-type(u'query', u'Query', options=u'', typeInterface='loops.query.IQueryConcept', viewName=u'')
+type(u'query', u'Query', options=u'', typeInterface='loops.expert.concept.IQueryConcept', viewName=u'')
type(u'textdocument', u'Text', options=u'', typeInterface='loops.interfaces.ITextDocument', viewName=u'')
type(u'topic', u'Topic', options=u'', typeInterface='loops.knowledge.interfaces.ITopic', viewName=u'')
type(u'type', u'Type', options=u'', typeInterface='loops.interfaces.ITypeConcept', viewName=u'')
diff --git a/expert/README.txt b/expert/README.txt
index 9c7eb7f..257f29b 100644
--- a/expert/README.txt
+++ b/expert/README.txt
@@ -27,7 +27,7 @@ configuration):
>>> concepts, resources, views = t.setup()
>>> len(concepts) + len(resources)
- 32
+ 33
>>> loopsRoot = site['loops']
@@ -47,19 +47,19 @@ Type- and text-based queries
>>> from loops.expert import query
>>> qu = query.Title('ty*')
>>> list(qu.apply())
- [0, 1, 45]
+ [0, 1, 47]
>>> qu = query.Type('loops:*')
>>> len(list(qu.apply()))
- 32
+ 33
>>> qu = query.Type('loops:concept:predicate')
>>> len(list(qu.apply()))
- 6
+ 7
>>> qu = query.Type('loops:concept:predicate') & query.Title('t*')
>>> list(qu.apply())
- [1]
+ [1, 43]
State-based queries
-------------------
@@ -99,7 +99,7 @@ they have at least one concept assigned).
>>> qu = query.State('classification_quality', 'classified')
>>> list(qu.apply())
- [23, 25, 27]
+ [21, 23, 25]
Using the stateful adapter for a resource we now manually execute the
``verify`` transition.
@@ -113,16 +113,16 @@ Now only two resources are still in the ``qualified`` state, the changed
one being in the ``verified`` state.
>>> list(qu.apply())
- [25, 27]
+ [23, 25]
>>> qu = query.State('classification_quality', 'verified')
>>> list(qu.apply())
- [23]
+ [21]
We may also provide a sequence of states for querying.
>>> qu = query.State('classification_quality', ('classified', 'verified',))
>>> list(qu.apply())
- [23, 25, 27]
+ [21, 23, 25]
Relationship-based queries
--------------------------
@@ -135,7 +135,7 @@ syntax (that in turn is based on hurry.query).
>>> cust1 = concepts['cust1']
>>> qu = query.Resources(cust1)
>>> list(qu.apply())
- [23, 27]
+ [21, 25]
Getting objects
---------------
@@ -208,6 +208,15 @@ A query instance consists of
>>> #qi.apply()
+Query Concepts and Query Views
+==============================
+
+ >>> from loops.expert.concept import QueryConcept
+ >>> component.provideAdapter(QueryConcept)
+
+ >>> from loops.expert.browser.base import BaseQueryView
+
+
Fin de partie
=============
diff --git a/expert/browser/base.py b/expert/browser/base.py
index afb4e28..3f119c9 100644
--- a/expert/browser/base.py
+++ b/expert/browser/base.py
@@ -26,8 +26,12 @@ $Id$
from zope import interface, component
from zope.app.pagetemplate import ViewPageTemplateFile
from zope.cachedescriptors.property import Lazy
+from zope.traversing.api import getName, getParent
-from loops.browser.common import BaseView
+from cybertools.browser.form import FormController
+from loops.browser.common import BaseView, concept_macros
+from loops.browser.concept import ConceptView
+from loops.browser.resource import ResourceView, ResourceRelationView
from loops.common import adapted
from loops import util
from loops.util import _
@@ -36,11 +40,65 @@ from loops.util import _
queryTemplate = ViewPageTemplateFile('query.pt')
+#class BaseQueryView(ConceptView):
class BaseQueryView(BaseView):
template = queryTemplate
+ childViewFactory = ResourceRelationView
+ showCheckboxes = True
+ form_action = 'execute_query_action'
@Lazy
def macro(self):
- return template.macros['query']
+ return self.template.macros['query']
+ @property
+ def infos(self):
+ return concept_macros.macros
+
+ @property
+ def listings(self):
+ return concept_macros.macros
+
+ @property
+ def listings(self):
+ return concept_macros.macros
+
+ @Lazy
+ def targetPredicate(self):
+ return self.conceptManager['querytarget']
+
+ @Lazy
+ def defaultPredicate(self):
+ return self.conceptManager.getDefaultPredicate()
+
+ @Lazy
+ def targets(self):
+ return self.context.getChildren([self.targetPredicate])
+
+ def queryInfo(self):
+ targetNames = ', '.join(["'%s'" % t.title for t in self.targets])
+ return _(u'Selection using: $targets',
+ mapping=dict(targets=targetNames))
+
+ def results(self):
+ for t in self.targets:
+ for r in t.getResourceRelations([self.defaultPredicate]):
+ yield self.childViewFactory(r, self.request, contextIsSecond=True)
+
+
+class ActionExecutor(FormController):
+
+ def update(self):
+ form = self.request.form
+ actions = [k for k in form.keys() if k.startswith('action.')]
+ if actions:
+ uids = form.get('selection', [])
+ action = actions[0]
+ if action == 'action.delete':
+ print '*** delete', uids
+ for uid in uids:
+ obj = util.getObjectForUid(uid)
+ parent = getParent(obj)
+ del parent[getName(obj)]
+ return True
diff --git a/expert/browser/configure.zcml b/expert/browser/configure.zcml
index a74e4b7..97150b6 100644
--- a/expert/browser/configure.zcml
+++ b/expert/browser/configure.zcml
@@ -6,7 +6,7 @@
i18n_domain="loops">
-
-
- Query
-
+
+
diff --git a/expert/concept.py b/expert/concept.py
new file mode 100644
index 0000000..5b905dd
--- /dev/null
+++ b/expert/concept.py
@@ -0,0 +1,211 @@
+#
+# Copyright (c) 2008 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+Query concepts management stuff.
+
+$Id$
+"""
+
+from BTrees.IOBTree import IOBTree
+from BTrees.IFBTree import weightedIntersection, weightedUnion, IFBucket
+from zope import schema, component
+from zope.interface import Interface, Attribute, implements
+from zope.app.catalog.interfaces import ICatalog
+from zope.app.intid.interfaces import IIntIds
+from zope.cachedescriptors.property import Lazy
+
+from cybertools.typology.interfaces import IType
+from loops.common import AdapterBase
+from loops.interfaces import IConcept, IConceptSchema
+from loops.security.common import canListObject
+from loops.type import TypeInterfaceSourceList
+from loops.versioning.util import getVersion
+from loops import util
+from loops.util import _
+
+
+class ScoredSet(set):
+
+ def __init__(self, data=set(), scores={}):
+ super(ScoredSet, self).__init__(data)
+ self.scores = scores
+
+ def getScore(self, obj):
+ return self.scores.get(obj, -1)
+
+
+class BaseQuery(object):
+
+ # TODO: replace with proper use of loops.expert.query
+
+ def __init__(self, context):
+ self.context = context
+
+ @Lazy
+ def catalog(self):
+ return component.getUtility(ICatalog)
+
+ @Lazy
+ def loopsRoot(self):
+ return self.context.context.getLoopsRoot()
+
+ def queryConcepts(self, title=None, type=None, **kw):
+ if type.endswith('*'):
+ start = type[:-1]
+ end = start + '\x7f'
+ else:
+ start = end = type
+ cat = self.catalog
+ if title:
+ result = cat.searchResults(loops_type=(start, end), loops_title=title)
+ else:
+ result = cat.searchResults(loops_type=(start, end))
+ result = set(r for r in result if r.getLoopsRoot() == self.loopsRoot
+ and canListObject(r))
+ 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, **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, **kw))
+ else:
+ queue = [util.getObjectForUid(uid)]
+ concepts = []
+ while queue:
+ c = queue.pop(0)
+ concepts.append(c)
+ for child in c.getChildren():
+ # TODO: check for tree level, use relevance factors, ...
+ if child not in queue and child not in concepts:
+ queue.append(child)
+ for c in concepts:
+ result.add(c)
+ result.update(getVersion(r) for r in c.getResources())
+ return result
+
+
+class FullQuery(BaseQuery):
+
+ def query(self, text=None, type=None, useTitle=True, useFull=False,
+ conceptTitle=None, conceptUid=None, conceptType=None, **kw):
+ result = set()
+ scores = {}
+ intids = component.getUtility(IIntIds)
+ rc = self.queryConceptsWithChildren(title=conceptTitle, uid=conceptUid,
+ type=conceptType)
+ if not rc and not text and '*' in type: # there should be some sort of selection...
+ return ScoredSet(result, scores)
+ if text or type != 'loops:*': # TODO: this may be highly inefficient!
+ cat = self.catalog
+ if type.endswith('*'):
+ start = type[:-1]
+ end = start + '\x7f'
+ else:
+ start = end = type
+ criteria = {'loops_type': (start, end),}
+ if useFull and text:
+ criteria['loops_text'] = text
+ r1 = cat.apply(criteria) #r1 = set(cat.searchResults(**criteria))
+ else:
+ r1 = IFBucket() #r1 = set()
+ if useTitle and text:
+ if 'loops_text' in criteria:
+ del criteria['loops_text']
+ criteria['loops_title'] = text
+ r2 = cat.apply(criteria) #r2 = set(cat.searchResults(**criteria))
+ else:
+ r2 = IFBucket() #r2 = set()
+ if not r1 and not r2:
+ r1 = cat.apply(criteria) # search only for type
+ x, uids = weightedUnion(r1, r2) #result = r1.union(r2)
+ for r, score in uids.items():
+ obj = intids.getObject(r)
+ result.add(obj)
+ scores[obj] = score
+ if rc is not None:
+ if result:
+ result = result.intersection(rc)
+ else:
+ result = rc
+ result = set(r for r in result
+ if r.getLoopsRoot() == self.loopsRoot
+ and canListObject(r)
+ and getVersion(r) == r)
+ return ScoredSet(result, scores)
+
+
+class ConceptQuery(BaseQuery):
+ """ Find concepts of type `type` whose title starts with `title`.
+ """
+
+ def query(self, title=None, type=None, **kw):
+ if title and not title.endswith('*'):
+ title += '*'
+ return self.queryConcepts(title=title, type=type, **kw)
+
+
+# QueryConcept: concept objects that allow querying the database.
+
+class IQueryConcept(IConceptSchema):
+ """ The schema for the query type.
+ """
+
+ viewName = schema.TextLine(
+ title=_(u'Adapter/View name'),
+ description=_(u'The name of the (multi-) adapter (typically a view) '
+ 'to be used for the query and for presenting '
+ 'the results'),
+ default=u'',
+ required=True)
+
+ options = schema.List(
+ title=_(u'Options'),
+ description=_(u'Additional settings.'),
+ value_type=schema.TextLine(),
+ default=[],
+ required=False)
+
+
+class QueryConcept(AdapterBase):
+
+ implements(IQueryConcept)
+
+ _contextAttributes = AdapterBase._contextAttributes + ['viewName']
+ _adapterAttributes = AdapterBase._adapterAttributes + ('options',)
+
+ def getOptions(self):
+ return getattr(self.context, '_options', [])
+ def setOptions(self, value):
+ self.context._options = value
+ options = property(getOptions, setOptions)
+
+
+TypeInterfaceSourceList.typeInterfaces += (IQueryConcept,)
+
diff --git a/expert/configure.zcml b/expert/configure.zcml
new file mode 100644
index 0000000..dd24ee9
--- /dev/null
+++ b/expert/configure.zcml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/expert/setup.py b/expert/setup.py
new file mode 100644
index 0000000..b5989a0
--- /dev/null
+++ b/expert/setup.py
@@ -0,0 +1,42 @@
+#
+# Copyright (c) 2008 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
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+Automatic setup of a loops site for the expert package.
+
+$Id$
+"""
+
+from loops.concept import Concept
+from loops.expert.concept import IQueryConcept
+from loops.setup import SetupManager as BaseSetupManager
+
+
+class SetupManager(BaseSetupManager):
+
+ def setup(self):
+ concepts = self.context.getConceptManager()
+ type = concepts.getTypeConcept()
+ predicate = concepts['predicate']
+ # type concepts:
+ queryConcept = self.addAndConfigureObject(concepts, Concept, 'query',
+ title=u'Query', conceptType=type,
+ typeInterface=IQueryConcept)
+ # predicates:
+ queryTarget = self.addObject(concepts, Concept, 'querytarget',
+ title=u'is Query Target', conceptType=predicate)
diff --git a/expert/testsetup.py b/expert/testsetup.py
index 1f8eaea..90b5075 100644
--- a/expert/testsetup.py
+++ b/expert/testsetup.py
@@ -10,6 +10,7 @@ from zope.app.catalog.interfaces import ICatalog
from cybertools.typology.interfaces import IType
from loops import util
from loops.concept import Concept
+from loops.expert.setup import SetupManager as ExpertSetupManager
from loops.resource import Resource
from loops.knowledge.interfaces import IPerson
from loops.knowledge.knowledge import Person
@@ -32,6 +33,7 @@ class TestSite(BaseTestSite):
component.provideAdapter(Person, provides=IPerson)
component.provideAdapter(KnowledgeSetupManager, name='knowledge')
+ component.provideAdapter(ExpertSetupManager, name='expert')
setup = SetupManager(loopsRoot)
concepts, resources, views = setup.setup()
diff --git a/external/README.txt b/external/README.txt
index 6577a3a..78bc117 100644
--- a/external/README.txt
+++ b/external/README.txt
@@ -12,12 +12,12 @@ Let's set up a loops site with basic and example concepts and resources.
>>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
>>> site = placefulSetUp(True)
- >>> from loops.tests.setup import TestSite
+ >>> from loops.expert.testsetup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
>>> loopsRoot = site['loops']
>>> len(concepts), len(resources), len(views)
- (11, 3, 0)
+ (30, 3, 0)
Importing loops Objects
@@ -44,7 +44,7 @@ Creating the corresponding objects
>>> loader = Loader(loopsRoot)
>>> loader.load(elements)
>>> len(concepts), len(resources), len(views)
- (12, 3, 0)
+ (31, 3, 0)
>>> from loops.common import adapted
>>> adMyquery = adapted(concepts['myquery'])
@@ -118,7 +118,7 @@ Extracting elements
>>> extractor = Extractor(loopsRoot, os.path.join(dataDirectory, 'export'))
>>> elements = list(extractor.extract())
>>> len(elements)
- 20
+ 52
Writing object information to the external storage
--------------------------------------------------
@@ -130,13 +130,13 @@ Writing object information to the external storage
>>> writer = PyWriter()
>>> writer.write(elements, output)
>>> print output.getvalue()
- type(u'customer', u'Customer', options=u'', typeInterface=u'', viewName=u'')...
- type(u'query', u'Query', options=u'', typeInterface='loops.query.IQueryConcept',
+ type(u'country', u'Country', options=u'', typeInterface=u'', viewName=u'')...
+ type(u'query', u'Query', options=u'', typeInterface='loops.expert.concept.IQueryConcept',
viewName=u'')...
concept(u'myquery', u'My Query', u'query', options=u'option1\noption2',
viewName=u'mystuff.html')...
child(u'projects', u'customer', u'standard')...
- resource(u'doc04.txt', u'Document 4', u'textdocument', contentType='text/restructured')
+ resource(u'doc04.txt', u'Document 4', u'textdocument', contentType='text/restructured')...
resourceRelation(u'myquery', u'doc04.txt', u'standard')
node('home', u'Home', '', u'menu', body=u'Welcome')
node('myquery', u'My Query', 'home', u'page', target=u'concepts/myquery')...
@@ -172,7 +172,7 @@ corresponding extractor adapter.
>>> PyWriter().write(extractor.extract(), output)
>>> print output.getvalue()
- type(u'customer', u'Customer', options=u'', typeInterface=u'', viewName=u'')...
+ type(u'country', u'Country', options=u'', typeInterface=u'', viewName=u'')...
concept(u'myquery', u'My Query', u'query', options=u'option1\noption2',
viewName=u'mystuff.html')[
annotations(creators=(u'john',))]...
diff --git a/helpers.txt b/helpers.txt
index a238383..27edd4d 100755
--- a/helpers.txt
+++ b/helpers.txt
@@ -226,7 +226,7 @@ get a type manager from all loops objects, always with the same context:
>>> types = typeManager.types
>>> typeTokens = sorted(t.token for t in types)
>>> len(typeTokens)
- 9
+ 8
>>> typeManager.getType('.loops/concepts/topic') == cc1_type
True
@@ -237,7 +237,7 @@ condition:
>>> types = typeManager.listTypes(include=('concept',))
>>> typeTokens = sorted(t.token for t in types)
>>> len(typeTokens)
- 6
+ 5
>>> types = typeManager.listTypes(exclude=('concept',))
>>> typeTokens = sorted(t.token for t in types)
>>> len(typeTokens)
@@ -313,9 +313,11 @@ We first have to set up the query type, i.e. a type concept associated
with the IQueryConcept interface. The query type concept itself has already
been provided by the setup, but we have to register a corresponding adapter.
- >>> from loops.query import QueryConcept
+ >>> from loops.expert.concept import IQueryConcept, QueryConcept
>>> component.provideAdapter(QueryConcept)
- >>> query = concepts['query']
+ >>> from loops.setup import addAndConfigureObject
+ >>> query = addAndConfigureObject(concepts, Concept,
+ ... 'query', conceptType=typeObject, typeInterface=IQueryConcept)
Next we need a concept of this type:
@@ -423,7 +425,6 @@ Folders
structures of their documents in the filesystem to the loops concept
map.
- >>> from loops.setup import addAndConfigureObject
>>> tFolder = addAndConfigureObject(concepts, Concept, 'folder',
... title=u'Folder', conceptType=typeObject)
diff --git a/integrator/README.txt b/integrator/README.txt
index 9fef34e..861ba87 100644
--- a/integrator/README.txt
+++ b/integrator/README.txt
@@ -27,7 +27,7 @@ configuration):
>>> concepts, resources, views = t.setup()
>>> len(concepts) + len(resources)
- 18
+ 17
External Collections
diff --git a/integrator/content/README.txt b/integrator/content/README.txt
index f874988..fe97840 100644
--- a/integrator/content/README.txt
+++ b/integrator/content/README.txt
@@ -27,7 +27,7 @@ configuration):
>>> concepts, resources, views = t.setup()
>>> len(concepts) + len(resources)
- 14
+ 13
Accessing a Directory in the Filesystem
diff --git a/interfaces.py b/interfaces.py
index 8fe2811..a8d4966 100644
--- a/interfaces.py
+++ b/interfaces.py
@@ -745,6 +745,14 @@ class IExternalFile(IFile):
missing_value='',
required=False)
+ externalAddress = schema.BytesLine(
+ title=_(u'External Address'),
+ description=_(u'The full address for accessing the object '
+ 'on the external storage, e.g. a filename or path.'),
+ default='',
+ missing_value='',
+ required=False)
+
class IImage(IResourceAdapter):
""" A media asset that may be embedded in a (web) page as an image.
diff --git a/organize/README.txt b/organize/README.txt
index a0cd7fc..4c86ee4 100644
--- a/organize/README.txt
+++ b/organize/README.txt
@@ -18,7 +18,7 @@ ZCML setup):
>>> from loops.organize.setup import SetupManager
>>> component.provideAdapter(SetupManager, name='organize')
- >>> from loops.tests.setup import TestSite
+ >>> from loops.expert.testsetup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
@@ -237,7 +237,7 @@ Automatic security settings on persons
>>> from zope.traversing.api import getName
>>> list(sorted(getName(c) for c in concepts['person'].getChildren()))
- [u'john', u'martha', u'person.newuser']
+ [u'jim', u'john', u'martha', u'person.newuser']
Person objects that have a user assigned to them receive this user
(principal) as their owner.
diff --git a/organize/personal/README.txt b/organize/personal/README.txt
index e4e2dfe..b50b551 100644
--- a/organize/personal/README.txt
+++ b/organize/personal/README.txt
@@ -72,10 +72,10 @@ So we are now ready to query the favorites.
>>> favs = list(favorites.query(userName=johnCId))
>>> favs
- []
+ []
>>> list(favAdapted.list(johnC))
- ['29']
+ ['27']
>>> util.getObjectForUid(favs[0].taskId) is resources['d001.txt']
True
diff --git a/organize/stateful/README.txt b/organize/stateful/README.txt
index 7d737f2..45ec8df 100644
--- a/organize/stateful/README.txt
+++ b/organize/stateful/README.txt
@@ -14,7 +14,7 @@ First we set up a loops site with basic and example concepts and resources.
>>> from loops.organize.setup import SetupManager
>>> component.provideAdapter(SetupManager, name='organize')
- >>> from loops.tests.setup import TestSite
+ >>> from loops.expert.testsetup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
>>> loopsRoot = site['loops']
@@ -40,7 +40,11 @@ making an object statful we'll use an adapter.
We may now take a document and adapt it to IStateful so that we may
check the document's state and perform transitions to other states.
- >>> doc01 = resources['d001.txt']
+ >>> from loops.resource import Resource
+ >>> from loops.setup import addAndConfigureObject
+ >>> tText = concepts['textdocument']
+ >>> doc01 = resources['doc01.txt'] = addAndConfigureObject(resources,
+ ... Resource, 'doc01.txt', conceptType=tText)
>>> statefulDoc01 = component.getAdapter(doc01, IStateful,
... name='simple_publishing')
@@ -88,7 +92,6 @@ later.
>>> tCustomer = concepts['customer']
>>> from loops.concept import Concept
- >>> from loops.setup import addAndConfigureObject
>>> c01 = addAndConfigureObject(concepts, Concept, 'c01', conceptType=tCustomer,
... title='im publishing')
>>> c02 = addAndConfigureObject(concepts, Concept, 'c02', conceptType=tCustomer,
diff --git a/resource.py b/resource.py
index 778679a..e678496 100644
--- a/resource.py
+++ b/resource.py
@@ -357,7 +357,6 @@ class ExternalFileAdapter(FileAdapter):
def getExternalAddress(self):
return getattr(self.context, '_externalAddress', self.context.__name__)
def setExternalAddress(self, addr):
- # TODO (?) - use intId as default?
self.context._externalAddress = addr
externalAddress = property(getExternalAddress, setExternalAddress)
diff --git a/rest/README.txt b/rest/README.txt
index 83b9e91..2a65ba3 100755
--- a/rest/README.txt
+++ b/rest/README.txt
@@ -35,7 +35,7 @@ ZCML setup):
Let's look what setup has provided us with:
>>> sorted(concepts)
- [u'domain', u'file', u'hasType', u'note', u'predicate', u'query',
+ [u'domain', u'file', u'hasType', u'note', u'predicate',
u'standard', u'textdocument', u'type']
diff --git a/schema.py b/schema.py
index 467a262..df9e8ee 100644
--- a/schema.py
+++ b/schema.py
@@ -47,8 +47,10 @@ class FileSchemaFactory(SchemaFactory):
def __call__(self, interface, **kw):
schema = super(FileSchemaFactory, self).__call__(interface, **kw)
- if 'request' in kw and kw['request'].principal.id != 'rootadmin':
- schema.fields.remove('contentType')
+ if 'request' in kw:
+ principal = kw['request'].principal
+ if not principal or principal.id != 'rootadmin':
+ schema.fields.remove('contentType')
return schema
diff --git a/search/README.txt b/search/README.txt
index cb9dad4..f350702 100755
--- a/search/README.txt
+++ b/search/README.txt
@@ -21,13 +21,13 @@ ZCML setup):
>>> from loops.type import ConceptType, TypeConcept
>>> from loops.interfaces import ITypeConcept
>>> from loops.base import Loops
- >>> from loops.tests.setup import TestSite
+ >>> from loops.expert.testsetup import TestSite
>>> t = TestSite(site)
>>> concepts, resources, views = t.setup()
>>> loopsRoot = site['loops']
>>> query = concepts['query']
- >>> topic = concepts['topic'] = Concept(u'Topic')
+ >>> topic = concepts['topic']
In addition we create a concept that holds the search page and a node
(page) that links to this concept:
@@ -66,13 +66,13 @@ zcml in real life:
>>> t = searchView.typesForSearch()
>>> len(t)
- 9
+ 14
>>> t.getTermByToken('loops:resource:*').title
'Any Resource'
>>> t = searchView.conceptTypesForSearch()
>>> len(t)
- 4
+ 9
>>> t.getTermByToken('loops:concept:*').title
'Any Concept'
@@ -91,7 +91,7 @@ a controller attribute for the search view.
>>> searchView.submitReplacing('1.results', '1.search.form', pageView)
'submitReplacing("1.results", "1.search.form",
- "http://127.0.0.1/loops/views/page/.target29/@@searchresults.html");...'
+ "http://127.0.0.1/loops/views/page/.target80/@@searchresults.html");...'
Basic (text/title) search
-------------------------
@@ -177,7 +177,7 @@ of the concepts' titles:
>>> request = TestRequest(form=form)
>>> view = Search(page, request)
>>> view.listConcepts()
- u"{identifier: 'id', items: [{label: 'Zope (Topic)', name: 'Zope', id: '34'}, {label: 'Zope 2 (Topic)', name: 'Zope 2', id: '37'}, {label: 'Zope 3 (Topic)', name: 'Zope 3', id: '39'}]}"
+ u"{identifier: 'id', items: [{label: 'Zope (Topic)', name: 'Zope', id: '85'}, {label: 'Zope 2 (Topic)', name: 'Zope 2', id: '87'}, {label: 'Zope 3 (Topic)', name: 'Zope 3', id: '89'}]}"
Preset Concept Types on Search Forms
------------------------------------
@@ -193,8 +193,8 @@ Let's start with a new type, the customer type.
>>> custType.options
[]
- >>> cust1 = concepts['cust1'] = Concept(u'Zope Corporation')
- >>> cust2 = concepts['cust2'] = Concept(u'cyberconcepts')
+ >>> cust1 = concepts['cust1']
+ >>> cust2 = concepts['cust2']
>>> for c in (cust1, cust2):
... c.conceptType = customer
... catalog.index_doc(int(util.getUidForObject(c)), c)
@@ -219,16 +219,17 @@ and thus include the customer type in the preset search types.
>>> searchView.conceptsForType('loops:concept:customer')
[{'token': 'none', 'title': u'not selected'},
- {'token': '47', 'title': u'Zope Corporation'},
- {'token': '49', 'title': u'cyberconcepts'}]
+ {'token': '58', 'title': u'Customer 1'},
+ {'token': '60', 'title': u'Customer 2'},
+ {'token': '62', 'title': u'Customer 3'}]
Let's use this new search option for querying:
- >>> form = {'search.4.text_selected': u'47'}
+ >>> form = {'search.4.text_selected': u'58'}
>>> resultsView = SearchResults(page, TestRequest(form=form))
>>> results = list(resultsView.results)
>>> results[0].title
- u'Zope Corporation'
+ u'Customer 1'
Automatic Filtering
diff --git a/search/browser.py b/search/browser.py
index 1f523fb..3579625 100644
--- a/search/browser.py
+++ b/search/browser.py
@@ -36,7 +36,8 @@ from cybertools.typology.interfaces import ITypeManager
from loops.browser.common import BaseView
from loops.browser.node import NodeView
from loops.common import adapted
-from loops.query import ConceptQuery, FullQuery
+#from loops.query import ConceptQuery, FullQuery
+from loops.expert.concept import ConceptQuery, FullQuery
from loops import util
from loops.util import _
diff --git a/setup.py b/setup.py
index dfccbda..fd043e9 100644
--- a/setup.py
+++ b/setup.py
@@ -35,7 +35,7 @@ from loops.common import adapted
from loops.concept import ConceptManager, Concept
from loops.interfaces import ILoops, ITypeConcept
from loops.interfaces import IFile, IImage, ITextDocument, INote
-from loops.query import IQueryConcept
+#from loops.query import IQueryConcept
from loops.record import RecordManager
from loops.resource import ResourceManager, Resource
from loops.view import ViewManager, Node
@@ -82,16 +82,16 @@ class SetupManager(object):
predicate = self.addObject(conceptManager, Concept, 'predicate', title=u'Predicate')
standard = self.addObject(conceptManager, Concept, 'standard', title=u'subobject')
domain = self.addObject(conceptManager, Concept, 'domain', title=u'Domain')
- query = self.addObject(conceptManager, Concept, 'query', title=u'Query')
+ #query = self.addObject(conceptManager, Concept, 'query', title=u'Query')
file = self.addObject(conceptManager, Concept, 'file', title=u'File')
textdocument = self.addObject(conceptManager, Concept,
'textdocument', title=u'Text')
note = self.addObject(conceptManager, Concept, 'note', title=u'Note')
- for c in (typeConcept, domain, query, note, file, textdocument, predicate):
+ for c in (typeConcept, domain, note, file, textdocument, predicate):
c.conceptType = typeConcept
notify(ObjectModifiedEvent(c))
ITypeConcept(typeConcept).typeInterface = ITypeConcept
- ITypeConcept(query).typeInterface = IQueryConcept
+ #ITypeConcept(query).typeInterface = IQueryConcept
ITypeConcept(file).typeInterface = IFile
ITypeConcept(textdocument).typeInterface = ITextDocument
ITypeConcept(note).typeInterface = INote
diff --git a/system/README.txt b/system/README.txt
index dd3acd7..3bc1aae 100644
--- a/system/README.txt
+++ b/system/README.txt
@@ -39,7 +39,7 @@ In addition to the application site we need a loops system management site.
>>> systemRoot = site['loops.system']
>>> sorted(sysConcepts)
- [u'domain', u'file', u'hasType', u'job', u'note', u'predicate', u'query',
+ [u'domain', u'file', u'hasType', u'job', u'note', u'predicate',
u'standard', u'textdocument', u'type']
diff --git a/tests/setup.py b/tests/setup.py
index d119d48..d732629 100644
--- a/tests/setup.py
+++ b/tests/setup.py
@@ -33,6 +33,7 @@ from cybertools.composer.schema.factory import SchemaFactory
from cybertools.composer.schema.field import FieldInstance, NumberFieldInstance
from cybertools.composer.schema.field import DateFieldInstance, BooleanFieldInstance
from cybertools.composer.schema.field import EmailFieldInstance, ListFieldInstance
+from cybertools.composer.schema.field import FileUploadFieldInstance
from cybertools.composer.schema.instance import Instance, Editor
from cybertools.relation.tests import IntIdsStub
from cybertools.relation.registry import RelationRegistry, IIndexableRelation
@@ -56,8 +57,7 @@ from loops.layout.base import LayoutNode
from loops.organize.memberinfo import MemberInfoProvider
from loops.organize.stateful.base import StatefulResourceIndexInfo, handleTransition
from loops.predicate import Predicate #, MappingAttributeRelation
-from loops.query import QueryConcept
-from loops.query import QueryConcept
+from loops.expert.concept import QueryConcept
from loops.resource import Resource, FileAdapter, TextDocumentAdapter
from loops.resource import Document, MediaAsset
from loops.resource import IndexAttributes as ResourceIndexAttributes
@@ -139,6 +139,7 @@ class TestSite(object):
component.provideAdapter(EmailFieldInstance, name='email')
component.provideAdapter(BooleanFieldInstance, name='boolean')
component.provideAdapter(ListFieldInstance, name='list')
+ component.provideAdapter(FileUploadFieldInstance, name='fileupload')
component.provideAdapter(SchemaFactory)
component.provideAdapter(ResourceSchemaFactory)
component.provideAdapter(FileSchemaFactory)
diff --git a/versioning/README.txt b/versioning/README.txt
index f934e26..2fd684d 100644
--- a/versioning/README.txt
+++ b/versioning/README.txt
@@ -29,7 +29,7 @@ configuration):
>>> #sorted(concepts)
>>> #sorted(resources)
>>> len(concepts) + len(resources)
- 24
+ 23
Version Information
diff --git a/xmlrpc/README.txt b/xmlrpc/README.txt
index 5f7b15d..e513779 100755
--- a/xmlrpc/README.txt
+++ b/xmlrpc/README.txt
@@ -34,7 +34,7 @@ ZCML setup):
Let's look what setup has provided us with:
>>> len(concepts)
- 19
+ 18
Now let's add a few more concepts:
@@ -58,7 +58,7 @@ There are a few standard objects we can retrieve directly:
>>> defaultPred = xrf.getDefaultPredicate()
>>> defaultPred['id'], defaultPred['name']
- ('16', u'standard')
+ ('14', u'standard')
>>> typePred = xrf.getTypePredicate()
>>> typePred['id'], typePred['name']
('1', u'hasType')
@@ -71,7 +71,7 @@ note that the 'hasType' predicate is not shown as it should not be
applied in an explicit assignment.
>>> sorted(t['name'] for t in xrf.getConceptTypes())
- [u'customer', u'domain', u'file', u'note', u'person', u'predicate', u'query',
+ [u'customer', u'domain', u'file', u'note', u'person', u'predicate',
u'task', u'textdocument', u'topic', u'type']
>>> sorted(t['name'] for t in xrf.getPredicates())
[u'depends', u'knows', u'ownedby', u'provides', u'requires', u'standard']
@@ -80,10 +80,10 @@ We can also retrieve a certain object by its id or its name:
>>> obj2 = xrf.getObjectById('5')
>>> obj2['id'], obj2['name']
- ('5', u'query')
+ ('5', u'note')
>>> textdoc = xrf.getObjectByName(u'textdocument')
>>> textdoc['id'], textdoc['name']
- ('11', u'textdocument')
+ ('9', u'textdocument')
All methods that retrieve one object also returns its children and parents:
@@ -94,7 +94,7 @@ All methods that retrieve one object also returns its children and parents:
u'hasType'
>>> sorted(c['name'] for c in ch[0]['objects'])
[u'customer', u'domain', u'file', u'note', u'person', u'predicate',
- u'query', u'task', u'textdocument', u'topic', u'type']
+ u'task', u'textdocument', u'topic', u'type']
>>> pa = defaultPred['parents']
>>> len(pa)
@@ -113,7 +113,7 @@ We can also retrieve children and parents explicitely:
u'hasType'
>>> sorted(c['name'] for c in ch[0]['objects'])
[u'customer', u'domain', u'file', u'note', u'person', u'predicate',
- u'query', u'task', u'textdocument', u'topic', u'type']
+ u'task', u'textdocument', u'topic', u'type']
>>> pa = xrf.getParents('7')
>>> len(pa)
@@ -172,14 +172,14 @@ Updating the concept map
>>> topicId = xrf.getObjectByName('topic')['id']
>>> xrf.createConcept(topicId, u'zope2', u'Zope 2')
- {'description': u'', 'title': u'Zope 2', 'type': '24', 'id': '56',
+ {'description': u'', 'title': u'Zope 2', 'type': '22', 'id': '54',
'name': u'zope2'}
The name of the concept is checked by a name chooser; if the corresponding
parameter is empty, the name will be generated from the title.
>>> xrf.createConcept(topicId, u'', u'Python')
- {'description': u'', 'title': u'Python', 'type': '24', 'id': '58',
+ {'description': u'', 'title': u'Python', 'type': '22', 'id': '56',
'name': u'python'}
If we try to deassign a ``hasType`` relation nothing will happen; a