diff --git a/browser/compound/__init__.py b/browser/compound/__init__.py
new file mode 100644
index 0000000..e12cdd8
--- /dev/null
+++ b/browser/compound/__init__.py
@@ -0,0 +1,4 @@
+"""
+package loops.browser.compound
+"""
+
diff --git a/browser/compound/configure.zcml b/browser/compound/configure.zcml
new file mode 100644
index 0000000..fac57ba
--- /dev/null
+++ b/browser/compound/configure.zcml
@@ -0,0 +1,14 @@
+
+
+
+
+
diff --git a/browser/compound/standard.py b/browser/compound/standard.py
new file mode 100644
index 0000000..4e5576f
--- /dev/null
+++ b/browser/compound/standard.py
@@ -0,0 +1,54 @@
+#
+# Copyright (c) 2013 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
+#
+
+"""
+Definition of compound views.
+"""
+
+from zope import interface, component
+from zope.app.pagetemplate import ViewPageTemplateFile
+from zope.cachedescriptors.property import Lazy
+
+from loops.browser.concept import ConceptView
+from loops.util import _
+
+
+compound_macros = ViewPageTemplateFile('view_macros.pt')
+
+
+class CompoundView(ConceptView):
+
+ @Lazy
+ def macro(self):
+ return compound_macros.macros['standard']
+
+ def getParts(self):
+ parts = (self.options('view_parts') or self.typeOptions('view_parts') or [])
+ return self.getPartViews(parts)
+
+ def getPartViews(self, parts):
+ result = []
+ for p in parts:
+ view = component.queryMultiAdapter((self.adapted, self.request), name=p)
+ if view is None:
+ view = component.queryMultiAdapter((self.context, self.request), name=p)
+ if view is not None:
+ view.parent = self
+ result.append(view)
+ return result
+
diff --git a/browser/compound/view_macros.pt b/browser/compound/view_macros.pt
new file mode 100644
index 0000000..c854558
--- /dev/null
+++ b/browser/compound/view_macros.pt
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/browser/concept.py b/browser/concept.py
index b550830..f06c419 100644
--- a/browser/concept.py
+++ b/browser/concept.py
@@ -235,6 +235,7 @@ class ConceptView(BaseView):
subMacro=concept_macros.macros['parents'],
priority=20, info=self)
+ # the part-based layout is now implemented in loops.browser.compound
def getParts(self):
parts = (self.params.get('parts') or []) # deprecated!
if not parts:
diff --git a/browser/configure.zcml b/browser/configure.zcml
index 37f0542..311b87b 100644
--- a/browser/configure.zcml
+++ b/browser/configure.zcml
@@ -772,6 +772,7 @@
attribute="cleanup"
permission="zope.ManageSite" />
+
diff --git a/browser/resource.py b/browser/resource.py
index 8ec1e95..0960cb2 100644
--- a/browser/resource.py
+++ b/browser/resource.py
@@ -132,6 +132,9 @@ class ResourceView(BaseView):
def macro(self):
if 'image/' in self.context.contentType:
return self.template.macros['image']
+ #elif 'audio/' in self.context.contentType:
+ # self.registerDojoAudio()
+ # return self.template.macros['audio']
else:
return self.template.macros['download']
diff --git a/knowledge/qualification/base.py b/knowledge/qualification/base.py
index 32c9910..8a667e2 100644
--- a/knowledge/qualification/base.py
+++ b/knowledge/qualification/base.py
@@ -26,6 +26,7 @@ from zope.component import adapts
from zope.interface import implementer, implements
from loops.common import AdapterBase
+from loops.interfaces import IConcept
from loops.knowledge.qualification.interfaces import ICompetence
from loops.type import TypeInterfaceSourceList
diff --git a/knowledge/qualification/configure.zcml b/knowledge/qualification/configure.zcml
index d8cbc74..dea246f 100644
--- a/knowledge/qualification/configure.zcml
+++ b/knowledge/qualification/configure.zcml
@@ -4,7 +4,14 @@
i18n_domain="loops">
+ factory="loops.knowledge.qualification.base.Competence"
+ trusted="True" />
+
+
+
+
diff --git a/knowledge/qualification/interfaces.py b/knowledge/qualification/interfaces.py
index 85a002a..87b3e2d 100644
--- a/knowledge/qualification/interfaces.py
+++ b/knowledge/qualification/interfaces.py
@@ -23,11 +23,11 @@ Interfaces for knowledge management and elearning with loops.
from zope.interface import Interface, Attribute
from zope import interface, component, schema
-from loops.interfaces import IConceptSchema
+from loops.interfaces import IConceptSchema, ILoopsAdapter
from loops.util import _
-class ICompetence(IConceptSchema):
+class ICompetence(ILoopsAdapter):
""" The competence of a person.
Maybe assigned to the person via a 'knows' relation or
diff --git a/locales/de/LC_MESSAGES/loops.mo b/locales/de/LC_MESSAGES/loops.mo
index 4308de5..0a244f1 100644
Binary files a/locales/de/LC_MESSAGES/loops.mo and b/locales/de/LC_MESSAGES/loops.mo differ
diff --git a/locales/de/LC_MESSAGES/loops.po b/locales/de/LC_MESSAGES/loops.po
index 77ed12b..afe516f 100644
--- a/locales/de/LC_MESSAGES/loops.po
+++ b/locales/de/LC_MESSAGES/loops.po
@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: 0.13.0\n"
"POT-Creation-Date: 2007-05-22 12:00 CET\n"
-"PO-Revision-Date: 2013-06-20 12:00 CET\n"
+"PO-Revision-Date: 2013-07-15 12:00 CET\n"
"Last-Translator: Helmut Merz \n"
"Language-Team: loops developers \n"
"MIME-Version: 1.0\n"
@@ -965,6 +965,9 @@ msgstr "Kalender"
msgid "Work Items"
msgstr "Aktivitäten"
+msgid "Work Items for $title"
+msgstr "Aktivitäten für $title"
+
msgid "Day"
msgstr "Tag"
diff --git a/organize/stateful/base.py b/organize/stateful/base.py
index fc2d942..be2a257 100644
--- a/organize/stateful/base.py
+++ b/organize/stateful/base.py
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
+# Copyright (c) 2013 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
@@ -18,8 +18,6 @@
"""
Basic implementations for stateful objects and adapters.
-
-$Id$
"""
from zope.app.catalog.interfaces import ICatalog
@@ -27,6 +25,7 @@ from zope.cachedescriptors.property import Lazy
from zope import component
from zope.component import adapts, adapter
+from cybertools.composer.schema.field import Field
from cybertools.meta.interfaces import IOptions
from cybertools.stateful.base import Stateful as BaseStateful
from cybertools.stateful.base import StatefulAdapter, IndexInfo
@@ -34,6 +33,7 @@ from cybertools.stateful.interfaces import IStatesDefinition, ITransitionEvent
from loops.common import adapted
from loops.interfaces import ILoopsObject, IConcept, IResource
from loops import util
+from loops.util import _
class Stateful(BaseStateful):
@@ -93,3 +93,10 @@ def handleTransition(obj, event):
if next != previous:
cat = component.getUtility(ICatalog)
cat.index_doc(int(util.getUidForObject(obj)), obj)
+
+
+# predefined fields for transition forms
+
+commentsField = Field('comments', _(u'label_transition_comments'), 'textarea',
+ description=_(u'desc_transition_comments'),
+ nostore=True)
diff --git a/organize/stateful/browser.py b/organize/stateful/browser.py
index 1265b45..c98b073 100644
--- a/organize/stateful/browser.py
+++ b/organize/stateful/browser.py
@@ -28,8 +28,6 @@ from zope.i18n import translate
from zope.lifecycleevent import ObjectModifiedEvent, Attributes
from cybertools.browser.action import Action, actions
-from cybertools.composer.schema.field import Field
-from cybertools.composer.schema.interfaces import ISchemaFactory
from cybertools.composer.schema.schema import Schema
from cybertools.stateful.interfaces import IStateful, IStatesDefinition
from loops.browser.common import BaseView
@@ -124,8 +122,18 @@ class ChangeStateBase(object):
def stateObject(self):
return self.stateful.getStateObject()
+ @Lazy
+ def schema(self):
+ schema = self.transition.schema
+ if schema is None:
+ return Schema()
+ else:
+ schema.manager = self
+ schema.request = self.request
+ return schema
-class ChangeStateForm(ObjectForm, ChangeStateBase):
+
+class ChangeStateForm(ChangeStateBase, ObjectForm):
form_action = 'change_state_action'
data = {}
@@ -138,24 +146,26 @@ class ChangeStateForm(ObjectForm, ChangeStateBase):
def title(self):
return self.virtualTargetObject.title
- @Lazy
- def schema(self):
- # TODO: use field information specified in transition
- commentsField = Field('comments', _(u'label_transition_comments'),
- 'textarea',
- description=_(u'desc_transition_comments'))
- fields = [commentsField]
- return Schema(name='change_state', request=self.request,
- manager=self, *fields)
-
-class ChangeState(EditObject, ChangeStateBase):
+class ChangeState(ChangeStateBase, EditObject):
def update(self):
- comments = self.request.form.get('comments') or u''
+ formData = self.request.form
+ # store data in context (unless field.nostore)
+ self.object = self.context
+ formState = self.instance.applyTemplate(data=formData)
+ # TODO: check formState
+ # track all fields
+ trackData = dict(transition=self.action)
+ for f in self.fields:
+ if f.readonly:
+ continue
+ name = f.name
+ fi = formState.fieldInstances[name]
+ rawValue = fi.getRawValue(formData, name, u'')
+ trackData[name] = fi.unmarshall(rawValue)
self.stateful.doTransition(self.action)
- notify(ObjectModifiedEvent(self.view.virtualTargetObject,
- dict(transition=self.action, comments=comments)))
+ notify(ObjectModifiedEvent(self.view.virtualTargetObject, trackData))
return True
diff --git a/organize/stateful/task.py b/organize/stateful/task.py
index 5e60ee5..dea88d4 100644
--- a/organize/stateful/task.py
+++ b/organize/stateful/task.py
@@ -26,12 +26,15 @@ from zope.component import adapter
from zope.interface import implementer
from zope.traversing.api import getName
+from cybertools.composer.schema.schema import Schema
from cybertools.stateful.definition import StatesDefinition
from cybertools.stateful.definition import State, Transition
from cybertools.stateful.interfaces import IStatesDefinition, IStateful
from loops.common import adapted
+from loops.organize.stateful.base import commentsField
from loops.organize.stateful.base import StatefulLoopsObject
from loops.security.interfaces import ISecuritySetter
+from loops.util import _
def setPermissionsForRoles(settings):
@@ -42,6 +45,10 @@ def setPermissionsForRoles(settings):
return setSecurity
+defaultSchema = Schema(commentsField,
+ name='change_state')
+
+
@implementer(IStatesDefinition)
def taskStates():
return StatesDefinition('task_states',
@@ -55,11 +62,11 @@ def taskStates():
color='x'),
State('archived', 'archived', ('reopen',),
color='grey'),
- Transition('release', 'release', 'active'),
- Transition('finish', 'finish', 'finished'),
- Transition('cancel', 'cancel', 'cancelled'),
- Transition('reopen', 're-open', 'draft'),
- Transition('archive', 'archive', 'archived'),
+ Transition('release', 'release', 'active', schema=defaultSchema),
+ Transition('finish', 'finish', 'finished', schema=defaultSchema),
+ Transition('cancel', 'cancel', 'cancelled', schema=defaultSchema),
+ Transition('reopen', 're-open', 'draft', schema=defaultSchema),
+ Transition('archive', 'archive', 'archived', schema=defaultSchema),
initialState='draft')
diff --git a/organize/work/browser.py b/organize/work/browser.py
index 564f90b..3a78a10 100644
--- a/organize/work/browser.py
+++ b/organize/work/browser.py
@@ -43,13 +43,14 @@ from loops.browser.concept import ConceptView
from loops.browser.form import ObjectForm, EditObject
from loops.browser.node import NodeView
from loops.common import adapted
+from loops.organize.interfaces import IPerson
from loops.organize.party import getPersonForUser
from loops.organize.stateful.browser import StateAction
from loops.organize.tracking.browser import BaseTrackView
from loops.organize.tracking.report import TrackDetails
from loops.organize.work.base import WorkItem
from loops.security.common import canAccessObject, canListObject, canWriteObject
-from loops.security.common import checkPermission
+from loops.security.common import canAccessRestricted, checkPermission
from loops import util
from loops.util import _
@@ -228,6 +229,10 @@ class BaseWorkItemsView(object):
def macro(self):
return self.work_macros['workitems_query']
+ @Lazy
+ def title(self):
+ return _(u'Work Items for $title', mapping=dict(title=self.context.title))
+
@Lazy
def workItems(self):
rm = self.loopsRoot.getRecordManager()
@@ -312,19 +317,25 @@ class RelatedTaskWorkItems(AllWorkItems):
class PersonWorkItems(BaseWorkItemsView, ConceptView):
- """ A query view showing work items for a person, the query's parent.
+ """ A view showing work items for a person or the context object's parents.
"""
columns = set(['Task', 'Title', 'Day', 'Start', 'End', 'Duration', 'Info'])
+ def checkPermissions(self):
+ return canAccessRestricted(self.context)
+
def getCriteria(self):
return self.baseCriteria
def listWorkItems(self):
criteria = self.getCriteria()
- for target in self.context.getParents([self.defaultPredicate]):
- un = criteria.setdefault('userName', [])
- un.append(util.getUidForObject(target))
+ un = criteria.setdefault('userName', [])
+ if IPerson.providedBy(self.adapted):
+ un.append(util.getUidForObject(self.context))
+ else:
+ for target in self.context.getParents([self.defaultPredicate]):
+ un.append(util.getUidForObject(target))
return sorted(self.query(**criteria), key=lambda x: x.track.timeStamp)
diff --git a/security/common.py b/security/common.py
index 1a5fb58..13e3835 100644
--- a/security/common.py
+++ b/security/common.py
@@ -74,6 +74,9 @@ def canListObject(obj, noCheck=False):
return True
return canAccess(obj, 'title')
+def canAccessRestricted(obj):
+ return checkPermission('loops.ViewRestricted', obj)
+
def canWriteObject(obj):
return canWrite(obj, 'title') or canAssignAsParent(obj)
diff --git a/util.py b/util.py
index 9d7e46a..6ba91a9 100644
--- a/util.py
+++ b/util.py
@@ -145,4 +145,7 @@ def saveRequest(request):
local_data.request = request
def getRequest():
- return local_data.request
+ try:
+ return local_data.request
+ except AttributeError:
+ return None