merge branch master
This commit is contained in:
commit
153f0ee0e7
14 changed files with 558 additions and 11 deletions
|
@ -276,7 +276,8 @@ class ConceptView(BaseView):
|
|||
if r.order != pos:
|
||||
r.order = pos
|
||||
|
||||
def getChildren(self, topLevelOnly=True, sort=True, noDuplicates=True):
|
||||
def getChildren(self, topLevelOnly=True, sort=True, noDuplicates=True,
|
||||
useFilter=True):
|
||||
form = self.request.form
|
||||
#if form.get('loops.viewName') == 'index.html' and self.editable:
|
||||
if self.editable:
|
||||
|
@ -311,10 +312,12 @@ class ConceptView(BaseView):
|
|||
break
|
||||
if skip:
|
||||
continue
|
||||
if useFilter:
|
||||
options = IOptions(adapted(r.predicate), None)
|
||||
if options is not None and options('hide_children'):
|
||||
continue
|
||||
if fv.check(r.context):
|
||||
if not fv.check(r.context):
|
||||
continue
|
||||
yield r
|
||||
|
||||
def checkCriteria(self, relation, criteria):
|
||||
|
@ -347,9 +350,10 @@ class ConceptView(BaseView):
|
|||
return result
|
||||
|
||||
def isHidden(self, pr):
|
||||
if (getName(pr.second.conceptType) in
|
||||
IOptions(adapted(pr.predicate))('hide_parents_for', [])):
|
||||
#IOptions(pr.predicate)('hide_parents_for', [])):
|
||||
predOptions = IOptions(adapted(pr.predicate))
|
||||
if predOptions('hide_parents'):
|
||||
return True
|
||||
if (getName(pr.second.conceptType) in predOptions('hide_parents_for', [])):
|
||||
return True
|
||||
hideRoles = None
|
||||
options = component.queryAdapter(adapted(pr.first), IOptions)
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
</div>
|
||||
<div metal:use-macro="views/relation_macros/clients" />
|
||||
|
||||
<div tal:define="items view/children;
|
||||
<div tal:define="items python:
|
||||
view.getChildren(topLevelOnly=False, useFilter=False);
|
||||
action string:remove;
|
||||
qualifier string:children;
|
||||
summary string:Currently assigned objects;
|
||||
|
|
|
@ -159,11 +159,20 @@ class BasePart(Base):
|
|||
gridPattern = []
|
||||
showImage = True
|
||||
|
||||
@Lazy
|
||||
def childPredicates(self):
|
||||
preds = [self.defaultPredicate]
|
||||
for name in ['ispartof', 'hasoverview']:
|
||||
pred = self.conceptManager.get(name)
|
||||
if pred is not None:
|
||||
preds.append(pred)
|
||||
return preds
|
||||
|
||||
def getChildren(self):
|
||||
subtypeNames = (self.params.get('subtypes') or [''])[0].split(',')
|
||||
subtypes = [self.conceptManager[st] for st in subtypeNames if st]
|
||||
result = []
|
||||
childRels = self.context.getChildRelations([self.defaultPredicate])
|
||||
childRels = self.context.getChildRelations(self.childPredicates)
|
||||
if subtypes:
|
||||
childRels = [r for r in childRels
|
||||
if r.second.conceptType in subtypes]
|
||||
|
|
|
@ -219,6 +219,25 @@ Query Concepts and Query Views
|
|||
>>> from loops.expert.browser.base import BaseQueryView
|
||||
|
||||
|
||||
Reports
|
||||
=======
|
||||
|
||||
>>> from loops.expert.report import IReport, Report
|
||||
>>> component.provideAdapter(Report, provides=IReport)
|
||||
|
||||
>>> report = Report(None)
|
||||
|
||||
>>> from loops.expert.report import IReportInstance, DefaultConceptReportInstance
|
||||
>>> component.provideAdapter(DefaultConceptReportInstance,
|
||||
... provides=IReportInstance,
|
||||
... name='default_concept_report')
|
||||
|
||||
>>> from loops.expert.report import ReportTypeSourceList
|
||||
>>> source = ReportTypeSourceList(report)
|
||||
>>> list(source)
|
||||
[(u'default_concept_report', u'Default Concept Report')]
|
||||
|
||||
|
||||
Fin de partie
|
||||
=============
|
||||
|
||||
|
|
|
@ -54,4 +54,25 @@
|
|||
factory="loops.expert.browser.search.ActionExecutor"
|
||||
permission="zope.ManageContent" />
|
||||
|
||||
<!-- reporting -->
|
||||
|
||||
<zope:adapter
|
||||
factory="loops.browser.common.SimpleTerms"
|
||||
for="loops.expert.report.ReportTypeSourceList
|
||||
zope.publisher.interfaces.browser.IBrowserRequest" />
|
||||
|
||||
<zope:adapter
|
||||
name="report.html"
|
||||
for="loops.interfaces.IConcept
|
||||
zope.publisher.interfaces.browser.IBrowserRequest"
|
||||
provides="zope.interface.Interface"
|
||||
factory="loops.expert.browser.report.ReportView"
|
||||
permission="zope.View" />
|
||||
|
||||
<browser:page
|
||||
name="results.html"
|
||||
for="loops.interfaces.INode"
|
||||
class="loops.expert.browser.report.ResultsView"
|
||||
permission="zope.View" />
|
||||
|
||||
</configure>
|
||||
|
|
22
expert/browser/report.pt
Normal file
22
expert/browser/report.pt
Normal file
|
@ -0,0 +1,22 @@
|
|||
<html i18n:domain="loops">
|
||||
|
||||
|
||||
<div metal:define-macro="main">
|
||||
<div tal:attributes="class string:content-$level;">
|
||||
<metal:block use-macro="view/concept_macros/concepttitle" />
|
||||
<form method="post" name="report_data" action="results.html">
|
||||
<tal:hidden define="params item/dynamicParams">
|
||||
<input type="hidden"
|
||||
tal:repeat="name params"
|
||||
tal:attributes="name name;
|
||||
value params/?name" /></tal:hidden>
|
||||
<div metal:define-macro="buttons">
|
||||
<input type="submit" name="report_execute" value="Execute Report"
|
||||
i18n:attributes="value" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</html>
|
107
expert/browser/report.py
Normal file
107
expert/browser/report.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
#
|
||||
# Copyright (c) 2011 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
|
||||
#
|
||||
|
||||
"""
|
||||
View classes for reporting.
|
||||
"""
|
||||
|
||||
from urllib import urlencode
|
||||
from zope import interface, component
|
||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.traversing.api import getName, getParent
|
||||
|
||||
from cybertools.browser.form import FormController
|
||||
from loops.browser.concept import ConceptView
|
||||
from loops.browser.node import NodeView
|
||||
from loops.common import adapted, AdapterBase
|
||||
from loops.expert.report import IReportInstance
|
||||
from loops.organize.personal.browser.filter import FilterView
|
||||
from loops.security.common import canWriteObject, checkPermission
|
||||
from loops import util
|
||||
from loops.util import _
|
||||
|
||||
|
||||
report_template = ViewPageTemplateFile('report.pt')
|
||||
results_template = ViewPageTemplateFile('results.pt')
|
||||
|
||||
|
||||
class ReportView(ConceptView):
|
||||
|
||||
@Lazy
|
||||
def report_macros(self):
|
||||
return self.controller.getTemplateMacros('report', report_template)
|
||||
|
||||
@Lazy
|
||||
def macro(self):
|
||||
return self.report_macros['main']
|
||||
|
||||
@Lazy
|
||||
def dynamicParams(self):
|
||||
return self.request.form
|
||||
|
||||
|
||||
class ResultsView(NodeView):
|
||||
|
||||
@Lazy
|
||||
def result_macros(self):
|
||||
return self.controller.getTemplateMacros('results', results_template)
|
||||
|
||||
@Lazy
|
||||
def macro(self):
|
||||
return self.result_macros['main']
|
||||
|
||||
@Lazy
|
||||
def item(self):
|
||||
return self
|
||||
|
||||
@Lazy
|
||||
def params(self):
|
||||
params = dict(self.request.form)
|
||||
if 'report_execute' in params:
|
||||
del params['report_execute']
|
||||
return params
|
||||
|
||||
@Lazy
|
||||
def report(self):
|
||||
return adapted(self.virtualTargetObject)
|
||||
|
||||
@Lazy
|
||||
def reportInstance(self):
|
||||
instance = component.getAdapter(self.report, IReportInstance,
|
||||
name=self.report.reportType)
|
||||
instance.request = self.request
|
||||
return instance
|
||||
|
||||
#@Lazy
|
||||
def results(self):
|
||||
return self.reportInstance.getResults(self.params)
|
||||
|
||||
@Lazy
|
||||
def resultsRenderer(self):
|
||||
return self.reportInstance.getResultsRenderer('results', self.result_macros)
|
||||
|
||||
@Lazy
|
||||
def reportUrl(self):
|
||||
url = self.virtualTargetUrl
|
||||
return '?'.join((url, urlencode(self.params)))
|
||||
|
||||
@Lazy
|
||||
def displayedColumns(self):
|
||||
return self.reportInstance.getActiveOutputFields()
|
||||
|
22
expert/browser/results.pt
Normal file
22
expert/browser/results.pt
Normal file
|
@ -0,0 +1,22 @@
|
|||
<html i18n:domain="loops">
|
||||
|
||||
|
||||
<div metal:define-macro="main"
|
||||
tal:define="item nocall:item/virtualTarget;
|
||||
report view/reportInstance">
|
||||
<div tal:attributes="class string:content-$level;">
|
||||
<metal:block use-macro="view/concept_macros/concepttitle" />
|
||||
</div>
|
||||
<div>
|
||||
<a tal:attributes="href view/reportUrl">Back to the report definition</a>
|
||||
</div>
|
||||
<div metal:use-macro="view/resultsRenderer" />
|
||||
</div>
|
||||
|
||||
|
||||
<div metal:define-macro="results">
|
||||
Default Results Listing
|
||||
</div>
|
||||
|
||||
|
||||
</html>
|
|
@ -16,6 +16,35 @@
|
|||
<adapter factory="loops.expert.setup.SetupManager"
|
||||
name="expert" />
|
||||
|
||||
<!-- reporting -->
|
||||
|
||||
<adapter factory="loops.expert.report.Report"
|
||||
provides="loops.expert.report.IReport" trusted="True" />
|
||||
<class class="loops.expert.report.Report">
|
||||
<require permission="zope.View"
|
||||
interface="loops.expert.report.IReportSchema" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="loops.expert.report.IReportSchema" />
|
||||
</class>
|
||||
|
||||
<adapter factory="loops.expert.report.DefaultConceptReportInstance"
|
||||
name="default_concept_report"
|
||||
provides="loops.expert.report.IReportInstance"
|
||||
trusted="True" />
|
||||
<class class="loops.expert.report.DefaultConceptReportInstance">
|
||||
<require permission="zope.View"
|
||||
interface="loops.expert.report.IReportInstance" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="loops.expert.report.IReportInstance" />
|
||||
</class>
|
||||
|
||||
<utility
|
||||
provides="zope.schema.interfaces.IVocabularyFactory"
|
||||
component="loops.expert.report.ReportTypeSourceList"
|
||||
name="loops.expert.reportTypeSource" />
|
||||
|
||||
<!-- includes -->
|
||||
|
||||
<include package=".browser" />
|
||||
|
||||
</configure>
|
||||
|
|
143
expert/report.py
Normal file
143
expert/report.py
Normal file
|
@ -0,0 +1,143 @@
|
|||
#
|
||||
# Copyright (c) 2011 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
|
||||
#
|
||||
|
||||
"""
|
||||
Report type, report concept adapter, and other reporting stuff.
|
||||
"""
|
||||
|
||||
from zope import schema, component
|
||||
from zope.component import adapts
|
||||
from zope.interface import Interface, Attribute, implements
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.security.proxy import removeSecurityProxy
|
||||
|
||||
from cybertools.composer.report.base import Report as BaseReport
|
||||
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
|
||||
from cybertools.composer.report.interfaces import IReport as IBaseReport
|
||||
from cybertools.composer.report.result import ResultSet, Row
|
||||
from cybertools.util.jeep import Jeep
|
||||
from loops.common import AdapterBase
|
||||
from loops.interfaces import ILoopsAdapter
|
||||
from loops.type import TypeInterfaceSourceList
|
||||
from loops import util
|
||||
from loops.util import _
|
||||
|
||||
|
||||
# interfaces
|
||||
|
||||
class IReport(ILoopsAdapter):
|
||||
""" The report adapter for the persistent object (concept) that stores
|
||||
the report in the concept map.
|
||||
"""
|
||||
|
||||
reportType = schema.Choice(
|
||||
title=_(u'Report Type'),
|
||||
description=_(u'The type of the report.'),
|
||||
default=None,
|
||||
source='loops.expert.reportTypeSource',
|
||||
required=True)
|
||||
|
||||
|
||||
class IReportSchema(IBaseReport, IReport):
|
||||
""" All report attributes - use for security declarations.
|
||||
"""
|
||||
|
||||
|
||||
class IReportInstance(Interface):
|
||||
""" The report-type-specific object (an adapter on the report) that
|
||||
does the real report execution stuff.
|
||||
"""
|
||||
|
||||
label = Attribute('The user-friendly label of the report type specified '
|
||||
'by this instance class.')
|
||||
|
||||
|
||||
# report concept adapter and instances
|
||||
|
||||
class Report(AdapterBase):
|
||||
|
||||
implements(IReport)
|
||||
|
||||
_contextAttributes = list(IReport)
|
||||
|
||||
TypeInterfaceSourceList.typeInterfaces += (IReport,)
|
||||
|
||||
|
||||
class ReportInstance(BaseReport):
|
||||
|
||||
implements(IReportInstance)
|
||||
adapts(IReport)
|
||||
|
||||
rowFactory = Row
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
def getResultsRenderer(self, name, macros):
|
||||
return macros[name]
|
||||
|
||||
def getResults(self, dynaParams=None):
|
||||
crit = self.queryCriteria
|
||||
if crit is None:
|
||||
return []
|
||||
for k, v in dynaParams.items():
|
||||
if k in crit.parts.keys():
|
||||
crit.parts[k].value = v
|
||||
parts = Jeep(crit.parts)
|
||||
result = list(self.selectObjects(parts)) # may modify parts
|
||||
qc = CompoundQueryCriteria(parts)
|
||||
return ResultSet(self, result, rowFactory=self.rowFactory,
|
||||
sortCriteria=self.getSortCriteria(), queryCriteria=qc)
|
||||
|
||||
def selectObjects(self, parts):
|
||||
return []
|
||||
|
||||
|
||||
class ReportTypeSourceList(object):
|
||||
|
||||
implements(schema.interfaces.IIterableSource)
|
||||
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.reportTypes)
|
||||
|
||||
def __contains__(self, value):
|
||||
return value in [item[0] for item in self]
|
||||
|
||||
@Lazy
|
||||
def reportTypes(self):
|
||||
result = []
|
||||
for item in component.getAdapters([self.context], IReportInstance):
|
||||
name, adapter = item
|
||||
adapter = removeSecurityProxy(adapter)
|
||||
label = getattr(adapter, 'label', name)
|
||||
result.append((name, label,))
|
||||
return result
|
||||
|
||||
def __len__(self):
|
||||
return len(self.reportTypes)
|
||||
|
||||
|
||||
# default concept report
|
||||
|
||||
class DefaultConceptReportInstance(ReportInstance):
|
||||
|
||||
label = u'Default Concept Report'
|
||||
|
|
@ -180,6 +180,16 @@ So we use the PersonWorkItems view, assigning john to the query.
|
|||
'creator': '33'}>]
|
||||
|
||||
|
||||
Work Reports
|
||||
============
|
||||
|
||||
>>> from loops.organize.work.report import WorkReportInstance
|
||||
>>> from loops.expert.report import IReportInstance
|
||||
>>> component.provideAdapter(WorkReportInstance,
|
||||
... provides=IReportInstance,
|
||||
... name='work_report')
|
||||
|
||||
|
||||
Fin de partie
|
||||
=============
|
||||
|
||||
|
|
|
@ -73,6 +73,19 @@
|
|||
class="loops.organize.work.browser.WorkItemInfo"
|
||||
permission="zope.View" />
|
||||
|
||||
<!-- reporting -->
|
||||
|
||||
<zope:adapter factory="loops.organize.work.report.WorkReportInstance"
|
||||
name="work_report"
|
||||
provides="loops.expert.report.IReportInstance"
|
||||
trusted="True" />
|
||||
<zope:class class="loops.organize.work.report.WorkReportInstance">
|
||||
<require permission="zope.View"
|
||||
interface="loops.expert.report.IReportInstance" />
|
||||
<require permission="zope.ManageContent"
|
||||
set_schema="loops.expert.report.IReportInstance" />
|
||||
</zope:class>
|
||||
|
||||
<!-- setup -->
|
||||
|
||||
<zope:adapter factory="loops.organize.work.setup.SetupManager"
|
||||
|
|
86
organize/work/report.py
Normal file
86
organize/work/report.py
Normal file
|
@ -0,0 +1,86 @@
|
|||
#
|
||||
# Copyright (c) 2011 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
|
||||
#
|
||||
|
||||
"""
|
||||
Work report definitions.
|
||||
"""
|
||||
|
||||
from zope.app.pagetemplate import ViewPageTemplateFile
|
||||
from zope.cachedescriptors.property import Lazy
|
||||
from zope.component import adapter
|
||||
|
||||
from cybertools.composer.report.base import Report
|
||||
from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCriteria
|
||||
from cybertools.composer.report.field import Field
|
||||
from cybertools.composer.report.result import ResultSet, Row as BaseRow
|
||||
from cybertools.util.jeep import Jeep
|
||||
from loops.expert.report import ReportInstance
|
||||
|
||||
results_template = ViewPageTemplateFile('results.pt')
|
||||
|
||||
|
||||
task = Field('task', u'Task',
|
||||
description=u'The task to which work items belong.',
|
||||
executionSteps=['query', 'output', 'sort'])
|
||||
work = Field('work', u'Work',
|
||||
description=u'The short description of the work.',
|
||||
executionSteps=['output'])
|
||||
workDescription = Field('workDescription', u'Work Description',
|
||||
description=u'The long description of the work.',
|
||||
executionSteps=['output'])
|
||||
day = Field('day', u'Day',
|
||||
description=u'The day the work was done.',
|
||||
executionSteps=['output', 'sort'])
|
||||
dayFrom = Field('dayFrom', u'Start Day',
|
||||
description=u'The first day from which to select work.',
|
||||
executionSteps=['query'])
|
||||
dayTo = Field('dayTo', u'End Day',
|
||||
description=u'The last day until which to select work.',
|
||||
executionSteps=['query'])
|
||||
|
||||
|
||||
class WorkRow(BaseRow):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class WorkReportInstance(ReportInstance):
|
||||
|
||||
type = "deliverables"
|
||||
label = u'Work Report'
|
||||
|
||||
rowFactory = WorkRow
|
||||
fields = Jeep((day, dayFrom, dayTo, task, work, workDescription))
|
||||
defaultOutputFields = fields
|
||||
|
||||
@Lazy
|
||||
def queryCriteria(self):
|
||||
# TODO: take from persistent report where appropriate
|
||||
crit = [LeafQueryCriteria(f.name, f.operator, None, f)
|
||||
for f in self.getAllQueryFields()]
|
||||
return CompoundQueryCriteria(crit)
|
||||
|
||||
def getResultsRenderer(self, name, defaultMacros):
|
||||
return results_template.macros[name]
|
||||
|
||||
def selectObjects(self, parts):
|
||||
task = parts.get('task')
|
||||
if not task:
|
||||
return []
|
||||
return []
|
||||
|
61
organize/work/results.pt
Normal file
61
organize/work/results.pt
Normal file
|
@ -0,0 +1,61 @@
|
|||
<html i18n:domain="loops">
|
||||
|
||||
|
||||
<div metal:define-macro="results">
|
||||
<h2 i18n:translate="">Work Items</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th tal:repeat="col view/displayedColumns"
|
||||
tal:content="col/title"
|
||||
i18n:translate="" />
|
||||
</tr>
|
||||
<tr tal:repeat="row view/results">
|
||||
<tal:column repeat="col view/displayedColumns">
|
||||
<metal:column use-macro="python: view.getColumnRenderer(col.renderer)" />
|
||||
</tal:column>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<div metal:define-macro="xx_results">
|
||||
Work Items Listing
|
||||
<table class="listing">
|
||||
<tr>
|
||||
<tal:colheader repeat="column work/allColumns">
|
||||
<th tal:condition="python: column in work.columns"
|
||||
tal:content="column"
|
||||
i18n:translate="">Task</th>
|
||||
</tal:colheader>
|
||||
</tr>
|
||||
<tal:workitem tal:repeat="row work/listWorkItems">
|
||||
<tr tal:condition="row/monthChanged">
|
||||
<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')">
|
||||
<td class="nowrap center"
|
||||
tal:define="today python: row.isToday and ' today' or ''"
|
||||
tal:attributes="title row/weekDay;
|
||||
class string:nowrap center$today"
|
||||
i18n:attributes="title"
|
||||
tal:content="row/day">2007-03-30</td>
|
||||
<td class="nowrap center" tal:content="row/start">17:30</td>
|
||||
<td class="nowrap center" tal:content="row/end">20:00</td>
|
||||
<td class="nowrap center" tal:content="row/duration">2:30</td>
|
||||
<td tal:condition="python: 'Task' in work.columns">
|
||||
<a tal:attributes="href row/objectData/url"
|
||||
tal:content="row/objectData/title">Task</a></td>
|
||||
<td tal:condition="python: 'User' in work.columns">
|
||||
<a tal:attributes="href row/user/url"
|
||||
tal:content="row/user/title">John</a></td>
|
||||
<td tal:content="row/track/title"
|
||||
tal:attributes="title row/descriptionPlain">Title</td>
|
||||
</tr>
|
||||
</tal:workitem>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
</html>
|
Loading…
Add table
Reference in a new issue