merge branch master

This commit is contained in:
Helmut Merz 2012-09-05 16:17:15 +02:00
commit 17012129b8
23 changed files with 565 additions and 171 deletions

View file

@ -45,6 +45,7 @@ from cybertools.composer.schema.grid.field import grid_macros
from cybertools.composer.schema.interfaces import ISchemaFactory
from cybertools.composer.schema.browser.common import schema_macros, schema_edit_macros
from cybertools.composer.schema.schema import FormState
from cybertools.meta.interfaces import IOptions
from cybertools.stateful.interfaces import IStateful
from cybertools.typology.interfaces import IType, ITypeManager
from cybertools.util.format import toUnicode
@ -739,6 +740,13 @@ class EditConcept(EditObject):
return obj.getParentRelations(predicates=predicates, parent=concept)
def assignConcept(self, obj, concept, predicate):
if IOptions(adapted(concept.conceptType)).children_append:
sibRelations = concept.getChildRelations()
if sibRelations:
maxOrder = max([r.order for r in sibRelations])
if maxOrder > 0:
return obj.assignParent(concept, predicate,
order=maxOrder+1)
obj.assignParent(concept, predicate)
def deassignConcept(self, obj, concept, predicates):

View file

@ -526,6 +526,12 @@ div.comment {
background-color: #eeeeff;
}
tr.agenda-item-headline td,
tr.agenda-item-headline td a,
tr.agenda-item-headline td a[href] {
color: white;
}
/* dojo stuff */
.dijitDialog {

View file

@ -125,22 +125,18 @@ function setConceptTypeForComboBox(typeId, cbId) {
}
var dialog;
var dialogName;
function objectDialog(dlgName, url) {
dojo.require('dijit.Dialog');
dojo.require('dojo.parser');
dojo.require('dijit.form.FilteringSelect');
dojo.require('dojox.data.QueryReadStore');
if (dialogName == undefined || dialogName != dlgName || dialogName == '') {
if (dialog != undefined) {
dialog.destroyRecursive();
}
dialogName = dlgName;
dialog = new dijit.Dialog({
href: url
}, dojo.byId('dialog.' + dlgName));
}
dialog.show();
}

View file

@ -510,6 +510,12 @@ img.notselected {
text-align: right;
}
/* work */
.work_event {
background-color: #f3f3ff;
}
/* lobo layout-specific classes */
.legend {
@ -591,6 +597,10 @@ div.comment {
/* calendar, work items */
.MinutesAndAgendaTitles a[href] {
color: white;
}
.today {
color: #444488;
font-weight: bold;

View file

@ -50,6 +50,18 @@ class Base(object):
for p in self.context.getParents([self.isPartOfPredicate]):
return self.nodeView.getViewForTarget(p)
@Lazy
def tabview(self):
if self.editable:
return 'index.html'
class BookOverview(Base, ConceptView):
@Lazy
def macro(self):
return book_template.macros['book']
class SectionView(Base, ConceptView):
@ -58,17 +70,24 @@ class SectionView(Base, ConceptView):
return book_template.macros['section']
@Lazy
def tabview(self):
if self.editable:
return 'index.html'
def documentTypeType(self):
return self.conceptManager['documenttype']
@Lazy
def sectionType(self):
return self.conceptManager['section']
def getCssClassForResource(self, r):
documentType = self.conceptManager['documenttype']
for c in r.context.getConcepts([self.defaultPredicate]):
if c.conceptType == documentType:
if c.conceptType == self.documentTypeType:
return getName(c)
return 'textelement'
def getParentsForResource(self, r):
for c in r.context.getConcepts([self.defaultPredicate]):
if c != self.context and c.conceptType != self.documentTypeType:
yield c
# layout parts - probably obsolete:

View file

@ -17,6 +17,14 @@
<!-- Views -->
<zope:adapter
name="book_overview"
for="loops.interfaces.IConcept
loops.browser.skin.Lobo"
provides="zope.interface.Interface"
factory="loops.compound.book.browser.BookOverview"
permission="zope.View" />
<zope:adapter
name="section_view"
for="loops.interfaces.IConcept

View file

@ -2,7 +2,7 @@ type(u'documenttype', u'Dokumentenart', options=u'qualifier:assign',
viewName=u'')
# book types
type(u'book', u'Buch', viewName=u'', typeInterface=u'',
type(u'book', u'Buch', viewName=u'book_overview', typeInterface=u'',
options=u'action.portlet:create_subtype,edit_concept')
#type(u'page', u'Seite', viewName=u'page_layout',
# typeInterface=u'loops.compound.book.interfaces.IPage',

View file

@ -1,9 +1,26 @@
<html i18n:domain="loops">
<metal:book define-macro="book">
<metal:info use-macro="view/concept_macros/concepttitle" />
<div tal:repeat="related item/children">
<h3>
<a tal:attributes="href python:view.getUrlForTarget(related)"
tal:content="related/title" /></h3>
<div tal:content="structure related/renderedDescription" />
</div>
</metal:book>
<metal:section define-macro="section">
<metal:info use-macro="view/concept_macros/concepttitle" />
<div tal:repeat="related item/resources">
<div class="span-4">
<div tal:attributes="class python:
item.getCssClassForResource(related)"
tal:content="structure related/render" />
</div>
<div class="span-2 last" style="padding-top: 0.4em">
<div class="object-actions" style="padding-top: 0"
tal:define="url python:view.getUrlForTarget(related.context)"
tal:condition="related/editable">
@ -24,9 +41,11 @@
<img tal:attributes="src
string:$resourceBase/cybertools.icons/application_edit.png" /></a>
</div>
<div tal:attributes="class python:
item.getCssClassForResource(related)"
tal:content="structure related/render" />
<div tal:repeat="parent python:item.getParentsForResource(related)">
<a tal:content="parent/title"
tal:attributes="href python:view.getUrlForTarget(parent)" />
</div>
</div>
</div>
</metal:section>

View file

@ -68,9 +68,19 @@
</metal:right>
<metal:state define-macro="state">
<tal:column define=" value python:col.getDisplayValue(row)">
<img tal:attributes="src string:$resourceBase${value/icon};
alt value/title;
title value/title" />
</tal:column>
</metal:state>
<metal:target define-macro="target">
<tal:column define="value python:col.getDisplayValue(row)">
<a tal:omit-tag="python:not getattr(item, 'showLinks', True) or not value['url']"
<a tal:omit-tag="python:
not getattr(item, 'showLinks', True) or not value['url']"
tal:attributes="href value/url"
tal:content="value/title" />
</tal:column>

View file

@ -28,6 +28,7 @@ from zope.schema.interfaces import IVocabularyFactory, IContextSourceBinder
from cybertools.composer.report.field import Field as BaseField
from cybertools.composer.report.field import TableCellStyle
from cybertools.composer.report.result import ResultSet
from cybertools.stateful.interfaces import IStateful, IStatesDefinition
from cybertools.util.date import timeStamp2Date
from loops.common import baseObject
from loops.expert.report import ReportInstance
@ -115,6 +116,23 @@ class DateField(Field):
return value.isoformat()[:10]
class StateField(Field):
statesDefinition = 'workItemStates'
renderer = 'state'
def getDisplayValue(self, row):
if IStateful.providedBy(row.context):
stf = row.context
else:
stf = component.getAdapter(row.context, IStateful,
name=self.statesDefinition)
stateObject = stf.getStateObject()
icon = stateObject.icon or 'led%s.png' % stateObject.color
return dict(title=util._(stateObject.title),
icon='cybertools.icons/' + icon)
class VocabularyField(Field):
vocabulary = None

Binary file not shown.

View file

@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: $Id$\n"
"POT-Creation-Date: 2007-05-22 12:00 CET\n"
"PO-Revision-Date: 2012-07-21 12:00 CET\n"
"PO-Revision-Date: 2012-08-20 12:00 CET\n"
"Last-Translator: Helmut Merz <helmutm@cy55.de>\n"
"Language-Team: loops developers <helmutm@cy55.de>\n"
"MIME-Version: 1.0\n"
@ -254,6 +254,18 @@ msgstr "Termin bearbeiten"
msgid "Modify follow-up event."
msgstr "Folgetermin bearbeiten"
msgid "Create Agenda Item..."
msgstr "Tagesordnungspunkt anlegen..."
msgid "Create a new agenda item."
msgstr "Einen neuen Tagesordnungspunkt anlegen."
msgid "Edit Agenda Item..."
msgstr "Tagesordnungspunkt bearbeiten..."
msgid "Modify agenda item."
msgstr "Tagesordnungspunkt bearbeiten"
msgid "Create Task..."
msgstr "Aufgabe anlegen..."
@ -275,6 +287,12 @@ msgstr "Eine neues Projekt anlegen."
msgid "Create Work Item..."
msgstr "Aktivität anlegen..."
msgid "Create a work item for this object."
msgstr "Eine Aktivität zu diesem Objekt anlegen."
msgid "Add Work Item"
msgstr "Aktivität anlegen/bearbeiten"
msgid "Edit Video..."
msgstr "Video bearbeiten..."
@ -296,6 +314,9 @@ msgstr "Besprechungsprotokoll für dieses Objekt anzeigen."
msgid "Download Meeting Minutes"
msgstr "Besprechungsprotokoll generieren"
msgid "Copy Agenda Items"
msgstr "Tagesordnungspunkte kopieren"
msgid "Participants"
msgstr "Teilnehmer"
@ -303,7 +324,7 @@ msgid "The names of the persons taking part in the event."
msgstr "Die Namen der Personen, die an der Besprechung teilnehmen."
msgid "label_responsible"
msgstr "Vortragender"
msgstr "Vortragende/r"
msgid "desc_responsible"
msgstr "Person, die diesen Tagesordnungpunkt vertritt."
@ -315,10 +336,19 @@ msgid "desc_discussion"
msgstr "Diskussion"
msgid "label_consequences"
msgstr "Schlussfolgerungen"
msgstr "Schlussfolgerung"
msgid "desc_consequences"
msgstr "Schlussfolgerungen"
msgstr "Schlussfolgerung"
msgid "header_workitems"
msgstr "Aufgaben"
msgid "header_responsible"
msgstr "zuständig"
msgid "header_deadline"
msgstr "Termin"
msgid "Task/Action"
msgstr "Aufgabe"

View file

@ -39,6 +39,7 @@ from loops.browser.node import NodeView
from loops.common import adapted, baseObject
from loops.concept import Concept
from loops.organize.work.meeting import MeetingMinutes
from loops.organize.tracking.report import TrackDetails
from loops.setup import addAndConfigureObject
from loops.util import _
from loops import util
@ -79,6 +80,24 @@ actions.register('editFollowUpEvent', 'portlet', TargetAction,
prerequisites=['registerDojoDateWidget'],
)
actions.register('createAgendaItem', 'portlet', DialogAction,
title=_(u'Create Agenda Item...'),
description=_(u'Create a new agenda item.'),
viewName='create_concept.html',
dialogName='createAgendaItem',
typeToken='.loops/concepts/agendaitem',
fixedType=True,
innerForm='inner_concept_form.html',
prerequisites=['registerDojoDateWidget'],
)
actions.register('editAgendaItem', 'portlet', DialogAction,
title=_(u'Edit Agenda Item...'),
description=_(u'Modify agenda item.'),
viewName='edit_concept.html',
dialogName='editAgendaItem',
)
class Events(ConceptView):
@ -317,9 +336,21 @@ class CreateFollowUpEvent(CreateConcept, BaseFollowUpController):
result = super(CreateFollowUpEvent, self).update()
form = self.request.form
toBeAssigned = form.get('cb_select_tasks') or []
for uid in toBeAssigned:
task = util.getObjectForUid(uid)
self.createFollowUpTask(adapted(task))
taskId = newTask = None
workItems = self.view.loopsRoot.getRecordManager()['work']
for id in sorted(toBeAssigned):
if not '.' in id:
taskId = id
task = util.getObjectForUid(id)
newTask = self.createFollowUpTask(adapted(task))
else:
tId, trackId = id.split('.')
if tId == taskId:
track = workItems.get(trackId)
if track is not None:
td = TrackDetails(self.view, track)
newTId = self.view.getUidForObject(newTask)
track.doAction('move', td.personId, task=newTId)
return result
def createFollowUpTask(self, source):
@ -333,12 +364,13 @@ class CreateFollowUpEvent(CreateConcept, BaseFollowUpController):
conceptType=taskType,
title=source.title,
description=source.description,
responsible=source.start,
responsible=source.responsible,
discussion=source.discussion,
consequences=source.consequences)
stask.assignChild(newTask, self.followsPredicate)
for rel in stask.getParentRelations():
if rel.predicate != self.view.typePredicate:
if rel.predicate not in (
self.view.typePredicate, self.followsPredicate):
if rel.first == bevt:
parent = self.object
else:

View file

@ -58,13 +58,14 @@
<form metal:define-macro="create_followup_event" method="post"
id="dialog_form" class="dialog"
dojoType="dijit.form.Form"
tal:define="item nocall:item|nocall:view">
tal:define="item nocall:item|nocall:view;
showState python:True">
<h2 tal:content="view/title"
i18n:translate="" />
<input type="hidden" name="form.action"
tal:attributes="value view/form_action" />
<metal:data use-macro="view/fieldRenderers/fields" />
<h3 i18n:translate="">Tasks</h3>
<br />
<tal:tasks define="report item/reportInstance;
reportView nocall:item;
results reportView/results;

View file

@ -186,7 +186,7 @@ class IAgendaItem(ILoopsAdapter):
missing_value=u'',
required=False)
concequences = schema.Text(
consequences = schema.Text(
title=_(u'label_consequences'),
description=_(u'desc_consequences.'),
default=u'',

View file

@ -60,7 +60,6 @@ class AgendaItem(AdapterBase):
implements(IAgendaItem)
_adapterAttributes = AdapterBase._adapterAttributes
_contextAttributes = list(IAgendaItem)

View file

@ -174,8 +174,8 @@ So we use the PersonWorkItems view, assigning john to the query.
>>> work = PersonWorkItems(query, TestRequest(form=input))
>>> work.listWorkItems()
[<WorkItem ['36', 2, '33', '2009-01-19 09:00', 'planned']:
{'start': 1232352000, 'created': ..., 'title': u'Install Zope',
'creator': '33'}>]
{'title': u'Install Zope', 'created': ..., 'end': 1232352000,
'start': 1232352000, 'creator': '33'}>]
Work Reports
@ -228,7 +228,8 @@ The user interface is a ReportConceptView subclass that is directly associated w
... print
08/12/28 19:00 20:15
{'url': '.../home/.36', 'title': u'loops Development'}
{'url': '.../home/.33', 'title': u'john'} 01:15 00:15 finished
{'url': '.../home/.33', 'title': u'john'} 01:15 00:15
{'icon': 'cybertools.icons/ledgreen.png', 'title': u'finished'}
>>> results.totals.data
{'effort': 900}
@ -248,7 +249,7 @@ Let's start with creating an a event and assigning it a task.
... typeInterface=IEvent)
>>> tAgendaItem = addAndConfigureObject(concepts, Concept, 'agendaitem',
... title=u'AgendaItem', conceptType=concepts.getTypeConcept(),
... typeInterface=IEvent)
... typeInterface=IAgendaItem)
>>> ev01 = addAndConfigureObject(concepts, Concept, 'ev01',
... title=u'loops Meeting', conceptType=tEvent)

View file

@ -33,13 +33,16 @@ from zope.traversing.api import getName, getParent
from cybertools.ajax import innerHtml
from cybertools.browser.action import actions
from cybertools.meta.interfaces import IOptions
from cybertools.organize.interfaces import IWorkItems
from cybertools.organize.work import workItemTypes
from cybertools.tracking.btree import getTimeStamp
from cybertools.util import format
from loops.browser.action import DialogAction
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.party import getPersonForUser
from loops.organize.stateful.browser import StateAction
from loops.organize.tracking.browser import BaseTrackView
@ -60,6 +63,10 @@ class WorkItemDetails(TrackDetails):
""" Render a single work item.
"""
@Lazy
def workItemType(self):
return self.track.getWorkItemType()
@Lazy
def description(self):
return self.track.description
@ -72,13 +79,19 @@ class WorkItemDetails(TrackDetails):
def descriptionFormatted(self):
return format.nl2br(self.description)
@Lazy
def deadline(self):
return self.formatTimeStamp(self.track.deadline, 'date')
@Lazy
def start(self):
return self.formatTimeStamp(self.track.start, 'time')
result = self.formatTimeStamp(self.track.start, 'time')
return result != '00:00' and result or ''
@Lazy
def end(self):
return self.formatTimeStamp(self.track.end, 'time')
result = self.formatTimeStamp(self.track.end, 'time')
return result != '00:00' and result or ''
@Lazy
def duration(self):
@ -340,6 +353,32 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView):
def description(self):
return self.track.description or u''
@Lazy
def workItemType(self):
return self.track.getWorkItemType() or self.workItemTypes[0]
@Lazy
def workItemTypes(self):
task = self.task
if task is None:
task = self.target
options = IOptions(adapted(task.conceptType))
typeNames = options.workitem_types
if typeNames:
return [workItemTypes[name] for name in typeNames]
return workItemTypes
@Lazy
def showTypes(self):
return len(self.workItemTypes) != 1
@Lazy
def deadline(self):
ts = self.track.deadline# or getTimeStamp()
if ts:
return time.strftime('%Y-%m-%d', time.localtime(ts))
return ''
@Lazy
def date(self):
ts = self.track.start or getTimeStamp()
@ -367,11 +406,21 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView):
@Lazy
def actions(self):
result = [dict(name=t.name, title=t.title)
for t in self.track.getAvailableTransitions()]
#if t.name != 'delegate' or
# checkPermission('loops.ManageSite', self.context)]
for t in self.track.getAvailableTransitions()
if t.name in self.workItemType.actions and
t.name not in self.hiddenActions]
#and (t.name != 'delegate' or
# checkPermission('loops.ManageSite', self.context))]
return result
@Lazy
def hiddenActions(self):
task = self.task
if task is None:
task = self.target
options = IOptions(adapted(task.conceptType))
return options.hidden_workitem_actions or []
def getTypesParamsForFilteringSelect(self, types=['person']):
result = []
for t in types:
@ -387,7 +436,14 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView):
return [dict(name=util.getUidForObject(p), title=p.title)
for p in persons]
taskTypes = ['task', 'event']
taskTypes = ['task', 'event', 'agendaitem']
@Lazy
def followUpTask(self):
pred = self.conceptManager.get('follows')
if pred is not None and self.task is not None:
for t in self.task.getChildren([pred]):
return t
@Lazy
def x_tasks(self):
@ -452,17 +508,16 @@ class CreateWorkItem(EditObject, BaseTrackView):
v = form.get(k)
if v:
result[k] = v
for k in ('title', 'description', 'comment'):
for k in ('workItemType', 'title', 'description', 'comment'):
setValue(k)
if action == 'delegate':
setValue('party')
if action == 'move':
setValue('task')
result['deadline'] = parseDate(form.get('deadline'))
startDate = form.get('start_date', '').strip()
startTime = form.get('start_time', '').strip().replace('T', '') or '00:00:00'
endTime = form.get('end_time', '').strip().replace('T', '') or '00:00:00'
#print '***', startDate, startTime, endTime
#if startDate and startTime:
if startDate:
result['start'] = parseDateTime('T'.join((startDate, startTime)))
result['end'] = parseDateTime('T'.join((startDate, endTime)))

View file

@ -115,6 +115,7 @@
name="meeting_minutes.doc"
for="loops.interfaces.IConceptSchema"
class="loops.organize.work.meeting.MeetingMinutesDocument"
attribute="embed"
permission="zope.View" />
<!-- setup -->

View file

@ -4,7 +4,8 @@
<div metal:define-macro="content"
tal:define="report item/reportInstance;
reportView nocall:item;
results reportView/results">
results reportView/results;
showState python:True">
<div tal:attributes="class string:content-$level;">
<metal:block use-macro="view/concept_macros/concepttitle" />
<metal:block use-macro="view/concept_macros/conceptfields" />
@ -19,91 +20,189 @@
</div>
<div metal:define-macro="document"
<metal:doc define-macro="document"
tal:define="item nocall:view;
report item/reportInstance;
reportView nocall:item;
results reportView/results;
fields results/context/fields">
fields results/context/fields;
showCheckboxes nothing;
showState nothing">
<div class ="WordSection1">
<div align="center">
<table class="MsoTableGrid" width="98%" cellpadding="0" cellspacing="0">
<metal:header use-macro="item/macros/header" />
<metal:results use-macro="reportView/resultsRenderer" />
</div>
<metal:header use-macro="item/macros/rows" />
</table>
</div>
</div>
</metal:doc>
<div metal:define-macro="header"
<metal:header define-macro="header"
tal:define="row results/first">
<h1 i18n:translate="">Meeting Minutes</h1>
<h2 tal:define="col fields/eventTitle">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></h2>
<div>
<tr style="background-color: #777777; color: white">
<td colspan="4">
<div tal:define="col fields/eventTitle"
class="MinutesAndAgendaTitles">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></div>
<div tal:define="col fields/eventDescription"
class="MinutesAndAgendaTitles">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></div>
</td>
</tr>
<tr style="background-color: #eeeeee">
<td colspan="2">
<span tal:define="col fields/eventDate">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></span>
</td>
<td colspan="2">
<span tal:define="col fields/eventStart">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></span> -
<span tal:define="col fields/eventEnd">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></span>
</div>
<div tal:define="col fields/eventDescription">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></div>
</div>
</td>
</tr>
<tr>
<td width="20%"
i18n:translate="">Participants</td>
<td colspan="3">
<span tal:define="col fields/participants">
<metal:col use-macro="python:item.getColumnRenderer(col)" /></span>
</td>
</tr>
</metal:header>
<div metal:define-macro="results">
<metal:results define-macro="results">
<table class="report"
tal:define="showCheckboxes cb_name|nothing">
<tr>
<th tal:condition="showCheckboxes"
style="border: 1px solid grey">
<metal:rows define-macro="rows">
<tr tal:condition="showCheckboxes">
<th style="border: 1px solid grey; width: 5%">
<input type="checkbox" checked
tal:attributes="onclick
string:toggleCheckBoxes(this, '$cb_name:list')" /></th>
<th i18n:translate=""
style="border: 1px solid grey">Task/Action</th>
<th style="border: 1px solid grey"
i18n:translate="">Who?</th>
<th style="border: 1px solid grey"
i18n:translate="">When?</th>
<th style="border: 1px solid grey"
i18n:translate=""></th>
<th colspan="4"
i18n:translate=""
style="border: 1px solid grey">Copy Agenda Items</th>
<td tal:condition="showState"
style="border: 1px solid #777777" />
</tr>
<tal:task repeat="row results">
<tr tal:repeat="colname python: ('title', 'description',)">
<tr>
<td tal:condition="showCheckboxes" style="width: 2%">&nbsp;</td>
<td style="width: 15%">&nbsp;</td>
<td style="width: 53%">&nbsp;</td>
<td style="width: 20%">&nbsp;</td>
<td style="width: 12%">&nbsp;</td>
<td tal:condition="showState" style="width: 2%">&nbsp;</td>
</tr>
<tal:tasks repeat="row results">
<tal:task define="taskUid row/context/uid">
<tr class="agenda-item-headline"
style="background-color: #777777">
<td tal:condition="showCheckboxes"
style="border: 1px solid grey">
style="border: 1px solid #777777" class="center">
<input type="checkbox" checked
tal:condition="python:colname == 'title'"
tal:attributes="name string:$cb_name:list;
value row/context/uid" /></td>
value taskUid" /></td>
<td colspan="4"
style="border: 1px solid #777777"
tal:define="col report/fields/title"
tal:attributes="class col/cssClass">
<div class="MinutesAndAgendaTitles">
<metal:column use-macro="python:
reportView.getColumnRenderer(col)" /></div>
</td>
<td tal:condition="showState"
style="border: 1px solid #777777" />
</tr>
<tal:item repeat="colname python:
('responsible', 'description', 'discussion', 'consequences')">
<tr>
<tal:field define="col report/fields/?colname">
<td style="border: 1px solid grey"
tal:define="col report/fields/?colname"
tal:condition="showCheckboxes"></td>
<td style="border: 1px solid grey; width: 15%"
tal:content="col/title"
i18n:translate=""></td>
<td colspan="3"
style="border: 1px solid grey; width: 85%"
tal:attributes="class col/cssClass">
<metal:column use-macro="python:
reportView.getColumnRenderer(col)" />
</td>
<td style="border: 1px solid grey" />
<td style="border: 1px solid grey" />
<td style="border: 1px solid grey" />
</tal:field>
<td tal:condition="showState"
style="border-right: 1px solid #777777" />
</tr>
</tal:item>
<tal:workitems define="col report/fields/workItems;
results python:col.getValue(row)"
condition="results/result">
<tr style="background-color: #eeeeee">
<td style="border: 1px solid grey"
tal:condition="showCheckboxes"></td>
<td colspan="2"
style="border: 1px solid grey; width: 68%"
i18n:translate="">header_workitems</td>
<td style="border: 1px solid grey; width: 20%"
i18n:translate="">header_responsible</td>
<td style="border: 1px solid grey; width: 12%" class="center"
i18n:translate="">header_deadline</td>
<td tal:condition="showState"
style="border: 1px solid #777777" />
</tr>
<tal:workitems define="col report/fields/workItems">
<metal:column use-macro="python:
reportView.getColumnRenderer(col)" />
</tal:workitems>
</tal:task>
</tal:tasks>
</metal:rows>
</table>
</div>
</metal:results>
<div metal:define-macro="subreport"
tal:define="results python:col.getValue(row)">
<metal:sub define-macro="subreport"
tal:define="fields results/displayedColumns">
<tr class="listing" tal:repeat="row results">
<td tal:condition="showCheckboxes"
style="border: 1px solid grey" />
<td tal:repeat="col results/displayedColumns"
style="border: 1px solid grey" class="center">
<input type="checkbox"
tal:define="trackId row/context/name"
tal:attributes="name string:$cb_name:list;
value string:$taskUid.$trackId;
checked row/isActive" />
</td>
<td colspan="2"
tal:define="col fields/title"
tal:attributes="class col/cssClass"
style="border: 1px solid grey">
style="border: 1px solid grey; width: 68%">
<metal:column use-macro="python:
reportView.getColumnRenderer(col)" />
</td>
<td tal:define="col fields/userName"
tal:attributes="class col/cssClass"
style="border: 1px solid grey; width: 20%">
<metal:column use-macro="python:
reportView.getColumnRenderer(col)" />
</td>
<td tal:define="col fields/deadline"
tal:attributes="class col/cssClass"
style="border: 1px solid grey; width: 12%">
<metal:column use-macro="python:
reportView.getColumnRenderer(col)" />
</td>
<tal:state condition="showState">
<td tal:define="col fields/state"
tal:attributes="class col/cssClass"
style="border: 1px solid grey; width: 12%">
<metal:column use-macro="python:
reportView.getColumnRenderer(col)" />
</td>
</tal:state>
</tr>
</div>
</metal:sub>
</html>

View file

@ -66,10 +66,14 @@ class MeetingMinutes(ResultsConceptView):
class MeetingMinutesDocument(WordDocument, MeetingMinutes):
isToplevel = True
omitSectionElement = True
def __init__(self, context, request):
MeetingMinutes.__init__(self, context, request)
def __call__(self, *args, **kw):
return self.embed(*args, **kw)
@Lazy
def macros(self):
return meeting_template.macros

View file

@ -34,7 +34,7 @@ from cybertools.util.format import formatDate
from cybertools.util.jeep import Jeep
from loops.common import adapted, baseObject
from loops.expert.browser.report import ReportConceptView
from loops.expert.field import Field, TargetField, DateField, \
from loops.expert.field import Field, TargetField, DateField, StateField, \
TextField, UrlField
from loops.expert.field import SubReport, SubReportField
from loops.expert.report import ReportInstance
@ -50,13 +50,6 @@ class WorkStatementView(ReportConceptView):
# fields
class StateField(Field):
def getDisplayValue(self, row):
value = self.getValue(row)
return util._(value)
class TrackDateField(Field):
fieldType = 'date'
@ -66,7 +59,7 @@ class TrackDateField(Field):
def getValue(self, row):
value = self.getRawValue(row)
if value is None:
if not value:
return None
return timeStamp2Date(value)
@ -119,6 +112,10 @@ tasks = Field('tasks', u'Tasks',
# work report fields
deadline = TrackDateField('deadline', u'Deadline',
description=u'The day the work has to be finished.',
cssClass='center',
executionSteps=['sort', 'output'])
dayFrom = TrackDateField('dayFrom', u'Start Day',
description=u'The first day from which to select work.',
fieldType='date',
@ -161,6 +158,7 @@ effort = DurationField('effort', u'Effort',
state = StateField('state', u'State',
description=u'The state of the work.',
cssClass='center',
statesDefinition='workItemStates',
executionSteps=['query', 'output'])
@ -278,14 +276,17 @@ class WorkReportInstance(ReportInstance):
class MeetingMinutesWorkRow(WorkRow):
pass
@Lazy
def isActive(self):
return self.context.state not in (
'finished', 'closed', 'cancelled', 'moved')
class MeetingMinutesWork(WorkReportInstance, SubReport):
rowFactory = MeetingMinutesWorkRow
fields = Jeep((workTitle, party, day, state)) #description,
fields = Jeep((workTitle, party, deadline, state)) #description,
defaultOutputFields = fields
defaultSortCriteria = (day,)
states = ('planned', 'accepted', 'running', 'done',
@ -309,13 +310,20 @@ eventTitle = CalculatedField('eventTitle', u'Event Title',
eventDescription = CalculatedField('eventDescription', u'Event Description',
description=u'',
executionSteps=(['header']))
eventDate = DateField('eventDate', u'Event Date',
description=u'',
format=('date', 'short'),
executionSteps=(['header']))
eventStart = DateField('eventStart', u'Event Start',
description=u'',
format=('dateTime', 'short'),
format=('time', 'short'),
executionSteps=(['header']))
eventEnd = DateField('eventEnd', u'Event End',
description=u'',
format=('dateTime', 'short'),
format=('time', 'short'),
executionSteps=(['header']))
participants = CalculatedField('participants', u'Participants',
description=u'',
executionSteps=(['header']))
taskTitle = UrlField('title', u'Task Title',
description=u'The short description of the task.',
@ -325,6 +333,18 @@ taskDescription = TextField('description', u'Description',
description=u'The long description of the task.',
cssClass='header-2',
executionSteps=['output'])
responsible = TextField('responsible', u'label_responsible',
description=u'Responsible.',
cssClass='header-2',
executionSteps=['output'])
discussion = TextField('discussion', u'label_discussion',
description=u'Discussion.',
cssClass='header-2',
executionSteps=['output'])
consequences = TextField('consequences', u'label_consequences',
description=u'Consequences.',
cssClass='header-2',
executionSteps=['output'])
workItems = SubReportField('workItems', u'Work Items',
description=u'A list of work items belonging to the task.',
reportFactory=MeetingMinutesWork,
@ -345,6 +365,10 @@ class TaskRow(BaseRow):
def eventDescription(self):
return self.event.description
@Lazy
def eventDate(self):
return self.event.start
@Lazy
def eventStart(self):
return self.event.start
@ -353,8 +377,13 @@ class TaskRow(BaseRow):
def eventEnd(self):
return self.event.end
@Lazy
def participants(self):
return self.event.participants
useRowProperty = BaseRow.useRowProperty
attributeHandlers = dict(
eventDate=useRowProperty,
eventStart=useRowProperty,
eventEnd=useRowProperty,
)
@ -367,8 +396,10 @@ class MeetingMinutes(WorkReportInstance):
rowFactory = TaskRow
fields = Jeep((eventTitle, eventStart, eventEnd, eventDescription,
tasks, taskTitle, taskDescription, workItems))
fields = Jeep((eventTitle, eventDate, eventStart, eventEnd,
eventDescription, participants,
tasks, taskTitle, responsible, taskDescription,
discussion, consequences, workItems))
defaultOutputFields = fields
states = ('planned', 'accepted', 'done', 'done_x', 'finished')
taskTypeNames = ('agendaitem',)

View file

@ -24,8 +24,8 @@
<td class="headline"
tal:attributes="colspan python: len(work.columns)"
tal:content="row/month">2009-01</td></tr>
<tr tal:attributes="class python:
(repeat['row'].odd() and 'even' or 'odd')">
<tr tal:define="wiType row/workItemType"
tal:attributes="class wiType/indicator|nothing">
<td class="nowrap center"
tal:define="today python: row.isToday and ' today' or ''"
tal:attributes="title row/weekDay;
@ -67,12 +67,29 @@
<metal:block define-macro="create_workitem">
<form method="post" id="addWorkitem_form" class="dialog"
xx_dojoType="dijit.form.Form">
xx_dojoType="dijit.form.Form"
tal:define="workItemTypes view/workItemTypes;
workItemType view/workItemType">
<input type="hidden" name="form.action" value="create_workitem" />
<input type="hidden" name="id"
tal:attributes="value request/form/id|nothing" />
<div class="heading" i18n:translate="">Add Work Item</div>
<div>
<tal:type condition="view/showTypes">
<label i18n:translate="" for="types">Work Item Type</label>
<select name="workItemType" id="types">
<option tal:repeat="type workItemTypes"
tal:attributes="value type/name;
selected python:
type.name == workItemType.name"
tal:content="type/title"
i18n:translate="" />
</select>
</tal:type>
<tal:type condition="not:view/showTypes">
<input type="hidden" name="workItemType"
tal:attributes="value python:workItemTypes[0].name" />
</tal:type>
<label i18n:translate="" for="title">Title</label>
<div>
<input name="title" id="title" style="width: 60em"
@ -108,7 +125,8 @@
name="party" id="input_party"
store="party_search_store" />
</span>
<span id="target_task" style="display: none">&nbsp;
<span id="target_task" style="display: none"
tal:condition="not:view/followUpTask">&nbsp;
<label i18n:translate="move_to_task" for="input_task"
style="display: inline">to</label>
<span dojoType="dojox.data.QueryReadStore" jsId="task_search_store"
@ -121,10 +139,29 @@
name="task" id="input_task"
store="task_search_store" />
</span>
<span id="target_task" style="display: none"
tal:condition="view/followUpTask">&nbsp;
<label i18n:translate="move_to_task" for="input_task"
style="display: inline">to</label>
<span tal:content="view/followUpTask/title" />
<input type="hidden" name="task" id="input_task"
tal:attributes="value python:
view.getUidForObject(view.followUpTask)" />
</span>
</div>
<div>
<label i18n:translate="" for="start-end">Start - End</label>
<div id="start-end">
<div id="deadline"
tal:condition="python:'deadline' in workItemType.fields">
<label i18n:translate="" for="deadline-input">Deadline</label>
<div id="deadline-input">
<input type="text" name="deadline" style="width: 8em"
dojoType="dijit.form.DateTextBox"
tal:attributes="value view/deadline" /></div>
</div>
<div id="start-end"
tal:condition="python:'start-end' in workItemType.fields">
<label i18n:translate="" for="start-end-input">Start - End</label>
<div id="start-end-input">
<input type="text" name="start_date" style="width: 8em"
dojoType="dijit.form.DateTextBox"
tal:attributes="value view/date" />
@ -134,9 +171,13 @@
<input type="text" name="end_time" style="width: 6em"
dojoType="dijit.form.TimeTextBox"
tal:attributes="value view/endTime" /></div>
</div>
<div id="duration-effort"
tal:condition="python:
'duration-effort' in workItemType.fields">
<label i18n:translate=""
for="duration-effort">Duration / Effort (hh:mm)</label>
<div id="duration-effort">
for="duration-effort-input">Duration / Effort (hh:mm)</label>
<div id="duration-effort-input">
<input type="text" name="duration" style="width: 5em"
dojoType="dijit.form.ValidationTextBox"
regexp="-{0,1}[0-9]{1,2}(:[0-5][0-9]){0,1}"
@ -146,6 +187,7 @@
regexp="-{0,1}[0-9]{1,2}(:[0-5][0-9]){0,1}"
tal:attributes="value view/effort" /></div>
</div>
</div>
<div>
<label i18n:translate="" for="comment">Comment</label>
<div>
@ -187,6 +229,10 @@
<td><span i18n:translate="">Task</span>:</td>
<td tal:content="item/object/title"></td>
</tr>
<tr>
<td><span i18n:translate="">Deadline</span>:</td>
<td tal:content="item/deadline"></td>
</tr>
<tr>
<td><span i18n:translate="">Start - End</span>:</td>
<td><span tal:content="item/startDay" />
@ -219,7 +265,8 @@
tal:attributes="href python:view.getUrlForTarget(party)"
tal:content="party/title" /></td>
</tr>
<tr tal:condition="python:state.name in ('moved', 'moved_x')">
<tr tal:condition="python:state.name in ('moved', 'moved_x') and
item.targetWorkItem is not None">
<td><span i18n:translate="">Moved To</span>:</td>
<td><a tal:define="task python:view.getObjectForUid(item.targetWorkItem.taskId)"
tal:attributes="href python:view.getUrlForTarget(task)"