From fa9092b137c47f4f313a45dbda2f72e1b413aa4d Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Wed, 3 Jul 2013 19:30:22 +0200 Subject: [PATCH 1/7] use state title for display not the internal state name --- organize/stateful/view_macros.pt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/organize/stateful/view_macros.pt b/organize/stateful/view_macros.pt index ae29073..b99d5dc 100644 --- a/organize/stateful/view_macros.pt +++ b/organize/stateful/view_macros.pt @@ -111,7 +111,8 @@
State: - + tal:define="stateObject view/stateful/getStateObject" + tal:content="stateObject/title" /> - Transition: From e183dd77c6a41c2585b034f4be271ef39819a665 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 5 Jul 2013 07:45:41 +0200 Subject: [PATCH 2/7] work in progress: fields for state change dialog: concept, TODO markers, basic definition --- organize/stateful/browser.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/organize/stateful/browser.py b/organize/stateful/browser.py index 633417b..34742b1 100644 --- a/organize/stateful/browser.py +++ b/organize/stateful/browser.py @@ -134,10 +134,11 @@ class ChangeStateForm(ObjectForm, ChangeStateBase): @Lazy def schema(self): - # TODO: use field information specified in transition + # TODO: use field information specified in transition.schema + # schema = self.transition.schema commentsField = Field('comments', _(u'label_transition_comments'), - 'textarea', - description=_(u'desc_transition_comments')) + 'textarea', description=_(u'desc_transition_comments'), + storeData=False) fields = [commentsField] return Schema(name='change_state', request=self.request, manager=self, *fields) @@ -146,6 +147,8 @@ class ChangeStateForm(ObjectForm, ChangeStateBase): class ChangeState(EditObject, ChangeStateBase): def update(self): + # TODO: get field information from self.schema, + # store data in context if field.storeData is set, always track comments = self.request.form.get('comments') or u'' self.stateful.doTransition(self.action) notify(ObjectModifiedEvent(self.view.virtualTargetObject, From 4814947c5f4c79cb6890a747713b030db2f196ea Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sun, 7 Jul 2013 10:48:01 +0200 Subject: [PATCH 3/7] fix class/adapter definition of Competence; fix access to thread.local when no request is stored --- knowledge/qualification/base.py | 1 + knowledge/qualification/configure.zcml | 9 ++++++++- knowledge/qualification/interfaces.py | 4 ++-- util.py | 5 ++++- 4 files changed, 15 insertions(+), 4 deletions(-) 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/util.py b/util.py index 74f152b..55096f3 100644 --- a/util.py +++ b/util.py @@ -141,4 +141,7 @@ def saveRequest(request): local_data.request = request def getRequest(): - return local_data.request + try: + return local_data.request + except AttributeError: + return None From f91a4983426a4f4acdc69fd87756b8791f4caa37 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 12 Jul 2013 10:34:41 +0200 Subject: [PATCH 4/7] take fields for state change form from transition; store in context (if appropriate) and in change record --- organize/stateful/base.py | 13 +++++++--- organize/stateful/browser.py | 47 +++++++++++++++++++++--------------- organize/stateful/task.py | 17 +++++++++---- 3 files changed, 49 insertions(+), 28 deletions(-) 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 34742b1..6d97497 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 @@ -118,8 +116,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 = {} @@ -132,27 +140,26 @@ class ChangeStateForm(ObjectForm, ChangeStateBase): def title(self): return self.virtualTargetObject.title - @Lazy - def schema(self): - # TODO: use field information specified in transition.schema - # schema = self.transition.schema - commentsField = Field('comments', _(u'label_transition_comments'), - 'textarea', description=_(u'desc_transition_comments'), - storeData=False) - fields = [commentsField] - return Schema(name='change_state', request=self.request, - manager=self, *fields) - -class ChangeState(EditObject, ChangeStateBase): +class ChangeState(ChangeStateBase, EditObject): def update(self): - # TODO: get field information from self.schema, - # store data in context if field.storeData is set, always track - 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') From 133a9e8018208599e8a1c06b8fa8e9806a122431 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 15 Jul 2013 15:53:43 +0200 Subject: [PATCH 5/7] provide a simple compound view to be used for combinations of data/info views and report views --- browser/compound/__init__.py | 4 +++ browser/compound/configure.zcml | 14 +++++++++ browser/compound/standard.py | 54 +++++++++++++++++++++++++++++++++ browser/compound/view_macros.pt | 11 +++++++ browser/concept.py | 1 + browser/configure.zcml | 1 + browser/resource.py | 3 ++ organize/work/browser.py | 12 +++++--- 8 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 browser/compound/__init__.py create mode 100644 browser/compound/configure.zcml create mode 100644 browser/compound/standard.py create mode 100644 browser/compound/view_macros.pt 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..eac9cc3 --- /dev/null +++ b/browser/compound/view_macros.pt @@ -0,0 +1,11 @@ + + + + + + + + + + + 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 70f3b2c..75e5d7d 100644 --- a/browser/configure.zcml +++ b/browser/configure.zcml @@ -765,6 +765,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/organize/work/browser.py b/organize/work/browser.py index 564f90b..9cbeda3 100644 --- a/organize/work/browser.py +++ b/organize/work/browser.py @@ -43,6 +43,7 @@ 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 @@ -312,7 +313,7 @@ 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']) @@ -322,9 +323,12 @@ class PersonWorkItems(BaseWorkItemsView, ConceptView): 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) From e88e30539d962e773c7046bfa1d453e576275b58 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 15 Jul 2013 16:26:30 +0200 Subject: [PATCH 6/7] provide more informative title on query --- locales/de/LC_MESSAGES/loops.mo | Bin 24577 -> 24640 bytes locales/de/LC_MESSAGES/loops.po | 5 ++++- organize/work/browser.py | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/locales/de/LC_MESSAGES/loops.mo b/locales/de/LC_MESSAGES/loops.mo index 454e658170da1b4c2b00c65e24c62f9b9deacdba..403cceb59a2edb6b89c40a66d63d45b22f56085e 100644 GIT binary patch delta 7650 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 beN%e=F+IIoX~k9h`u(6Pci*0yHfH=2$Qwg% diff --git a/locales/de/LC_MESSAGES/loops.po b/locales/de/LC_MESSAGES/loops.po index 32535c4..46c2e6b 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" @@ -962,6 +962,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/work/browser.py b/organize/work/browser.py index 9cbeda3..4b002eb 100644 --- a/organize/work/browser.py +++ b/organize/work/browser.py @@ -229,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() From 6f59f3892a2ffb9d8617477d037dd374be1594a7 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 15 Jul 2013 16:50:41 +0200 Subject: [PATCH 7/7] check permission when displaying part views; restrict access to person work items --- browser/compound/view_macros.pt | 4 +++- organize/work/browser.py | 5 ++++- security/common.py | 3 +++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/browser/compound/view_macros.pt b/browser/compound/view_macros.pt index eac9cc3..c854558 100644 --- a/browser/compound/view_macros.pt +++ b/browser/compound/view_macros.pt @@ -3,7 +3,9 @@ - + + + diff --git a/organize/work/browser.py b/organize/work/browser.py index 4b002eb..3a78a10 100644 --- a/organize/work/browser.py +++ b/organize/work/browser.py @@ -50,7 +50,7 @@ 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 _ @@ -322,6 +322,9 @@ class PersonWorkItems(BaseWorkItemsView, ConceptView): columns = set(['Task', 'Title', 'Day', 'Start', 'End', 'Duration', 'Info']) + def checkPermissions(self): + return canAccessRestricted(self.context) + def getCriteria(self): return self.baseCriteria diff --git a/security/common.py b/security/common.py index 30c1c3c..f1904ec 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)