diff --git a/browser/action.py b/browser/action.py
index 4330f6e..20420d2 100644
--- a/browser/action.py
+++ b/browser/action.py
@@ -39,7 +39,7 @@ class TargetAction(Action):
viewTitle = ''
addParams = {}
- #@Lazy
+ @Lazy
def url(self):
if self.page is None: # how could this happen?
baseUrl = self.view.virtualTargetUrl
diff --git a/browser/form.py b/browser/form.py
index d68e74f..0e10db8 100644
--- a/browser/form.py
+++ b/browser/form.py
@@ -57,6 +57,7 @@ from loops.browser.concept import ConceptRelationView
from loops.i18n.browser import I18NView
from loops.query import ConceptQuery, IQueryConcept
from loops.resource import Resource
+from loops.schema.field import relation_macros
from loops.type import ITypeConcept
from loops import util
from loops.util import _
@@ -113,6 +114,7 @@ class ObjectForm(NodeView):
# replace HTML edit widget with Dojo Editor
renderers['input_html'] = self.template.macros['input_html']
renderers['input_grid'] = grid_macros.macros['input_grid']
+ renderers['input_relationset'] = relation_macros.macros['input_relationset']
return renderers
@Lazy
@@ -358,6 +360,7 @@ class CreateConceptForm(CreateObjectForm):
if ti is None:
return c
ad = ti(c)
+ ad.__is_dummy__ = True
return ad
@Lazy
@@ -394,7 +397,8 @@ class CreateConceptPage(CreateConceptForm):
@Lazy
def nextUrl(self):
- return self.nodeView.getUrlForTarget(self.context)
+ #return self.nodeView.getUrlForTarget(self.context)
+ return self.getUrlForTarget(self.context)
class InnerForm(CreateObjectForm):
diff --git a/browser/loops.js b/browser/loops.js
index 1345f4c..8b0b7f2 100644
--- a/browser/loops.js
+++ b/browser/loops.js
@@ -143,6 +143,17 @@ function addConceptAssignment(prefix, suffix) {
node.appendChild(tr);
}
+function addRelation(fieldName) {
+ valuesNode = dojo.byId(fieldName + '_values');
+ widget = dijit.byId(fieldName + '_search');
+ token = widget.getValue();
+ title = widget.getDisplayedValue();
+ ih = ' ' + title + '';
+ newNode = document.createElement('div');
+ newNode.innerHTML = ih;
+ valuesNode.appendChild(newNode);
+}
+
function validate() {
//var form = dijit.byId('dialog_form');
var form = dojo.byId('dialog_form');
diff --git a/common.py b/common.py
index 79ac503..5724308 100644
--- a/common.py
+++ b/common.py
@@ -96,6 +96,8 @@ class AdapterBase(object):
_noexportAttributes = ()
_textIndexAttributes = ()
+ __is_dummy__ = False
+
languageInfo = None
def __init__(self, context):
@@ -107,7 +109,7 @@ class AdapterBase(object):
return getattr(self.context, '_' + attr, None)
def __setattr__(self, attr, value):
- if attr in self._adapterAttributes:
+ if attr.startswith('__') or attr in self._adapterAttributes:
try:
object.__setattr__(self, attr, value)
except AttributeError:
@@ -332,6 +334,8 @@ class ParentRelationSet(RelationSet):
self.context.deassignParent(related, [self.predicate])
def __iter__(self):
+ if self.adapted.__is_dummy__:
+ return
for c in self.context.getParents([self.predicate]):
yield adapted(c, langInfo=self.langInfo)
@@ -350,6 +354,8 @@ class ChildRelationSet(RelationSet):
self.context.deassignChild(related, [self.predicate])
def __iter__(self):
+ if self.adapted.__is_dummy__:
+ return
for c in self.context.getChildren([self.predicate]):
yield adapted(c, langInfo=self.langInfo)
diff --git a/configure.zcml b/configure.zcml
index c05be78..fb088c1 100644
--- a/configure.zcml
+++ b/configure.zcml
@@ -346,13 +346,15 @@
name="fileupload" />
+
-
-
+
-
-
+
+
diff --git a/schema/__init__.py b/schema/__init__.py
new file mode 100644
index 0000000..4bc90fb
--- /dev/null
+++ b/schema/__init__.py
@@ -0,0 +1,4 @@
+"""
+$Id$
+"""
+
diff --git a/schema/base.py b/schema/base.py
new file mode 100644
index 0000000..d4711f7
--- /dev/null
+++ b/schema/base.py
@@ -0,0 +1,76 @@
+#
+# Copyright (c) 2009 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
+#
+
+"""
+Specialized field definitions.
+
+$Id$
+"""
+
+from zope.component import adapts
+from zope.interface import Attribute, implements
+from zope.schema import Choice, List
+from zope.schema.interfaces import IChoice, IList
+
+from cybertools.composer.schema.interfaces import FieldType
+
+
+class IRelation(IChoice):
+ """ An object addressed via a single relation.
+ """
+
+ target_types = Attribute('A list of names that denote types of '
+ 'loops objects (typically concept types) that may be used as '
+ 'targets for the relation.')
+
+
+class IRelationSet(IList):
+ """ A collection of objects addressed via a set of relations.
+
+ Despite its name, the collection may have a predefined order.
+ """
+
+ target_types = Attribute('A list of names that denote types of '
+ 'loops objects (typically concept types) that may be used as '
+ 'targets for the relations.')
+
+
+class Relation(Choice):
+
+ implements(IRelation)
+
+ __typeInfo__ = ('relation',)
+
+ def __init__(self, *args, **kw):
+ self.target_types = kw.pop('target_types')
+ super(Relation, self).__init__(*args, **kw)
+
+
+class RelationSet(List):
+
+ implements(IRelationSet)
+
+ __typeInfo__ = ('relationset',
+ FieldType('relationset', 'relationset',
+ u'A field representing a sequence of related objects.',
+ instanceName='relationset'))
+
+ def __init__(self, *args, **kw):
+ self.target_types = kw.pop('target_types')
+ super(RelationSet, self).__init__(*args, **kw)
+
diff --git a/schema.py b/schema/factory.py
similarity index 60%
rename from schema.py
rename to schema/factory.py
index 1d47974..33697af 100644
--- a/schema.py
+++ b/schema/factory.py
@@ -17,64 +17,16 @@
#
"""
-Specialized fields and schema factories
+Specialized fields factories.
$Id$
"""
from zope.component import adapts
-from zope.interface import Attribute, implements
-from zope.schema import Choice, List
-from zope.schema.interfaces import IChoice, IList
from cybertools.composer.schema.factory import SchemaFactory
from loops.interfaces import IResourceAdapter, IFile, INote
-# fields
-
-class IRelation(IChoice):
- """ An object addressed via a single relation.
- """
-
- target_types = Attribute('A list of names that denote types of '
- 'loops objects (typically concept types) that may be used as '
- 'targets for the relation.')
-
-
-class IRelationSet(IList):
- """ A collection of objects addressed via a set of relations.
-
- Despite its name, the collection may have a predefined order.
- """
-
- target_types = Attribute('A list of names that denote types of '
- 'loops objects (typically concept types) that may be used as '
- 'targets for the relations.')
-
-
-class Relation(Choice):
-
- implements(IRelation)
-
- __typeInfo = ('relation',)
-
- def __init__(self, *args, **kw):
- self.target_types = kw.pop('target_types')
- super(Relation, self).__init__(*args, **kw)
-
-
-class RelationSet(List):
-
- implements(IRelationSet)
-
- __typeInfo = ('relationset',)
-
- def __init__(self, *args, **kw):
- self.target_types = kw.pop('target_types')
- super(RelationSet, self).__init__(*args, **kw)
-
-
-# schema factories
class ResourceSchemaFactory(SchemaFactory):
diff --git a/schema/field.py b/schema/field.py
new file mode 100644
index 0000000..f799bc9
--- /dev/null
+++ b/schema/field.py
@@ -0,0 +1,64 @@
+#
+# 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
+#
+
+"""
+Field and field instance classes for grids.
+
+$Id$
+"""
+
+from zope import component
+from zope.component import adapts
+from zope.interface import implements
+from zope.app.pagetemplate import ViewPageTemplateFile
+from zope.cachedescriptors.property import Lazy
+import zope.schema
+
+from cybertools.composer.schema.factory import createField
+from cybertools.composer.schema.field import ListFieldInstance
+from cybertools.composer.schema.interfaces import IField, IFieldInstance
+from cybertools.composer.schema.interfaces import fieldTypes, undefined
+from cybertools.util.format import toStr, toUnicode
+from cybertools.util import json
+from loops import util
+
+
+relation_macros = ViewPageTemplateFile('relation_macros.pt')
+
+
+class RelationSetFieldInstance(ListFieldInstance):
+
+ def marshall(self, value):
+ return [dict(title=v.title, uid=util.getUidForObject(v.context))
+ for v in value]
+
+ def display(self, value):
+ return value
+
+ def unmarshall(self, value):
+ return value
+
+ @Lazy
+ def typesParams(self):
+ result = []
+ types = self.context.target_types
+ for t in types:
+ result.append('searchType=loops:concept:%s' % t)
+ if result:
+ return '?' + '&'.join(result)
+ return ''
diff --git a/schema/relation_macros.pt b/schema/relation_macros.pt
new file mode 100755
index 0000000..f0d1dda
--- /dev/null
+++ b/schema/relation_macros.pt
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/search/browser.py b/search/browser.py
index 3579625..43996c6 100644
--- a/search/browser.py
+++ b/search/browser.py
@@ -98,29 +98,32 @@ class Search(BaseView):
title = request.get('name')
if title == '*':
title = None
- type = request.get('searchType')
+ types = request.get('searchType')
data = []
- if title or type:
+ if title or types:
if title is not None:
title = title.replace('(', ' ').replace(')', ' ').replace(' -', ' ')
#title = title.split(' ', 1)[0]
- if not type:
- type = 'loops:concept:*'
- result = ConceptQuery(self).query(title=title or None, type=type,
- exclude=('system',))
- for o in result:
- if o.getLoopsRoot() == self.loopsRoot:
- name = adapted(o, self.languageInfo).title
- if title and title.endswith('*'):
- title = title[:-1]
- sort = ((title and name.startswith(title) and '0' or '1')
- + name.lower())
- if o.conceptType is None:
- raise ValueError('Concept Type missing for %r.' % name)
- data.append({'label': '%s (%s)' % (name, o.conceptType.title),
- 'name': name,
- 'id': util.getUidForObject(o),
- 'sort': sort})
+ if not types:
+ types = ['loops:concept:*']
+ if not isinstance(types, (list, tuple)):
+ types = [types]
+ for type in types:
+ result = ConceptQuery(self).query(title=title or None, type=type,
+ exclude=('system',))
+ for o in result:
+ if o.getLoopsRoot() == self.loopsRoot:
+ name = adapted(o, self.languageInfo).title
+ if title and title.endswith('*'):
+ title = title[:-1]
+ sort = ((title and name.startswith(title) and '0' or '1')
+ + name.lower())
+ if o.conceptType is None:
+ raise ValueError('Concept Type missing for %r.' % name)
+ data.append({'label': '%s (%s)' % (name, o.conceptType.title),
+ 'name': name,
+ 'id': util.getUidForObject(o),
+ 'sort': sort})
data.sort(key=lambda x: x['sort'])
if not title:
data.insert(0, {'label': '', 'name': '', 'id': ''})
diff --git a/tests/setup.py b/tests/setup.py
index 7101e78..d830da4 100644
--- a/tests/setup.py
+++ b/tests/setup.py
@@ -61,7 +61,9 @@ 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
-from loops.schema import ResourceSchemaFactory, FileSchemaFactory, NoteSchemaFactory
+from loops.schema.factory import ResourceSchemaFactory, FileSchemaFactory, \
+ NoteSchemaFactory
+from loops.schema.field import RelationSetFieldInstance
from loops.security.common import grantAcquiredSecurity, revokeAcquiredSecurity
from zope.security.management import setSecurityPolicy
from loops.security.policy import LoopsSecurityPolicy
@@ -141,6 +143,7 @@ class TestSite(object):
component.provideAdapter(BooleanFieldInstance, name='boolean')
component.provideAdapter(ListFieldInstance, name='list')
component.provideAdapter(FileUploadFieldInstance, name='fileupload')
+ component.provideAdapter(RelationSetFieldInstance, name='relationset')
component.provideAdapter(SchemaFactory)
component.provideAdapter(ResourceSchemaFactory)
component.provideAdapter(FileSchemaFactory)