merge branch master

This commit is contained in:
Helmut Merz 2013-07-16 18:13:05 +02:00
commit 7e70140f52
18 changed files with 177 additions and 35 deletions

View file

@ -0,0 +1,4 @@
"""
package loops.browser.compound
"""

View file

@ -0,0 +1,14 @@
<configure
xmlns:zope="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="loops">
<zope:adapter
name="compound.html"
for="loops.interfaces.IConcept
zope.publisher.interfaces.browser.IBrowserRequest"
provides="zope.interface.Interface"
factory="loops.browser.compound.standard.CompoundView"
permission="zope.View" />
</configure>

View file

@ -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

View file

@ -0,0 +1,13 @@
<html i18n:domain="loops">
<metal:data define-macro="standard">
<tal:part repeat="item item/getParts">
<tal:check condition="item/checkPermissions">
<metal:part use-macro="item/macro" />
</tal:check>
</tal:part>
</metal:data>
</html>

View file

@ -235,6 +235,7 @@ class ConceptView(BaseView):
subMacro=concept_macros.macros['parents'], subMacro=concept_macros.macros['parents'],
priority=20, info=self) priority=20, info=self)
# the part-based layout is now implemented in loops.browser.compound
def getParts(self): def getParts(self):
parts = (self.params.get('parts') or []) # deprecated! parts = (self.params.get('parts') or []) # deprecated!
if not parts: if not parts:

View file

@ -772,6 +772,7 @@
attribute="cleanup" attribute="cleanup"
permission="zope.ManageSite" /> permission="zope.ManageSite" />
<include package=".compound" />
<include package=".skin" /> <include package=".skin" />
<include package=".lobo" /> <include package=".lobo" />
<include package=".mobile" /> <include package=".mobile" />

View file

@ -132,6 +132,9 @@ class ResourceView(BaseView):
def macro(self): def macro(self):
if 'image/' in self.context.contentType: if 'image/' in self.context.contentType:
return self.template.macros['image'] return self.template.macros['image']
#elif 'audio/' in self.context.contentType:
# self.registerDojoAudio()
# return self.template.macros['audio']
else: else:
return self.template.macros['download'] return self.template.macros['download']

View file

@ -26,6 +26,7 @@ from zope.component import adapts
from zope.interface import implementer, implements from zope.interface import implementer, implements
from loops.common import AdapterBase from loops.common import AdapterBase
from loops.interfaces import IConcept
from loops.knowledge.qualification.interfaces import ICompetence from loops.knowledge.qualification.interfaces import ICompetence
from loops.type import TypeInterfaceSourceList from loops.type import TypeInterfaceSourceList

View file

@ -4,7 +4,14 @@
i18n_domain="loops"> i18n_domain="loops">
<zope:adapter <zope:adapter
factory="loops.knowledge.qualification.base.Competence" /> factory="loops.knowledge.qualification.base.Competence"
trusted="True" />
<zope:class class="loops.knowledge.qualification.base.Competence">
<require permission="zope.View"
interface="loops.knowledge.qualification.interfaces.ICompetence" />
<require permission="zope.ManageContent"
set_schema="loops.knowledge.qualification.interfaces.ICompetence" />
</zope:class>
<!-- views --> <!-- views -->

View file

@ -23,11 +23,11 @@ Interfaces for knowledge management and elearning with loops.
from zope.interface import Interface, Attribute from zope.interface import Interface, Attribute
from zope import interface, component, schema from zope import interface, component, schema
from loops.interfaces import IConceptSchema from loops.interfaces import IConceptSchema, ILoopsAdapter
from loops.util import _ from loops.util import _
class ICompetence(IConceptSchema): class ICompetence(ILoopsAdapter):
""" The competence of a person. """ The competence of a person.
Maybe assigned to the person via a 'knows' relation or Maybe assigned to the person via a 'knows' relation or

Binary file not shown.

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: 0.13.0\n" "Project-Id-Version: 0.13.0\n"
"POT-Creation-Date: 2007-05-22 12:00 CET\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 <helmutm@cy55.de>\n" "Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
"Language-Team: loops developers <helmutm@cy55.de>\n" "Language-Team: loops developers <helmutm@cy55.de>\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -965,6 +965,9 @@ msgstr "Kalender"
msgid "Work Items" msgid "Work Items"
msgstr "Aktivitäten" msgstr "Aktivitäten"
msgid "Work Items for $title"
msgstr "Aktivitäten für $title"
msgid "Day" msgid "Day"
msgstr "Tag" msgstr "Tag"

View file

@ -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 # 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 # it under the terms of the GNU General Public License as published by
@ -18,8 +18,6 @@
""" """
Basic implementations for stateful objects and adapters. Basic implementations for stateful objects and adapters.
$Id$
""" """
from zope.app.catalog.interfaces import ICatalog from zope.app.catalog.interfaces import ICatalog
@ -27,6 +25,7 @@ from zope.cachedescriptors.property import Lazy
from zope import component from zope import component
from zope.component import adapts, adapter from zope.component import adapts, adapter
from cybertools.composer.schema.field import Field
from cybertools.meta.interfaces import IOptions from cybertools.meta.interfaces import IOptions
from cybertools.stateful.base import Stateful as BaseStateful from cybertools.stateful.base import Stateful as BaseStateful
from cybertools.stateful.base import StatefulAdapter, IndexInfo 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.common import adapted
from loops.interfaces import ILoopsObject, IConcept, IResource from loops.interfaces import ILoopsObject, IConcept, IResource
from loops import util from loops import util
from loops.util import _
class Stateful(BaseStateful): class Stateful(BaseStateful):
@ -93,3 +93,10 @@ def handleTransition(obj, event):
if next != previous: if next != previous:
cat = component.getUtility(ICatalog) cat = component.getUtility(ICatalog)
cat.index_doc(int(util.getUidForObject(obj)), obj) 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)

View file

@ -28,8 +28,6 @@ from zope.i18n import translate
from zope.lifecycleevent import ObjectModifiedEvent, Attributes from zope.lifecycleevent import ObjectModifiedEvent, Attributes
from cybertools.browser.action import Action, actions 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.composer.schema.schema import Schema
from cybertools.stateful.interfaces import IStateful, IStatesDefinition from cybertools.stateful.interfaces import IStateful, IStatesDefinition
from loops.browser.common import BaseView from loops.browser.common import BaseView
@ -124,8 +122,18 @@ class ChangeStateBase(object):
def stateObject(self): def stateObject(self):
return self.stateful.getStateObject() 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' form_action = 'change_state_action'
data = {} data = {}
@ -138,24 +146,26 @@ class ChangeStateForm(ObjectForm, ChangeStateBase):
def title(self): def title(self):
return self.virtualTargetObject.title 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(ChangeStateBase, EditObject):
class ChangeState(EditObject, ChangeStateBase):
def update(self): 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) self.stateful.doTransition(self.action)
notify(ObjectModifiedEvent(self.view.virtualTargetObject, notify(ObjectModifiedEvent(self.view.virtualTargetObject, trackData))
dict(transition=self.action, comments=comments)))
return True return True

View file

@ -26,12 +26,15 @@ from zope.component import adapter
from zope.interface import implementer from zope.interface import implementer
from zope.traversing.api import getName from zope.traversing.api import getName
from cybertools.composer.schema.schema import Schema
from cybertools.stateful.definition import StatesDefinition from cybertools.stateful.definition import StatesDefinition
from cybertools.stateful.definition import State, Transition from cybertools.stateful.definition import State, Transition
from cybertools.stateful.interfaces import IStatesDefinition, IStateful from cybertools.stateful.interfaces import IStatesDefinition, IStateful
from loops.common import adapted from loops.common import adapted
from loops.organize.stateful.base import commentsField
from loops.organize.stateful.base import StatefulLoopsObject from loops.organize.stateful.base import StatefulLoopsObject
from loops.security.interfaces import ISecuritySetter from loops.security.interfaces import ISecuritySetter
from loops.util import _
def setPermissionsForRoles(settings): def setPermissionsForRoles(settings):
@ -42,6 +45,10 @@ def setPermissionsForRoles(settings):
return setSecurity return setSecurity
defaultSchema = Schema(commentsField,
name='change_state')
@implementer(IStatesDefinition) @implementer(IStatesDefinition)
def taskStates(): def taskStates():
return StatesDefinition('task_states', return StatesDefinition('task_states',
@ -55,11 +62,11 @@ def taskStates():
color='x'), color='x'),
State('archived', 'archived', ('reopen',), State('archived', 'archived', ('reopen',),
color='grey'), color='grey'),
Transition('release', 'release', 'active'), Transition('release', 'release', 'active', schema=defaultSchema),
Transition('finish', 'finish', 'finished'), Transition('finish', 'finish', 'finished', schema=defaultSchema),
Transition('cancel', 'cancel', 'cancelled'), Transition('cancel', 'cancel', 'cancelled', schema=defaultSchema),
Transition('reopen', 're-open', 'draft'), Transition('reopen', 're-open', 'draft', schema=defaultSchema),
Transition('archive', 'archive', 'archived'), Transition('archive', 'archive', 'archived', schema=defaultSchema),
initialState='draft') initialState='draft')

View file

@ -43,13 +43,14 @@ from loops.browser.concept import ConceptView
from loops.browser.form import ObjectForm, EditObject from loops.browser.form import ObjectForm, EditObject
from loops.browser.node import NodeView from loops.browser.node import NodeView
from loops.common import adapted from loops.common import adapted
from loops.organize.interfaces import IPerson
from loops.organize.party import getPersonForUser from loops.organize.party import getPersonForUser
from loops.organize.stateful.browser import StateAction from loops.organize.stateful.browser import StateAction
from loops.organize.tracking.browser import BaseTrackView from loops.organize.tracking.browser import BaseTrackView
from loops.organize.tracking.report import TrackDetails from loops.organize.tracking.report import TrackDetails
from loops.organize.work.base import WorkItem from loops.organize.work.base import WorkItem
from loops.security.common import canAccessObject, canListObject, canWriteObject 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 import util
from loops.util import _ from loops.util import _
@ -228,6 +229,10 @@ class BaseWorkItemsView(object):
def macro(self): def macro(self):
return self.work_macros['workitems_query'] return self.work_macros['workitems_query']
@Lazy
def title(self):
return _(u'Work Items for $title', mapping=dict(title=self.context.title))
@Lazy @Lazy
def workItems(self): def workItems(self):
rm = self.loopsRoot.getRecordManager() rm = self.loopsRoot.getRecordManager()
@ -312,19 +317,25 @@ class RelatedTaskWorkItems(AllWorkItems):
class PersonWorkItems(BaseWorkItemsView, ConceptView): 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']) columns = set(['Task', 'Title', 'Day', 'Start', 'End', 'Duration', 'Info'])
def checkPermissions(self):
return canAccessRestricted(self.context)
def getCriteria(self): def getCriteria(self):
return self.baseCriteria return self.baseCriteria
def listWorkItems(self): def listWorkItems(self):
criteria = self.getCriteria() criteria = self.getCriteria()
for target in self.context.getParents([self.defaultPredicate]): un = criteria.setdefault('userName', [])
un = criteria.setdefault('userName', []) if IPerson.providedBy(self.adapted):
un.append(util.getUidForObject(target)) 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) return sorted(self.query(**criteria), key=lambda x: x.track.timeStamp)

View file

@ -74,6 +74,9 @@ def canListObject(obj, noCheck=False):
return True return True
return canAccess(obj, 'title') return canAccess(obj, 'title')
def canAccessRestricted(obj):
return checkPermission('loops.ViewRestricted', obj)
def canWriteObject(obj): def canWriteObject(obj):
return canWrite(obj, 'title') or canAssignAsParent(obj) return canWrite(obj, 'title') or canAssignAsParent(obj)

View file

@ -145,4 +145,7 @@ def saveRequest(request):
local_data.request = request local_data.request = request
def getRequest(): def getRequest():
return local_data.request try:
return local_data.request
except AttributeError:
return None