diff --git a/configure.zcml b/configure.zcml
index 34517f0..08d315a 100644
--- a/configure.zcml
+++ b/configure.zcml
@@ -371,5 +371,6 @@
+
diff --git a/search/README.txt b/search/README.txt
index a29ee86..9eaa82c 100755
--- a/search/README.txt
+++ b/search/README.txt
@@ -103,7 +103,7 @@ a controller attribute for the search view.
>>> searchView.submitReplacing('1.results', '1.search.form', pageView)
'return submitReplacing("1.results", "1.search.form",
- "http://127.0.0.1/loops/views/page/.target19/@@searchresults.html")'
+ "http://127.0.0.1/loops/views/page/.target9/@@searchresults.html")'
Basic (text/title) search
-------------------------
@@ -198,7 +198,7 @@ of the concepts' titles:
>>> request = TestRequest(form=form)
>>> view = Search(page, request)
>>> view.listConcepts()
- "[['Zope (Topic)', '23']]"
+ "[['Zope (Topic)', '11']]"
TODO - more to come...
diff --git a/xmlrpc/README.txt b/xmlrpc/README.txt
new file mode 100755
index 0000000..d60bc0e
--- /dev/null
+++ b/xmlrpc/README.txt
@@ -0,0 +1,102 @@
+===============================================================
+loops.xmlrpc
+===============================================================
+
+ ($Id$)
+
+Let's do some basic set up
+
+ >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown
+ >>> site = placefulSetUp(True)
+
+ >>> from zope import component, interface
+ >>> from zope.publisher.browser import TestRequest
+
+and setup 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 cybertools.relation.registry import DummyRelationRegistry
+ >>> component.provideUtility(DummyRelationRegistry())
+ >>> from cybertools.relation.tests import IntIdsStub
+ >>> component.provideUtility(IntIdsStub())
+
+ >>> from loops.type import ConceptType, TypeConcept
+ >>> component.provideAdapter(ConceptType)
+ >>> component.provideAdapter(TypeConcept)
+
+ >>> from loops import Loops
+ >>> loopsRoot = site['loops'] = Loops()
+
+ >>> from loops.setup import SetupManager
+ >>> setup = SetupManager(loopsRoot)
+ >>> concepts, resources, views = setup.setup()
+
+Let's look what setup has provided us with:
+
+ >>> list(concepts)
+ [u'file', u'hasType', u'image', u'predicate', u'standard', u'textdocument', u'type']
+
+Navigation typically starts at a start object, which by default ist the
+top-level type concept:
+
+ >>> from loops.xmlrpc.common import LoopsMethods
+ >>> xrf = LoopsMethods(loopsRoot, TestRequest())
+ >>> xrf.getStartObject()
+ {'title': u'Type', 'type': '0', 'id': '0', 'name': u'type'}
+
+If we provide a concept named "domain" this will be used as starting point:
+
+ >>> from loops.concept import Concept
+ >>> domain = concepts[u'domain'] = Concept(u'Domain')
+ >>> domain.conceptType = concepts.getTypeConcept()
+ >>> xrf.getStartObject()
+ {'title': u'Domain', 'type': '0', 'id': '7', 'name': u'domain'}
+
+There are a few standard objects we can retrieve directly:
+
+ >>> xrf.getDefaultPredicate()
+ {'title': u'subobject', 'type': '4', 'id': '6', 'name': u'standard'}
+ >>> xrf.getTypePredicate()
+ {'title': u'has Type', 'type': '4', 'id': '5', 'name': u'hasType'}
+ >>> xrf.getTypeConcept()
+ {'title': u'Type', 'type': '0', 'id': '0', 'name': u'type'}
+
+In addition we can get a list of all types and all predicates available:
+
+ >>> sorted(t['name'] for t in xrf.getConceptTypes())
+ [u'domain', u'file', u'image', u'predicate', u'textdocument', u'type']
+ >>> sorted(t['name'] for t in xrf.getPredicates())
+ [u'hasType', u'standard']
+
+We can also retrieve a certain object by its id or its name:
+
+ >>> xrf.getObjectById('2')
+ {'title': u'Image', 'type': '0', 'id': '2', 'name': u'image'}
+ >>> xrf.getObjectByName(u'textdocument')
+ {'title': u'Text Document', 'type': '0', 'id': '3', 'name': u'textdocument'}
+
+Now we are ready to deal with children and parents...
+
+ >>> ch = xrf.getChildren('0')
+ >>> len(ch)
+ 1
+ >>> ch[0]['name']
+ u'hasType'
+ >>> sorted(c['name'] for c in ch[0]['objects'])
+ [u'domain', u'file', u'image', u'predicate', u'textdocument', u'type']
+
+ >>> pa = xrf.getParents('5')
+ >>> len(pa)
+ 1
+ >>> pa[0]['name']
+ u'hasType'
+ >>> sorted(p['name'] for p in pa[0]['objects'])
+ u'predicate'
+
+
+Fin de partie
+=============
+
+ >>> placefulTearDown()
+
diff --git a/xmlrpc/__init__.py b/xmlrpc/__init__.py
new file mode 100644
index 0000000..4bc90fb
--- /dev/null
+++ b/xmlrpc/__init__.py
@@ -0,0 +1,4 @@
+"""
+$Id$
+"""
+
diff --git a/xmlrpc/common.py b/xmlrpc/common.py
new file mode 100644
index 0000000..56ec685
--- /dev/null
+++ b/xmlrpc/common.py
@@ -0,0 +1,108 @@
+#
+# Copyright (c) 2005 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
+#
+
+"""
+Tournament and Assessment views.
+
+$Id$
+"""
+
+from zope.interface import implements
+from zope.app.publisher.xmlrpc import XMLRPCView
+from zope.app.publisher.xmlrpc import MethodPublisher
+from zope.app.traversing.api import getName
+from zope.security.proxy import removeSecurityProxy
+from zope.cachedescriptors.property import Lazy
+
+from loops.util import getUidForObject, getObjectForUid
+
+class LoopsMethods(MethodPublisher):
+ """ XML-RPC methods for the loops root object.
+ """
+
+ def __init__(self, context, request):
+ self.context = removeSecurityProxy(context)
+ self.request = request
+
+ @Lazy
+ def concepts(self):
+ return self.context.getConceptManager()
+
+ def getStartObject(self):
+ so = self.concepts.get('domain', self.concepts.getTypeConcept())
+ return objectAsDict(so)
+
+ def getObjectById(self, id):
+ return objectAsDict(getObjectForUid(id))
+
+ def getObjectByName(self, name):
+ return objectAsDict(self.concepts[name])
+
+ def getDefaultPredicate(self):
+ return objectAsDict(self.concepts.getDefaultPredicate())
+
+ def getTypePredicate(self):
+ return objectAsDict(self.concepts.getTypePredicate())
+
+ def getTypeConcept(self):
+ return objectAsDict(self.concepts.getTypeConcept())
+
+ def getConceptTypes(self):
+ tc = self.concepts.getTypeConcept()
+ types = tc.getChildren((self.concepts.getTypePredicate(),))
+ return [objectAsDict(t) for t in types]
+
+ def getPredicates(self):
+ pt = self.concepts.getDefaultPredicate().conceptType
+ types = pt.getChildren((self.concepts.getTypePredicate(),))
+ return [objectAsDict(t) for t in types]
+
+ def getChildren(self, id, predicates=[], child=''):
+ obj = getObjectForUid(id)
+ preds = [getObjectForUid(p) for p in predicates]
+ child = child and getObjectForUid(child) or None
+ rels = obj.getChildRelations(preds, child)
+ return formatRelations(rels)
+
+ def getParents(self, id, predicates=[], parent=''):
+ obj = getObjectForUid(id)
+ preds = [getObjectForUid(p) for p in predicates]
+ parent = parent and getObjectForUid(parent) or None
+ rels = obj.getParentRelations(preds, parent)
+ return formatRelations(rels, useSecond=False)
+
+
+def objectAsDict(obj):
+ mapping = {'id': getUidForObject(obj), 'name': getName(obj), 'title': obj.title,
+ 'type': getUidForObject(obj.conceptType)}
+ return mapping
+
+def formatRelations(rels, useSecond=True):
+ predIds = {}
+ result = []
+ for rel in rels:
+ pred = rel.predicate
+ predId = getUidForObject(pred)
+ if not predId in predIds:
+ predIds[predId] = len(result)
+ result.append({'id': predId, 'name': getName(pred),
+ 'title': pred.title, 'objects': []})
+ result[predIds[predId]]['objects'].append(
+ objectAsDict(useSecond and rel.second or rel.first))
+ return result
+
diff --git a/xmlrpc/configure.zcml b/xmlrpc/configure.zcml
new file mode 100644
index 0000000..02fb696
--- /dev/null
+++ b/xmlrpc/configure.zcml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
diff --git a/xmlrpc/tests.py b/xmlrpc/tests.py
new file mode 100755
index 0000000..159d2ef
--- /dev/null
+++ b/xmlrpc/tests.py
@@ -0,0 +1,22 @@
+# $Id$
+
+import unittest, doctest
+from zope.testing.doctestunit import DocFileSuite
+
+
+class Test(unittest.TestCase):
+ "Basic tests for the loops.xmlrpc package."
+
+ def testBasics(self):
+ pass
+
+
+def test_suite():
+ flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
+ return unittest.TestSuite((
+ unittest.makeSuite(Test),
+ DocFileSuite('README.txt', optionflags=flags),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')