From 4cb27a1e4a1089415540f6a25b5fd1ee02e1024e Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 23 Jul 2012 19:43:00 +0200 Subject: [PATCH 01/16] delay search on relation field (like for other filtering select fields) --- schema/relation_macros.pt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/relation_macros.pt b/schema/relation_macros.pt index f63d31a..1d2a767 100755 --- a/schema/relation_macros.pt +++ b/schema/relation_macros.pt @@ -51,7 +51,7 @@ - \n" "Language-Team: loops developers \n" "MIME-Version: 1.0\n" @@ -296,6 +296,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 +306,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 +318,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" diff --git a/organize/browser/view_macros.pt b/organize/browser/view_macros.pt index b3eb002..e99a3f8 100644 --- a/organize/browser/view_macros.pt +++ b/organize/browser/view_macros.pt @@ -64,7 +64,7 @@ -

Tasks

+
- - - - - -
-

Meeting Minutes

-

-

-
- - - - - -
-
-
-
- - -
- - - - - - - - - - - - - - - - - + +
- Task/ActionWho?When?
- - - - - -
+ +
-
+ + + + + + +
 

Meeting Minutes

Page
+ + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+ + + + + - + + +
Participants + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Copy Agenda Items
+ + +
+ +
header_workitemsheader_responsibleheader_deadline
+ + + + - -
+ diff --git a/organize/work/report.py b/organize/work/report.py index 2cd9628..57aae78 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -285,7 +285,7 @@ class MeetingMinutesWork(WorkReportInstance, SubReport): rowFactory = MeetingMinutesWorkRow - fields = Jeep((workTitle, party, day, state)) #description, + fields = Jeep((workTitle, party, day)) #, state)) #description, defaultOutputFields = fields defaultSortCriteria = (day,) states = ('planned', 'accepted', 'running', 'done', @@ -309,13 +309,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 +332,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 +364,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 +376,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 +395,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',) From ac1968233dfd0f415f36af29926861337f6e744d Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sun, 29 Jul 2012 10:15:19 +0200 Subject: [PATCH 03/16] additional fields workItemType and deadline for work items: control editing and display, e.g. for meeting minutes --- organize/task.py | 1 - organize/work/README.txt | 4 +- organize/work/browser.py | 39 +++++++++++++++--- organize/work/report.py | 8 +++- organize/work/work_macros.pt | 78 +++++++++++++++++++++++++----------- 5 files changed, 96 insertions(+), 34 deletions(-) diff --git a/organize/task.py b/organize/task.py index cdbd172..5f1ad41 100644 --- a/organize/task.py +++ b/organize/task.py @@ -60,7 +60,6 @@ class AgendaItem(AdapterBase): implements(IAgendaItem) - _adapterAttributes = AdapterBase._adapterAttributes _contextAttributes = list(IAgendaItem) diff --git a/organize/work/README.txt b/organize/work/README.txt index bd588e1..49bb1a8 100644 --- a/organize/work/README.txt +++ b/organize/work/README.txt @@ -174,8 +174,8 @@ So we use the PersonWorkItems view, assigning john to the query. >>> work = PersonWorkItems(query, TestRequest(form=input)) >>> work.listWorkItems() [] + {'title': u'Install Zope', 'created': ..., 'end': 1232352000, + 'start': 1232352000, 'creator': '33'}>] Work Reports diff --git a/organize/work/browser.py b/organize/work/browser.py index f0d86fb..2658aa9 100644 --- a/organize/work/browser.py +++ b/organize/work/browser.py @@ -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 @@ -340,6 +343,30 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView): def description(self): return self.track.description or u'' + @Lazy + def workItemType(self): + name = self.track.workItemType + return (name and workItemTypes[name] or self.workItemTypes[0]) + + @Lazy + def workItemTypes(self): + options = IOptions(adapted(self.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,9 +394,10 @@ 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 != 'delegate' or + # checkPermission('loops.ManageSite', self.context))] return result def getTypesParamsForFilteringSelect(self, types=['person']): @@ -452,17 +480,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))) diff --git a/organize/work/report.py b/organize/work/report.py index 57aae78..7ef40c9 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -66,7 +66,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 +119,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', @@ -285,7 +289,7 @@ 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', diff --git a/organize/work/work_macros.pt b/organize/work/work_macros.pt index 0dde42f..a82c69b 100644 --- a/organize/work/work_macros.pt +++ b/organize/work/work_macros.pt @@ -67,12 +67,29 @@
+ xx_dojoType="dijit.form.Form" + tal:define="workItemTypes view/workItemTypes; + workItemType view/workItemType">
Add Work Item
+ + + + + + +
- -
- - - -
- -
- / -
+
+ +
+
+
+
+ +
+ + - +
+
+
+ +
+ / +
+
From c2ee316927aadf88968dfc9c9fcd6d3d9cc66ac4 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sun, 29 Jul 2012 10:23:53 +0200 Subject: [PATCH 04/16] fix option checking for new work item --- organize/work/browser.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/organize/work/browser.py b/organize/work/browser.py index 2658aa9..935c5a5 100644 --- a/organize/work/browser.py +++ b/organize/work/browser.py @@ -350,7 +350,10 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView): @Lazy def workItemTypes(self): - options = IOptions(adapted(self.task.conceptType)) + 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] From c0b68ace67a0a36792e5f18c2c84308cbe71e395 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 31 Jul 2012 17:55:39 +0200 Subject: [PATCH 05/16] improvements and fixes for meeting minutes: views, infos, creating follow-up events, ... --- organize/browser/event.py | 5 +++-- organize/work/browser.py | 13 ++++++++++++- organize/work/work_macros.pt | 21 ++++++++++++++++++--- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/organize/browser/event.py b/organize/browser/event.py index ad9ff99..7245e41 100644 --- a/organize/browser/event.py +++ b/organize/browser/event.py @@ -333,12 +333,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: diff --git a/organize/work/browser.py b/organize/work/browser.py index 935c5a5..b10a057 100644 --- a/organize/work/browser.py +++ b/organize/work/browser.py @@ -75,6 +75,10 @@ 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') @@ -418,7 +422,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): diff --git a/organize/work/work_macros.pt b/organize/work/work_macros.pt index a82c69b..e67654c 100644 --- a/organize/work/work_macros.pt +++ b/organize/work/work_macros.pt @@ -88,7 +88,7 @@ + tal:attributes="value python:workItemTypes[0].name" />
@@ -125,7 +125,8 @@ name="party" id="input_party" store="party_search_store" /> -
-
- - - -
-
+
+ + + +
+
+ -
+ -
 

Meeting Minutes

Page
- +
+
Copy Agenda Items + +   +   +   +   + @@ -126,7 +134,7 @@ header_workitems header_responsible @@ -144,15 +152,27 @@ + tal:define="results python:col.getValue(row); + fields results/displayedColumns"> - + + + + + + + diff --git a/organize/work/meeting.py b/organize/work/meeting.py index 7aaa8a9..7343c2c 100644 --- a/organize/work/meeting.py +++ b/organize/work/meeting.py @@ -66,6 +66,7 @@ class MeetingMinutes(ResultsConceptView): class MeetingMinutesDocument(WordDocument, MeetingMinutes): isToplevel = True + omitSectionElement = True def __init__(self, context, request): MeetingMinutes.__init__(self, context, request) From 96850f110ca02a249912d7bc5e044c6287afe05e Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sat, 4 Aug 2012 18:19:11 +0200 Subject: [PATCH 07/16] indicator attribute depending on work item type: background color for event --- browser/skin/lobo/lobo.css | 6 ++++++ organize/work/browser.py | 13 +++++++++---- organize/work/work_macros.pt | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index 3ce5c26..693b38b 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -510,6 +510,12 @@ img.notselected { text-align: right; } +/* work */ + +.work_event { + background-color: #f3f3ff; +} + /* lobo layout-specific classes */ .legend { diff --git a/organize/work/browser.py b/organize/work/browser.py index b10a057..3d606f9 100644 --- a/organize/work/browser.py +++ b/organize/work/browser.py @@ -63,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 @@ -81,11 +85,13 @@ class WorkItemDetails(TrackDetails): @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): @@ -349,8 +355,7 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView): @Lazy def workItemType(self): - name = self.track.workItemType - return (name and workItemTypes[name] or self.workItemTypes[0]) + return self.track.getWorkItemType() or self.workItemTypes[0] @Lazy def workItemTypes(self): diff --git a/organize/work/work_macros.pt b/organize/work/work_macros.pt index e67654c..0d67bbc 100644 --- a/organize/work/work_macros.pt +++ b/organize/work/work_macros.pt @@ -24,8 +24,8 @@ 2009-01 - +
-
- - - - +
+
+
From c029e988785655ecb61ed66bafc72579b11b3788 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 6 Aug 2012 14:25:07 +0200 Subject: [PATCH 09/16] resources in books: show links to other sections in which this resource is used --- compound/book/browser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compound/book/browser.py b/compound/book/browser.py index ac3e905..f454941 100644 --- a/compound/book/browser.py +++ b/compound/book/browser.py @@ -78,7 +78,7 @@ class SectionView(Base, ConceptView): def getParentsForResource(self, r): for c in r.context.getConcepts([self.defaultPredicate]): - if c.conceptType not in (self.documentTypeType, self.sectionType): + if c != self.context and c.conceptType != self.documentTypeType: yield c From 1b8146b68308bd3923f7fbd0fbc570ee82fbc111 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 7 Aug 2012 18:57:16 +0200 Subject: [PATCH 10/16] meeting minutes: generate Word document by embedding content in .mht file --- browser/loops.css | 6 +++ organize/work/configure.zcml | 1 + organize/work/meeting.pt | 92 +++++++++++++++++------------------- organize/work/meeting.py | 3 ++ 4 files changed, 54 insertions(+), 48 deletions(-) diff --git a/browser/loops.css b/browser/loops.css index 963101c..52b9a53 100644 --- a/browser/loops.css +++ b/browser/loops.css @@ -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 { diff --git a/organize/work/configure.zcml b/organize/work/configure.zcml index 562df3f..5dbdc5b 100644 --- a/organize/work/configure.zcml +++ b/organize/work/configure.zcml @@ -115,6 +115,7 @@ name="meeting_minutes.doc" for="loops.interfaces.IConceptSchema" class="loops.organize.work.meeting.MeetingMinutesDocument" + attribute="embed" permission="zope.View" /> diff --git a/organize/work/meeting.pt b/organize/work/meeting.pt index c31b314..4e80606 100644 --- a/organize/work/meeting.pt +++ b/organize/work/meeting.pt @@ -25,39 +25,31 @@ reportView nocall:item; results reportView/results; fields results/context/fields"> - -
- - - -
+
+
+ + + +
+
- - - - - - - -
 

Meeting Minutes

Page
-
- - - + -
+
-
+
- + @@ -100,7 +92,8 @@   - + - +
+
- - - - - - - + + + + + + + - - - - + + + + header_workitems - header_responsible - header_deadline + i18n:translate="">header_workitems + header_responsible + header_deadline \n" "Language-Team: loops developers \n" "MIME-Version: 1.0\n" @@ -275,6 +275,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..." diff --git a/organize/browser/event.py b/organize/browser/event.py index 7245e41..514c0ed 100644 --- a/organize/browser/event.py +++ b/organize/browser/event.py @@ -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 @@ -317,9 +318,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): diff --git a/organize/work/meeting.pt b/organize/work/meeting.pt index 4e80606..3d5a5c7 100644 --- a/organize/work/meeting.pt +++ b/organize/work/meeting.pt @@ -86,19 +86,21 @@ style="border: 1px solid grey">Copy Agenda Items +           - + + + style="border: 1px solid grey" class="center"> + value taskUid" /> - - - header_workitems - header_responsible - header_deadline - - - + + + + header_workitems + header_responsible + header_deadline + + - + + + tal:define="fields results/displayedColumns"> + style="border: 1px solid grey" class="center"> + + Date: Sun, 19 Aug 2012 18:07:21 +0200 Subject: [PATCH 12/16] provide book overview that lists all sections with descriptions --- compound/book/browser.py | 7 +++++++ compound/book/configure.zcml | 8 ++++++++ compound/book/loops_book_de.dmp | 2 +- compound/book/view_macros.pt | 11 +++++++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/compound/book/browser.py b/compound/book/browser.py index f454941..87a14d1 100644 --- a/compound/book/browser.py +++ b/compound/book/browser.py @@ -51,6 +51,13 @@ class Base(object): return self.nodeView.getViewForTarget(p) +class BookOverview(Base, ConceptView): + + @Lazy + def macro(self): + return book_template.macros['book'] + + class SectionView(Base, ConceptView): @Lazy diff --git a/compound/book/configure.zcml b/compound/book/configure.zcml index 98d0b40..9c5a628 100644 --- a/compound/book/configure.zcml +++ b/compound/book/configure.zcml @@ -17,6 +17,14 @@ + + + + +
+

+

+
+
+ + +
From 61562b6af64f8997bc68d1ee22b50476b5970b36 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sun, 19 Aug 2012 18:18:56 +0200 Subject: [PATCH 13/16] show icon for tabular view --- compound/book/browser.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compound/book/browser.py b/compound/book/browser.py index 87a14d1..d6e2232 100644 --- a/compound/book/browser.py +++ b/compound/book/browser.py @@ -50,6 +50,11 @@ 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): @@ -64,11 +69,6 @@ class SectionView(Base, ConceptView): def macro(self): return book_template.macros['section'] - @Lazy - def tabview(self): - if self.editable: - return 'index.html' - @Lazy def documentTypeType(self): return self.conceptManager['documenttype'] From 6c72358426bbc1862df5e181f2c4cb6bb1419559 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 21 Aug 2012 07:06:36 +0200 Subject: [PATCH 14/16] provide actions for agenda items; show state in meeting minutes browser view --- browser/skin/lobo/lobo.css | 4 ++++ locales/de/LC_MESSAGES/loops.mo | Bin 18719 -> 19020 bytes locales/de/LC_MESSAGES/loops.po | 14 +++++++++++- organize/browser/event.py | 18 ++++++++++++++++ organize/browser/view_macros.pt | 3 ++- organize/work/meeting.pt | 37 ++++++++++++++++++++++++-------- organize/work/report.py | 4 ++-- 7 files changed, 67 insertions(+), 13 deletions(-) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index 693b38b..ec06252 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -597,6 +597,10 @@ div.comment { /* calendar, work items */ +.MinutesAndAgendaTitles a[href] { + color: white; +} + .today { color: #444488; font-weight: bold; diff --git a/locales/de/LC_MESSAGES/loops.mo b/locales/de/LC_MESSAGES/loops.mo index 25e408f49f0556a845b72ad254e0b6e8330e026a..e3d088f6b48f789fe72174d4b72ff9e04eb4986d 100644 GIT binary patch delta 7324 zcmY+|3w+P@9>?+Tu68rGxy)w!v(3zPH@jqG8^&BBBE`~VAv45;Q~ir_se{Vs;xMg3 z31vy4s1u6GF_a{dZYt_1$|dK#-v8fs=gjZX$MgIB{=T>0_xt_*{yV4QX|L*XFZapD zzRL_(KQChZW01QUu!%+7lunET5 z@=R<%J_j}7!8nxh%?J|t6ugG&=r{DkYp9CeQBHXfD&GXvaRfHTF7|#NYT*8;Cmn`7 zp}EI84b}g{*a#P*KjWJfwn8PUgH5)=JE#@dkLvI=YTygl9Q|2WbrgkqvP4w99BhNP zU^q@k?dD=syBAUYZb!E|JVZiE{4J`(Yp9MwT~0n4TaZseJ#jzORup0=PDDNFY}7>N zqxyXYTjF}ufO}9AI)R$VB^T?j8=SzF}dv-bek{|nvo0DpgsCxiY?DTb(n`5 zARqOF12GkgZ259jKPyoWvKDz!%ogN0m?Nk&aLIZdef9qPw{->#Ms?5-HIbG!AA|nn zJE1yAML*0!&%{s@%11rv9jGOrh&s#QS~cqegnF7cs7%$hr3Z7A3#>e z97jEoY3JN;WDQ5vYmHio&KQ8Hn26b^iIk!aWf|)1Jc0atnB^FNo7=JeTAJ+?Xaf6D z`NOD=&tM-si&~ka1gBvR>PZSv1Kft1&?I|*HU^TPYx9dxE3h0@Zv|?C>k?RhEzu?l zG{AOLejjS4pJD)BKsC6Es^{IFgNlv#M=O|xdd~+T2i#0RZPjAbO0Gdopc?gHhfw2u z<|YwD;*7m<9vQ>@Zu3zc7>s-@Y69`7Gtn8FEm0Hdf|}q>sQNuo?fYUN_Q&2h43lsL>H!X* z+8suseKLzIO2JD?A; zd!`um+AT#*a6N|NCRF?PurcmM9oFN?tiS&9IZuH)^hqV89tR_d7b6!HhM`3}{ePgkd1Flyj%)JnKxNoWbvQBN`q^}9aF<`2ITnxhzsQZ&pZ`ET+e{S{e8gxr;t#CA*cz=MpZ0F zE#d2^1_!V?{(uQ+GWiHWdu)Qms4bd>TH-mVCtZYkYc`;^s0y`(+t7#c%||5Eup0Gb zM^UH$JJjC&hU)Mts-Zulx-bmYaaYvDx}ok5Mb$4ttxyTNa1OS_7j5}_==uFWLZTTr zzPC57qqZQ7cTpx_ICe*^$OxM+!xrS1peC>mTjDO%S@{C>`kh1dbIsZ)%cMcn}weN{q zx&GO#zh1vvDA13{NL0f+QSW^*@}DW=A2qB(ZPj+vlYNM7@G!>VC3`=zyVFk`YN8#j zNvMgWp*~P~ZW5Y6KC0uv*4t4t9D}Mj9<{_Js3&|BHNj_4E4UW5g70E0JcQbsUr_A= zdN^C+LQOmwwW97^5~?u5R+xejadHy22#qkx`H#U`kMqmct`l2NbUWYktI#3XzH zTjOC&#*3(lwBwgX11F*;oNn{ItphNU@}cPY{hva@n}Qjr4rZYa(<3&&%(@aa^OtRY zBWj{=q1u0psaTCs=+)c#Lb`AW`H`4_8&MB@tT*eg4$n~Fhd-m9>;h`3V)LEy6jXyu z)FJDIx<4E>a51W%@#uq7QHQt;btaxdZP^Q`dh2cZoB6E2p7=uw^r5ImE!k;QgI`eh zFQW$X>f?0ai-XA5$2=@VJ-}+z#MYoMZb40a8|qN+w)bl=jr?gh2_3o?eVvZePz`#Z z4oyGQM26e^U8s&qP!pYnn!tRlhx<_NYfuyZ64m}I)NA>py?+%ulXv?UI30FHHOxbG zG|)N>{mB>F`(rVH{A5&zGi~`o)P%}WTTqEwku}!WP+PGX)&B>MZnKYsp0ozFWG7J* zIfLrxJZjG`peEwm&*>l-)vk%nM`IxQc+@~i7=*d#g)b1gN(g?3JlDf||7)+|B$g5r zi1h?pZrT!MgwDn*gkHnitH>twi`SeH-oqx?jz}Txhr0AFxJwDHR<`V2()`GpSdHJ4 zc$;WuZ!%#MWz&Cw2H~H_^e1^)Dy!1+k0hNreRBb<%GTFB7`-Q`&;^{;0#afq0N;PlQme zOJB_S#2jVpH3}<k1}aj0ZhA=btPXKzvL1J4I#&x_eQ1nk|SRT|oM@@}!5tO*jR!iIqeK@gz}u zRgq{)9Y1`C_=M1hMOQ=0k2*=u56)AhlZiN625$4%|1)NvP3!%=Ol%`g6716duCGXa zWOJXPz5#6+=oVXc9Xpc`B6MAH@cd8usW$zHHBkGnt2N(GbzfQTpRUa7Xe3xD87U*f#R?`mtE!cN@uw-s~A=MmvHKbiEG z#EV4j^-mJt5NSj-^^V{j#9-nIq3c-(&o66l>Ws7bAJM&ue;&03d+@rrC*h!^VU!aWCa-h`$r9$wv~`NPkUq(9U)t;X`34E+86_uDvFb z$f=W8`7gCMt$)L-M3l{YQ~x>ApW6JJ*8iZ3c#X)leg37BwV8qlqD`HFc9U;LR1>;p z5&MZO>WsvWwr(`(x>q2XczbUM>7R+ǻhTOG`Ac$|nUr1JA55^bZ6w!&Uav1Omz zGLb~t$9RJ1Lb{KwX;zRET$Qky9{1 z1=C#vy>F=TcAG+<8)}5Ky%^}7oLJ?P_@r;xw8{64EiEe@H*HE;>BQ--qSBJ$aZ49x zHmj;g&GHVZdv|GKMtIe+v|ZkzbxS8suX-#q*EjseGS`^mqWi`aGojL|_p|Q|{x5I} B;bs5; delta 7117 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 diff --git a/locales/de/LC_MESSAGES/loops.po b/locales/de/LC_MESSAGES/loops.po index 4336297..9e1801a 100644 --- a/locales/de/LC_MESSAGES/loops.po +++ b/locales/de/LC_MESSAGES/loops.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: $Id$\n" "POT-Creation-Date: 2007-05-22 12:00 CET\n" -"PO-Revision-Date: 2012-08-09 12:00 CET\n" +"PO-Revision-Date: 2012-08-20 12:00 CET\n" "Last-Translator: Helmut Merz \n" "Language-Team: loops developers \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..." diff --git a/organize/browser/event.py b/organize/browser/event.py index 514c0ed..90f0f76 100644 --- a/organize/browser/event.py +++ b/organize/browser/event.py @@ -80,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): diff --git a/organize/browser/view_macros.pt b/organize/browser/view_macros.pt index e99a3f8..9eb86b9 100644 --- a/organize/browser/view_macros.pt +++ b/organize/browser/view_macros.pt @@ -58,7 +58,8 @@ + tal:define="item nocall:item|nocall:view; + showState python:True">

+ results reportView/results; + showState python:True">
@@ -24,11 +25,12 @@ report item/reportInstance; reportView nocall:item; results reportView/results; - fields results/context/fields"> + fields results/context/fields; + showCheckboxes nothing; + showState nothing">
- +
@@ -39,7 +41,7 @@ - +
@@ -84,6 +86,8 @@ Copy Agenda Items +   @@ -91,24 +95,27 @@       +   + style="border: 1px solid #777777" class="center">
+ @@ -125,7 +132,9 @@ - + + header_responsible header_deadline + -
@@ -182,6 +193,14 @@ + + + + + diff --git a/organize/work/report.py b/organize/work/report.py index 15bc425..537e32f 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -285,14 +285,14 @@ class MeetingMinutesWorkRow(WorkRow): @Lazy def isActive(self): return self.context.state not in ( - 'finished', 'closed', 'cancelled') + 'finished', 'closed', 'cancelled', 'moved') class MeetingMinutesWork(WorkReportInstance, SubReport): rowFactory = MeetingMinutesWorkRow - fields = Jeep((workTitle, party, deadline)) #, state)) #description, + fields = Jeep((workTitle, party, deadline, state)) #description, defaultOutputFields = fields defaultSortCriteria = (day,) states = ('planned', 'accepted', 'running', 'done', From e054d4322189148826b4ecd6b2a39d810993989c Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sat, 25 Aug 2012 10:08:56 +0200 Subject: [PATCH 15/16] provide state field for reports, use in meeting minutes --- expert/browser/results.pt | 12 +++++++++++- expert/field.py | 18 ++++++++++++++++++ organize/work/README.txt | 3 ++- organize/work/report.py | 10 ++-------- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/expert/browser/results.pt b/expert/browser/results.pt index 7d30c4f..2fd8129 100644 --- a/expert/browser/results.pt +++ b/expert/browser/results.pt @@ -68,9 +68,19 @@ + + + + + + + -
diff --git a/expert/field.py b/expert/field.py index 2295771..2ff64af 100644 --- a/expert/field.py +++ b/expert/field.py @@ -27,6 +27,7 @@ from zope.schema.interfaces import IVocabularyFactory, IContextSourceBinder from cybertools.composer.report.field import Field as BaseField 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 @@ -100,6 +101,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 diff --git a/organize/work/README.txt b/organize/work/README.txt index 49bb1a8..bc9b5d4 100644 --- a/organize/work/README.txt +++ b/organize/work/README.txt @@ -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} diff --git a/organize/work/report.py b/organize/work/report.py index 537e32f..1d15e8f 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -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' @@ -165,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']) From 2d121fe028bb1cb643e6a9a2602b56ad8f8de3d9 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sat, 25 Aug 2012 11:34:59 +0200 Subject: [PATCH 16/16] allow hiding actions via type options --- organize/work/browser.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/organize/work/browser.py b/organize/work/browser.py index 3d606f9..de90159 100644 --- a/organize/work/browser.py +++ b/organize/work/browser.py @@ -407,11 +407,20 @@ class CreateWorkItemForm(ObjectForm, BaseTrackView): def actions(self): result = [dict(name=t.name, title=t.title) for t in self.track.getAvailableTransitions() - if t.name in self.workItemType.actions] + 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: