From a2bc7f570dbc2a850e1881e1cc172de3413b7764 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sat, 19 Nov 2011 10:19:34 +0100 Subject: [PATCH 01/22] work in progress: breadcrumbs --- browser/node.py | 31 +++++++++++++++++++++---------- browser/skin/lobo/body.pt | 8 ++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/browser/node.py b/browser/node.py index 2ac755c..9c4cc9b 100644 --- a/browser/node.py +++ b/browser/node.py @@ -18,14 +18,11 @@ """ View class for Node objects. - -$Id$ """ from urlparse import urlparse, urlunparse from zope import component, interface, schema from zope.cachedescriptors.property import Lazy -from zope.app import zapi from zope.annotation.interfaces import IAnnotations from zope.app.catalog.interfaces import ICatalog from zope.app.container.browser.contents import JustContents @@ -39,8 +36,11 @@ from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent from zope.lifecycleevent import Attributes from zope.formlib.form import Form, FormFields from zope.proxy import removeAllProxies +from zope.publisher.defaultview import getDefaultViewName from zope.security import canAccess, canWrite, checkPermission from zope.security.proxy import removeSecurityProxy +from zope.traversing.api import getParent, getParents, getPath +from zope.traversing.browser import absoluteURL from cybertools.ajax import innerHtml from cybertools.browser import configurator @@ -91,6 +91,17 @@ class NodeView(BaseView): self.recordAccess() return result + def breadcrumbs(self): + if not self.globalOptions('showBreadcrumbs'): + return [] + menu = self.menu + data = [dict(label=menu.title, url=menu.url)] + menuItem = self.nearestMenuItem + if menuItem != menu.context: + data.append(dict(label=menuItem.title, + url=absoluteURL(menuItem, self.request))) + return data + def recordAccess(self, viewName=''): target = self.virtualTargetObject targetUid = target is not None and util.getUidForObject(target) or '' @@ -243,7 +254,7 @@ class NodeView(BaseView): return u'' if text.startswith('<'): # seems to be HTML return text - source = zapi.createObject(self.context.contentType, text) + source = component.createObject(self.context.contentType, text) view = component.getMultiAdapter((removeAllProxies(source), self.request)) return view.render() @@ -316,7 +327,7 @@ class NodeView(BaseView): menu = self.menuObject parentMenu = None while menu is not None: - parent = zapi.getParent(menu) + parent = getParent(menu) if INode.providedBy(parent): parentMenu = parent.getMenu() if parentMenu is None or parentMenu is menu: @@ -348,7 +359,7 @@ class NodeView(BaseView): @Lazy def parents(self): - return zapi.getParents(self.context) + return getParents(self.context) @Lazy def nearestMenuItem(self): @@ -413,7 +424,7 @@ class NodeView(BaseView): target = self.virtualTargetObject if target is not None: # zope.app.publisher.browser - name = zapi.getDefaultViewName(target, self.request) + name = getDefaultViewName(target, self.request) return self.targetView(name) return u'' @@ -732,8 +743,8 @@ class ConfigureView(NodeView): container = type.defaultContainer name = form.get('create.name', '') if not name: - viewManagerPath = zapi.getPath(root.getViewManager()) - name = zapi.getPath(self.context)[len(viewManagerPath)+1:] + viewManagerPath = getPath(root.getViewManager()) + name = getPath(self.context)[len(viewManagerPath)+1:] name = name.replace('/', '.') # check for duplicates: num = 1 @@ -850,7 +861,7 @@ class NodeViewConfigurator(configurator.AnnotationViewConfigurator): @property def viewProperties(self): result = [] - for p in list(reversed(zapi.getParents(self.context))) + [self.context]: + for p in list(reversed(getParents(self.context))) + [self.context]: if not INode.providedBy(p) or p.nodeType != 'menu': continue ann = IAnnotations(p) diff --git a/browser/skin/lobo/body.pt b/browser/skin/lobo/body.pt index 9d1a66c..6f8f83c 100644 --- a/browser/skin/lobo/body.pt +++ b/browser/skin/lobo/body.pt @@ -26,6 +26,14 @@
+ +
+ + + / +
From 705b4a6f2dc295a32330fab2d159430cc65f7782 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 09:02:14 +0100 Subject: [PATCH 02/22] work in progress: breadcrumbs --- README.txt | 17 +++++++++++++++++ browser/common.py | 5 +++-- browser/node.py | 13 ++++++++++++- browser/skin/lobo/body.pt | 3 ++- locales/de/LC_MESSAGES/loops.mo | Bin 15234 -> 15279 bytes locales/de/LC_MESSAGES/loops.po | 5 ++++- 6 files changed, 38 insertions(+), 5 deletions(-) diff --git a/README.txt b/README.txt index 3d37044..2d862e4 100755 --- a/README.txt +++ b/README.txt @@ -649,6 +649,23 @@ to the bottom, and to the top. ['m111', 'm114', 'm112', 'm113'] +Breadcrumbs +----------- + + >>> view = NodeView(m112, TestRequest()) + >>> view.breadcrumbs() + [] + + >>> loopsRoot.options = ['showBreadcrumbs'] + >>> m114.nodeType = 'page' + >>> m114.target = cc1 + >>> view = NodeView(m114, TestRequest()) + >>> view.breadcrumbs() + [{'url': 'http://127.0.0.1/loops/views/m1', 'label': u'Menu'}, + {'url': 'http://127.0.0.1/loops/views/m1/m11', 'label': u'Zope'}, + {'url': 'http://127.0.0.1/loops/views/m1/m11/m114', 'label': u''}] + + End-user Forms and Special Views ================================ diff --git a/browser/common.py b/browser/common.py index 762fba1..659fc11 100644 --- a/browser/common.py +++ b/browser/common.py @@ -18,8 +18,6 @@ """ Common base class for loops browser view classes. - -$Id$ """ from cgi import parse_qs, parse_qsl @@ -146,6 +144,9 @@ class BaseView(GenericView, I18NView): return self.controller.getTemplateMacros('resource', resource_macros) #return resource_macros.macros + def breadcrumbs(self): + return [] + @Lazy def name(self): return getName(self.context) diff --git a/browser/node.py b/browser/node.py index 9c4cc9b..5c95f8d 100644 --- a/browser/node.py +++ b/browser/node.py @@ -36,7 +36,7 @@ from zope.lifecycleevent import ObjectCreatedEvent, ObjectModifiedEvent from zope.lifecycleevent import Attributes from zope.formlib.form import Form, FormFields from zope.proxy import removeAllProxies -from zope.publisher.defaultview import getDefaultViewName +from zope.app.publisher.browser import getDefaultViewName from zope.security import canAccess, canWrite, checkPermission from zope.security.proxy import removeSecurityProxy from zope.traversing.api import getParent, getParents, getPath @@ -91,6 +91,10 @@ class NodeView(BaseView): self.recordAccess() return result + @Lazy + def title(self): + return self.context.title or getName(self.context) + def breadcrumbs(self): if not self.globalOptions('showBreadcrumbs'): return [] @@ -100,6 +104,13 @@ class NodeView(BaseView): if menuItem != menu.context: data.append(dict(label=menuItem.title, url=absoluteURL(menuItem, self.request))) + for p in getParents(menuItem): + if p == menu.context: + break + data.insert(1, dict(label=p.title, + url=absoluteURL(p, self.request))) + if self.virtualTarget: + data.extend(self.virtualTarget.breadcrumbs()) return data def recordAccess(self, viewName=''): diff --git a/browser/skin/lobo/body.pt b/browser/skin/lobo/body.pt index 6f8f83c..fc9fc50 100644 --- a/browser/skin/lobo/body.pt +++ b/browser/skin/lobo/body.pt @@ -29,10 +29,11 @@
+ You are here: - / + >
diff --git a/locales/de/LC_MESSAGES/loops.mo b/locales/de/LC_MESSAGES/loops.mo index 616b70a25aa0e526ef0b7ce215c893781f0a340c..72fad95c341c5318a67892cf39cac7935a17724d 100644 GIT binary patch delta 4920 zcmXxo32;@_8Nl)TLIO!3gzSI;0wH1F!VaY)o0L`wiq5o-El@~-LPgo3I8Sg?5DvhSvvo< z`N@;{>FXl?nkS;D6zejhXfEX!@@$Qwhf1PoEIy1~uofHQ`QSxtMEyE8!1U%()EG0d z4HjZE?1Nc25}RN((mxqJK*0eM!i5=FM13I^<7&*om(dAcM+0~hi8VS9>PN%%<5)oZ zC!zft%%y%64K%w&6ph1t%w+xOXB0AMn2C0nd+S0JJre3qpc6ig`M5D$--QPBCc48z z=*~V2*UzGXUO@ZRrD(rr(G0zY_CJ93 zI}z&Vkj+L{(SQqXi!)e>O{w=m109Y@7aB{!9ZoG*dvbswOI>r}XY2HnY* z=-IiBraF^V9hZyFQy6T8&R>ac_;$44*jD7T^T=QRFs8Pon|+7TxI| z(2Tr=?sPx8&=IuXN%T;kM>F&-nn|BY`)8vWNET4=LUVM&cIX0?*ay3X`cKh*)6gBv zM&^u`pl4t`dQWT7iT@h>8#?Y7y70+R{}-}OGWv>w123ZyM`dv!4bhZlqdRMj-kKih z4u_)kyV3W=Bs9f~&~eK{{YCUJZ$&e)AKk#a*xcv;Bn5YT3C+Mwv|}3EawkpE0R^Gn z7QOeCXy60UTR0Rea10vooN)c|;3{;3>(ER*k3CsG+C;$sj-!XE4m|^3BTrUz10B$! zUA*&l=)zT@J_w!o`)CHnqW#CCflo!}oreaxGF*QSlir(+pHlKe~n#n3%0@Icq>!reAm&9WmLxPS?GMpW)w0gv)oBZgl?I;GuB+C^q!@KS9BTPNEZ?MN|9*I`A?Y z`OVOt)*o6;s4wlvYuM-iAO#m}z)5R}&Cm%; z&_h{{cyN`8a+Gp!Awr7{abb=|GpTiXfQ>0 zqNjHfI^Z#MfpuuVEofl1q5VYgb2RW9Xl5F7pGgdXBLG?SlV4t|E7 zol8jy4!nj&p57xqj74aw+o2P7MhEsqCmfCjFgmnPMElJ|cfJS>bWNzgie_>jn(1Sq zJ^2L%Q<~@*@3$U%)ZcFQfB} z>Kh-zYRsknb1cV2Xa-;E%l&ua-_c;i`_LU7LMQkLeO^yv5hnV@*GtfPXLRScqqkrT z8qg2X@smRPbaei?=)#N94KC|P{@u~DG&tdUbb>cR$9;G!#rPfCPoeLHR{i5!)E_x# zQ8l{Y5_F;EXkcqX{iWb$^y%7)9?rLu6dd?Y@K~@8UHA;z@e-2N=qmc6>MIo(Uc5(SfU3?^1Ar_tAkLhYp{k11<({pc%;+9H%%d zn2Tnn2<=}H?1l!|7kz&WMHjvUo#$@M^HAMK!9z0zo!~)q;Ji>@iU#-;y3jM|M4M8} z^MZ) z3reys(zbz5ed^Q>kxE1z-4Xlk1JapClgYm^$Njue*U+@$nm z=vtims<0$+HI-G=J8?ZVq^LUa-_)w2j}n(s(~8F@8l~PYE>9GuzAWz0DU;c1!>9?! zYw^}Mz(&mD+Gn_qPr=S1C1aarmra~iQ#Q3`R?VQD%PX!W(tCC5HE`#$4(0jTv!^{c RxoqmRnpuN(zC5rl@jsT_@yh@J delta 4882 zcmXxo3y{s%9l-H(_rWe}**6JMc3E%MD?t(xr6Uv1M5IPZiW-beV}#K(xoNa9Q4-@u zOBzWVA|*rAt2CvlBHE(TF)ZQ#?^zwW7-JD>ADzw`T_|J^%2p7!dr+}r*1#jt6Ihe_8O+4XSR1clW6Y`_LOra& zT6m4m;FN#1+onR9{2q z-9gvMA_}d8u3w5CxC1(_S0nOoLmwJka3~t_7&Jvw(bUdB$GwG)TY_d}4SM43=m`&@ zap!4YX8)ycSnY^TS|~_zk-7rdZ#J23C!3 zbQv9g3munL9zrWDLNoO+`uYvV3Y>`UGq;@l8~J=1+~^nR$v;HfS7S@ufEjoU-RK0m zz!`M>1@yJOiH^%>m$q1dmDm#vcpMt=40PV?3i9tpKc~TOwndnUtI_uL(chz&Y73ge zedtEV(MNF-v+x|6>PzUj+-B)z%8ij5! z9v$!eHaH6#<8RRn?MK%+f*$BpZ2vd9&PB||vKs2>aqZ22f zuiq3jz#n4`d>tJ>7Y%G7y1|m@^4Px$YtX(C*(Vu3rQkw4(3I^$Cmuv2|3_^95*>E| z4e$!O;q7S6%5-}@^w!r$$2Z3Z5`5+2H>r5c-l zelNOVMQnc%{bK2h1~?2y;b`>4f5KY$Il952SU-t1sb4_v+I4h(erxjYM)y!~fpT;} z6`GO0(f;THPhu?`fu3|6-u<0`{*js&>nmdaHuUaPM^DH8o9OpJaU1e)irR67y}frlA98p@A)l?Hi*z(ZG+QnK_H5`ZgL+G0&|6+arIG!;|Ruz+|-ljke_9zhcX1 z;C_T1=!WOxz*_Cnzlxiq6MCa39*I8Vsb~ggp_xgc2YDC0#GBDfZpAwISM=^2K<6D! zQZVw9n1@%V$^L_t&P-sfWW7rHQp%YVRU~fm?LpNT6KEqY$hHKGhxjFh78qgke+&(lz2eA+@ zVqMJcM5fq3lu$6$EzyCGqNyGp>*KM2`j61tn?e`*7`+=?&;#s21HXiBd;`5xx6%2v zJEtE>BQ(HvnB=!b=uE-XJcVvN5_v_#B=lJ=L{GR1U2r3M>9)uA&v5|tgXlUfyQD8+ z6?)Rgu^dOB8GHp@cXk)@Z^Vmf@I=ee1va7Y>ozRG<7of&SkLR4K6z905p+NU>WkmUW9*G`s0(zk7UCF;2yh?)$EQkXa zzF3R{&Cs*Qr2L-vYfW-LYmH3Xf25Vm}*jC>r5+(2d5S3r$NcEa;taC!~%P zG|$LLq-qon=$FGU8~^Jj@cA12mrL1oSMo8Vb*Znz5qyU5@!z#HVGO!w?bM>e#)-Vt zrotWt`HVjr`|D$ADz~V0lLW0TsQohbtib7f`tT{HFEcf?Xj&pWwX3Ks_cCq2;`5)> z*`gjLS1H}(9VjhN6s2yJR<^9kYFpy0XOOSGyWjlpV?Fx* kh4c9ouIXPkD1XgI&8{Z0I<)OnwPtB$dEuJsZu=Ae1D90t!~g&Q diff --git a/locales/de/LC_MESSAGES/loops.po b/locales/de/LC_MESSAGES/loops.po index 8586a11..a14d767 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: 2011-10-31 12:00 CET\n" +"PO-Revision-Date: 2011-11-19 12:00 CET\n" "Last-Translator: Helmut Merz \n" "Language-Team: loops developers \n" "MIME-Version: 1.0\n" @@ -11,6 +11,9 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: kwrite\n" +msgid "You are here:" +msgstr "Sie sind hier:" + msgid "Concept" msgstr "Begriff" From 9b42dc8f435d9e461a0ffdaa138562567dc3fd08 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 10:37:39 +0100 Subject: [PATCH 03/22] do not use bold face for blogpost descriptions --- browser/loops.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/loops.css b/browser/loops.css index a28fd08..44b16ef 100644 --- a/browser/loops.css +++ b/browser/loops.css @@ -455,7 +455,7 @@ div.comment { } .blogpost .description { - font-weight: bold; + /* font-weight: bold; */ font-size: 90%; color: #666666; padding-top: 0.4em; From c7f68ab9830dedef46e8380ecab2878b25cadb72 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 11:13:36 +0100 Subject: [PATCH 04/22] improve blogpost layout --- browser/loops.css | 1 - browser/skin/lobo/lobo.css | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/browser/loops.css b/browser/loops.css index 44b16ef..e4790e5 100644 --- a/browser/loops.css +++ b/browser/loops.css @@ -455,7 +455,6 @@ div.comment { } .blogpost .description { - /* font-weight: bold; */ font-size: 90%; color: #666666; padding-top: 0.4em; diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index de8850b..b7d404c 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -478,17 +478,19 @@ div.comment { } .blogpost .description { - font-weight: bold; font-size: 90%; color: #666666; - padding-top: 0.4em; + margin-top: 1em; +} + +.blogpost .description img { + padding-top: 0.2em; } .blog .info, .blogpost .info { font-style: italic; font-size: 90%; color: #666666; - padding-top: 0.4em; } /* calendar, work items */ From 8b53393c6b1085385c5f90d3981c7d040528614b Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 11:20:15 +0100 Subject: [PATCH 05/22] improve blogpost layout --- browser/skin/lobo/lobo.css | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index b7d404c..09eff89 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -477,16 +477,21 @@ div.comment { color: #666666; } +.blog .text { + font-size: 90%; + margin-top: 1em; +} + +.blog img { + padding-top: 0.2em; +} + .blogpost .description { font-size: 90%; color: #666666; margin-top: 1em; } -.blogpost .description img { - padding-top: 0.2em; -} - .blog .info, .blogpost .info { font-style: italic; font-size: 90%; From 279b2ff0a62e3e74c18a2246eb6d92ba75863217 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 11:25:42 +0100 Subject: [PATCH 06/22] improve blogpost layout --- browser/skin/lobo/lobo.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index 09eff89..42c8a83 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -472,6 +472,12 @@ div.comment { /* blog */ +.blog h1.headline { + clear: both; + margin-top: 0.7em; + margin-bottom: 0.2em; +} + .blog .description { font-size: 90%; color: #666666; From 1a9c98c9fe56d04521277a92be88535ef4469689 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 11:28:10 +0100 Subject: [PATCH 07/22] improve blogpost layout --- browser/skin/lobo/lobo.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index 42c8a83..c179b2f 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -485,7 +485,7 @@ div.comment { .blog .text { font-size: 90%; - margin-top: 1em; + margin-top: 0.5em; } .blog img { From 76c29330ed4132cf3243859235ad6647c0850b81 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 11:31:16 +0100 Subject: [PATCH 08/22] improve blogpost layout --- browser/skin/lobo/lobo.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index c179b2f..6e7e5d2 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -13,7 +13,7 @@ /* general */ h1, h2, h3, h4, h5, h6 { - margin-bottom: 0.5em; + margin-bottom: 0.4em; } a[href]:hover { @@ -474,7 +474,7 @@ div.comment { .blog h1.headline { clear: both; - margin-top: 0.7em; + margin-top: 0.6em; margin-bottom: 0.2em; } From 049bce7e3027b678080ad4db16157afae3d54874 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 12:03:57 +0100 Subject: [PATCH 09/22] improve blogpost layout --- browser/skin/lobo/lobo.css | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index 6e7e5d2..039c772 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -474,6 +474,7 @@ div.comment { .blog h1.headline { clear: both; + padding-top: 0; margin-top: 0.6em; margin-bottom: 0.2em; } From a08845569adff0a7e2758dee59438dc416cd1bcc Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 12:12:09 +0100 Subject: [PATCH 10/22] improve blogpost layout --- browser/skin/lobo/lobo.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index 039c772..d388ea6 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -475,7 +475,7 @@ div.comment { .blog h1.headline { clear: both; padding-top: 0; - margin-top: 0.6em; + margin-top: 0.7em; margin-bottom: 0.2em; } From f062086d433de626e489128c2b5aec7e638913e1 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 13:03:35 +0100 Subject: [PATCH 11/22] improve blogpost layout --- browser/skin/lobo/lobo.css | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index d388ea6..cf3dd3f 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -477,6 +477,7 @@ div.comment { padding-top: 0; margin-top: 0.7em; margin-bottom: 0.2em; + font-size: 150%; } .blog .description { From 993b773fec1908bdd5dfb5a5ca47368c887a9cd6 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 13:08:18 +0100 Subject: [PATCH 12/22] improve blogpost layout --- browser/skin/lobo/lobo.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index cf3dd3f..b3d3a54 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -475,7 +475,7 @@ div.comment { .blog h1.headline { clear: both; padding-top: 0; - margin-top: 0.7em; + margin-top: 1em; margin-bottom: 0.2em; font-size: 150%; } From e12462ba56cb08d116337b92a169485c54c8419c Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 13:09:23 +0100 Subject: [PATCH 13/22] improve blogpost layout --- browser/skin/lobo/lobo.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/browser/skin/lobo/lobo.css b/browser/skin/lobo/lobo.css index b3d3a54..185692d 100644 --- a/browser/skin/lobo/lobo.css +++ b/browser/skin/lobo/lobo.css @@ -474,8 +474,7 @@ div.comment { .blog h1.headline { clear: both; - padding-top: 0; - margin-top: 1em; + padding-top: 1em; margin-bottom: 0.2em; font-size: 150%; } From eb73015265b56dd281a1cb8f4a8385c04962b9b3 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 25 Nov 2011 14:26:34 +0100 Subject: [PATCH 14/22] use new URL for loops site --- browser/skin/lobo/body.pt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/skin/lobo/body.pt b/browser/skin/lobo/body.pt index fc9fc50..7d22bca 100644 --- a/browser/skin/lobo/body.pt +++ b/browser/skin/lobo/body.pt @@ -64,7 +64,7 @@ tal:attributes="href string:${view/topMenu/url}/impressum">Impressum
)
Powered by - loops · + loops · Zope 3 · Python · Dojo. From 120156cd041f1c9b5b1caef7ae3df484adce7210 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sat, 26 Nov 2011 14:54:24 +0100 Subject: [PATCH 15/22] work in progress: reporting, example 'work statement' report --- expert/browser/report.py | 2 +- expert/configure.zcml | 4 ++-- expert/report.py | 32 +++++++++++++++++-------- organize/work/README.txt | 35 +++++++++++++++++++++++++++ organize/work/report.py | 52 +++++++++++++++++++++++++++++++++------- organize/work/results.pt | 3 ++- 6 files changed, 105 insertions(+), 23 deletions(-) diff --git a/expert/browser/report.py b/expert/browser/report.py index b8f5f9b..27912eb 100644 --- a/expert/browser/report.py +++ b/expert/browser/report.py @@ -85,7 +85,7 @@ class ResultsView(NodeView): def reportInstance(self): instance = component.getAdapter(self.report, IReportInstance, name=self.report.reportType) - instance.request = self.request + instance.view = self return instance #@Lazy diff --git a/expert/configure.zcml b/expert/configure.zcml index 03543ad..68c5d79 100644 --- a/expert/configure.zcml +++ b/expert/configure.zcml @@ -22,9 +22,9 @@ provides="loops.expert.report.IReport" trusted="True" /> + interface="loops.expert.report.IReport" /> + set_schema="loops.expert.report.IReport" /> >> from loops.expert.report import IReport, Report + >>> component.provideAdapter(Report) + >>> tReport = addAndConfigureObject(concepts, Concept, 'report', + ... title=u'Report', conceptType=concepts.getTypeConcept(), + ... typeInterface=IReport) + >>> hasReport = addAndConfigureObject(concepts, Concept, 'hasreport', + ... title=u'has Report', conceptType=concepts.getPredicateType()) + +Now we can create a report and register it as the report for the task +used above. + + >>> workStatement = addAndConfigureObject(concepts, Concept, 'work_statement', + ... title=u'Work Statement', conceptType=tReport, + ... reportType='work_report') + >>> workStatement.assignChild(task01, hasReport) + +The executable report is a report instance that is an adapter to the +(adapted) report instance. + >>> from loops.organize.work.report import WorkReportInstance >>> from loops.expert.report import IReportInstance >>> component.provideAdapter(WorkReportInstance, ... provides=IReportInstance, ... name='work_report') +The user interface to the report is a report view, the results are presented +in a results view. + + >>> from loops.view import Node + >>> reportNode = addAndConfigureObject(home, Node, 'report', + ... title=u'Report', target=workStatement) + >>> from loops.expert.browser.report import ReportView, ResultsView + >>> resultsView = ResultsView(reportNode, TestRequest()) + + >>> results = resultsView.results() + >>> len(results.data) + 2 + Fin de partie ============= diff --git a/organize/work/report.py b/organize/work/report.py index 71ec849..d7bab83 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -28,14 +28,17 @@ 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.organize.interfaces import IWorkItems from cybertools.util.jeep import Jeep +from loops.common import adapted, baseObject from loops.expert.report import ReportInstance +from loops import util results_template = ViewPageTemplateFile('results.pt') -task = Field('task', u'Task', - description=u'The task to which work items belong.', +tasks = Field('tasks', u'Tasks', + description=u'The tasks to which work items belong.', executionSteps=['query', 'output', 'sort']) work = Field('work', u'Work', description=u'The short description of the work.', @@ -65,12 +68,20 @@ class WorkReportInstance(ReportInstance): label = u'Work Report' rowFactory = WorkRow - fields = Jeep((day, dayFrom, dayTo, task, work, workDescription)) + fields = Jeep((day, dayFrom, dayTo, tasks, work, workDescription)) defaultOutputFields = fields - @Lazy + @property def queryCriteria(self): - # TODO: take from persistent report where appropriate + crit = self.context.queryCriteria + if crit is None: + f = self.fields['tasks'] + tasks = baseObject(self.context).getChildren([self.hasReportPredicate]) + crit = [LeafQueryCriteria(f.name, f.operator, tasks, f)] + return CompoundQueryCriteria(crit) + + @property + def xx_queryCriteria(self): crit = [LeafQueryCriteria(f.name, f.operator, None, f) for f in self.getAllQueryFields()] return CompoundQueryCriteria(crit) @@ -79,8 +90,31 @@ class WorkReportInstance(ReportInstance): return results_template.macros[name] def selectObjects(self, parts): - task = parts.get('task') - if not task: - return [] - return [] + result = [] + tasks = parts.pop('tasks').comparisonValue + for t in list(tasks): + tasks.extend(self.getAllSubtasks(t)) + for t in tasks: + result.extend(self.selectWorkItems(t, parts)) + # TODO: remove parts already used for selection from parts list + return result + def selectWorkItems(self, task, parts): + wi = self.workItems + return wi.query(task=util.getUidForObject(task)) + + def getAllSubtasks(self, concept): + result = [] + for c in concept.getChildren(): + if c.conceptType == self.taskType: + result.append(c) + result.extend(self.getAllSubtasks(c)) + return result + + @Lazy + def taskType(self): + return self.conceptManager['task'] + + @Lazy + def workItems(self): + return IWorkItems(self.recordManager['work']) diff --git a/organize/work/results.pt b/organize/work/results.pt index 0d1cec0..fbd482b 100644 --- a/organize/work/results.pt +++ b/organize/work/results.pt @@ -11,7 +11,8 @@ - +

+ From 55fbb7eefc118f64ce44574d78a67fc661ad149f Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sun, 27 Nov 2011 15:19:19 +0100 Subject: [PATCH 16/22] work in progress: reporting, example 'work statement' report --- expert/browser/report.py | 5 +- expert/browser/results.pt | 12 +++++ organize/work/README.txt | 14 ++++-- organize/work/report.py | 100 ++++++++++++++++++++++++++++++-------- organize/work/results.pt | 3 +- 5 files changed, 106 insertions(+), 28 deletions(-) diff --git a/expert/browser/report.py b/expert/browser/report.py index 27912eb..8fdb035 100644 --- a/expert/browser/report.py +++ b/expert/browser/report.py @@ -73,8 +73,7 @@ class ResultsView(NodeView): @Lazy def params(self): params = dict(self.request.form) - if 'report_execute' in params: - del params['report_execute'] + params.pop('report_execute', None) return params @Lazy @@ -105,3 +104,5 @@ class ResultsView(NodeView): def displayedColumns(self): return self.reportInstance.getActiveOutputFields() + def getColumnRenderer(self, name): + return self.result_macros[name] diff --git a/expert/browser/results.pt b/expert/browser/results.pt index 9c892ad..91e3948 100644 --- a/expert/browser/results.pt +++ b/expert/browser/results.pt @@ -19,4 +19,16 @@

+ + + + + + + + + + + diff --git a/organize/work/README.txt b/organize/work/README.txt index 4f1ac64..0365fa4 100644 --- a/organize/work/README.txt +++ b/organize/work/README.txt @@ -220,10 +220,16 @@ in a results view. >>> from loops.expert.browser.report import ReportView, ResultsView >>> resultsView = ResultsView(reportNode, TestRequest()) - >>> results = resultsView.results() - >>> len(results.data) - 2 - + >>> results = resultsView.results().getResult() + >>> len(results) + 1 + >>> for row in results: + ... for col in resultsView.displayedColumns: + ... print col.getDisplayValue(row), + ... print + 08/12/28 1230487200 1230491700 + {'url': '.../home/report/.36', 'title': u'loops Development'} + {'url': '.../home/report/.33', 'title': u'john'} 4500 900 finished Fin de partie ============= diff --git a/organize/work/report.py b/organize/work/report.py index d7bab83..61f6e67 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -29,6 +29,8 @@ from cybertools.composer.report.base import LeafQueryCriteria, CompoundQueryCrit from cybertools.composer.report.field import Field from cybertools.composer.report.result import ResultSet, Row as BaseRow from cybertools.organize.interfaces import IWorkItems +from cybertools.util.date import timeStamp2Date +from cybertools.util.format import formatDate from cybertools.util.jeep import Jeep from loops.common import adapted, baseObject from loops.expert.report import ReportInstance @@ -37,29 +39,90 @@ from loops import util results_template = ViewPageTemplateFile('results.pt') +class TargetField(Field): + + renderer = 'target' + + def getValue(self, row): + value = self.getRawValue(row) + return util.getObjectForUid(value) + + def getDisplayValue(self, row): + value = self.getValue(row) + if value is None: + return dict(title=self.getRawValue(row), url=u'') + view = row.parent.context.view + return dict(title=value.title, url=view.getUrlForTarget(value)) + + +class DayField(Field): + + def getValue(self, row): + return timeStamp2Date(self.getRawValue(row)) + + def getDisplayValue(self, row): + value = self.getValue(row) + if value: + view = row.parent.context.view + return formatDate(value, 'date', 'short', view.languageInfo.language) + return u'' + + tasks = Field('tasks', u'Tasks', - description=u'The tasks 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']) + description=u'The tasks from which work items should be selected.', + executionSteps=['query']) 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']) +day = DayField('day', u'Day', + description=u'The day the work was done.', + executionSteps=['sort', 'output']) +timeStart = Field('start', u'Start Time', + description=u'The time the unit of work was started.', + executionSteps=['sort', 'output']) +timeEnd = Field('end', u'End Time', + description=u'The time the unit of work was finished.', + executionSteps=['output']) +task = TargetField('taskId', u'Task', + description=u'The task to which work items belong.', + executionSteps=['output']) +party = TargetField('userName', u'Party', + description=u'The party (usually a person) who did the work.', + executionSteps=['sort', 'output']) +title = Field('title', u'Title', + description=u'The short description of the work.', + executionSteps=['output']) +description = Field('description', u'Description', + description=u'The long description of the work.', + executionSteps=['x_output']) +duration = Field('duration', u'Duration', + description=u'The duration of the work.', + executionSteps=['output']) +effort = Field('effort', u'Effort', + description=u'The effort of the work.', + executionSteps=['output', 'sum']) +state = Field('state', u'State', + description=u'The state of the work.', + executionSteps=['query', 'output']) class WorkRow(BaseRow): - pass + def getRawValue(self, attr): + if attr in self.attributeHandlers: + return self.attributeHandlers[attr](self, attr) + track = self.context + if attr in track.metadata_attributes: + return getattr(track, attr) + return track.data.get(attr, u'') + + def getDay(self, attr): + return self.context.timeStamp + + attributeHandlers = dict(day=getDay) class WorkReportInstance(ReportInstance): @@ -68,7 +131,9 @@ class WorkReportInstance(ReportInstance): label = u'Work Report' rowFactory = WorkRow - fields = Jeep((day, dayFrom, dayTo, tasks, work, workDescription)) + fields = Jeep((dayFrom, dayTo, tasks, + day, timeStart, timeEnd, task, party, title, description, + duration, effort, state)) defaultOutputFields = fields @property @@ -80,12 +145,6 @@ class WorkReportInstance(ReportInstance): crit = [LeafQueryCriteria(f.name, f.operator, tasks, f)] return CompoundQueryCriteria(crit) - @property - def xx_queryCriteria(self): - 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] @@ -101,7 +160,8 @@ class WorkReportInstance(ReportInstance): def selectWorkItems(self, task, parts): wi = self.workItems - return wi.query(task=util.getUidForObject(task)) + states = ['done', 'done_x', 'finished'] + return wi.query(task=util.getUidForObject(task), state=states) def getAllSubtasks(self, concept): result = [] diff --git a/organize/work/results.pt b/organize/work/results.pt index fbd482b..7115062 100644 --- a/organize/work/results.pt +++ b/organize/work/results.pt @@ -11,8 +11,7 @@ -

- + From 0a1d2ec2628ba26f248a184ec6f298747c50df88 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sun, 27 Nov 2011 19:44:58 +0100 Subject: [PATCH 17/22] work in progress: reporting; example 'work statement' report is operational --- expert/browser/results.pt | 33 ++++++++++-- expert/field.py | 43 ++++++++++++++++ organize/work/README.txt | 12 +++-- organize/work/report.py | 103 ++++++++++++++++++++++++++------------ organize/work/results.pt | 61 ---------------------- 5 files changed, 150 insertions(+), 102 deletions(-) create mode 100644 expert/field.py delete mode 100644 organize/work/results.pt diff --git a/expert/browser/results.pt b/expert/browser/results.pt index 91e3948..60c65be 100644 --- a/expert/browser/results.pt +++ b/expert/browser/results.pt @@ -15,7 +15,25 @@

- Default Results Listing +

Work Items

+ + + + + + + + + + + + + +
+
@@ -24,11 +42,18 @@ - + + + + + + -
- + diff --git a/expert/field.py b/expert/field.py new file mode 100644 index 0000000..0a9d002 --- /dev/null +++ b/expert/field.py @@ -0,0 +1,43 @@ +# +# 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 +# + +""" +Field definitions for reports. +""" + +from cybertools.composer.report.field import Field +from loops import util + + +class TargetField(Field): + + renderer = 'target' + + def getValue(self, row): + value = self.getRawValue(row) + if value is None: + return None + return util.getObjectForUid(value) + + def getDisplayValue(self, row): + value = self.getValue(row) + if value is None: + return dict(title=u'', url=u'') + view = row.parent.context.view + return dict(title=value.title, url=view.getUrlForTarget(value)) + diff --git a/organize/work/README.txt b/organize/work/README.txt index 0365fa4..2dea1a0 100644 --- a/organize/work/README.txt +++ b/organize/work/README.txt @@ -220,16 +220,20 @@ in a results view. >>> from loops.expert.browser.report import ReportView, ResultsView >>> resultsView = ResultsView(reportNode, TestRequest()) - >>> results = resultsView.results().getResult() - >>> len(results) + >>> results = resultsView.results()#.getResult() + >>> len(list(results)) 1 >>> for row in results: ... for col in resultsView.displayedColumns: ... print col.getDisplayValue(row), ... print - 08/12/28 1230487200 1230491700 + 08/12/28 19:00 20:15 {'url': '.../home/report/.36', 'title': u'loops Development'} - {'url': '.../home/report/.33', 'title': u'john'} 4500 900 finished + {'url': '.../home/report/.33', 'title': u'john'} 01:15 00:15 finished + + >>> results.totals.data + {'effort': 900} + Fin de partie ============= diff --git a/organize/work/report.py b/organize/work/report.py index 61f6e67..9d3c8b6 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -33,41 +33,57 @@ from cybertools.util.date import timeStamp2Date from cybertools.util.format import formatDate from cybertools.util.jeep import Jeep from loops.common import adapted, baseObject +from loops.expert.field import TargetField from loops.expert.report import ReportInstance from loops import util -results_template = ViewPageTemplateFile('results.pt') +class DateField(Field): -class TargetField(Field): - - renderer = 'target' + part = 'date' + format = 'short' + renderer = 'right' def getValue(self, row): value = self.getRawValue(row) - return util.getObjectForUid(value) - - def getDisplayValue(self, row): - value = self.getValue(row) if value is None: - return dict(title=self.getRawValue(row), url=u'') - view = row.parent.context.view - return dict(title=value.title, url=view.getUrlForTarget(value)) - - -class DayField(Field): - - def getValue(self, row): - return timeStamp2Date(self.getRawValue(row)) + return None + return timeStamp2Date(value) def getDisplayValue(self, row): value = self.getValue(row) if value: view = row.parent.context.view - return formatDate(value, 'date', 'short', view.languageInfo.language) + return formatDate(value, self.part, self.format, + view.languageInfo.language) return u'' +class TimeField(DateField): + + part = 'time' + + +class DurationField(Field): + + renderer = 'right' + + def getValue(self, row): + value = self.getRawValue(row) + if value and 'totals' in self.executionSteps: + data = row.parent.totals.data + data[self.name] = data.get(self.name, 0) + value + if value: + value /= 3600.0 + return value + + def getDisplayValue(self, row): + value = self.getValue(row) + if not value: + return u'' + return u'%02i:%02i' % divmod(value * 60, 60) + + tasks = Field('tasks', u'Tasks', description=u'The tasks from which work items should be selected.', executionSteps=['query']) @@ -77,13 +93,13 @@ dayFrom = Field('dayFrom', u'Start Day', dayTo = Field('dayTo', u'End Day', description=u'The last day until which to select work.', executionSteps=['query']) -day = DayField('day', u'Day', +day = DateField('day', u'Day', description=u'The day the work was done.', executionSteps=['sort', 'output']) -timeStart = Field('start', u'Start Time', +timeStart = TimeField('start', u'Start', description=u'The time the unit of work was started.', executionSteps=['sort', 'output']) -timeEnd = Field('end', u'End Time', +timeEnd = TimeField('end', u'End', description=u'The time the unit of work was finished.', executionSteps=['output']) task = TargetField('taskId', u'Task', @@ -91,19 +107,19 @@ task = TargetField('taskId', u'Task', executionSteps=['output']) party = TargetField('userName', u'Party', description=u'The party (usually a person) who did the work.', - executionSteps=['sort', 'output']) + executionSteps=['query', 'sort', 'output']) title = Field('title', u'Title', description=u'The short description of the work.', executionSteps=['output']) description = Field('description', u'Description', description=u'The long description of the work.', executionSteps=['x_output']) -duration = Field('duration', u'Duration', +duration = DurationField('duration', u'Duration', description=u'The duration of the work.', executionSteps=['output']) -effort = Field('effort', u'Effort', +effort = DurationField('effort', u'Effort', description=u'The effort of the work.', - executionSteps=['output', 'sum']) + executionSteps=['output', 'totals']) state = Field('state', u'State', description=u'The state of the work.', executionSteps=['query', 'output']) @@ -122,7 +138,19 @@ class WorkRow(BaseRow): def getDay(self, attr): return self.context.timeStamp - attributeHandlers = dict(day=getDay) + def getDuration(self, attr): + value = self.context.data.get('duration') + if value is None: + value = self.getRawValue('end') - self.getRawValue('start') + return value + + def getEffort(self, attr): + value = self.context.data.get('effort') + if value is None: + value = self.getDuration(attr) + return value + + attributeHandlers = dict(day=getDay, duration=getDuration, effort=getEffort) class WorkReportInstance(ReportInstance): @@ -131,37 +159,46 @@ class WorkReportInstance(ReportInstance): label = u'Work Report' rowFactory = WorkRow + fields = Jeep((dayFrom, dayTo, tasks, day, timeStart, timeEnd, task, party, title, description, duration, effort, state)) + defaultOutputFields = fields + defaultSortCriteria = (day, timeStart,) @property def queryCriteria(self): + form = self.view.request.form crit = self.context.queryCriteria if crit is None: f = self.fields['tasks'] tasks = baseObject(self.context).getChildren([self.hasReportPredicate]) + tasks = [util.getUidForObject(task) for task in tasks] crit = [LeafQueryCriteria(f.name, f.operator, tasks, f)] + for f in self.getAllQueryFields(): + if f.name in form: + crit.append(LeafQueryCriteria(f.name, f.operator, form[f.name], f)) return CompoundQueryCriteria(crit) - def getResultsRenderer(self, name, defaultMacros): - return results_template.macros[name] - def selectObjects(self, parts): result = [] - tasks = parts.pop('tasks').comparisonValue + tasks = [util.getObjectForUid(t) for t in parts.pop('tasks').comparisonValue] for t in list(tasks): tasks.extend(self.getAllSubtasks(t)) for t in tasks: result.extend(self.selectWorkItems(t, parts)) - # TODO: remove parts already used for selection from parts list + # remove parts already used for selection from parts list: + parts.pop('userName', None) return result def selectWorkItems(self, task, parts): - wi = self.workItems states = ['done', 'done_x', 'finished'] - return wi.query(task=util.getUidForObject(task), state=states) + kw = dict(task=util.getUidForObject(task), state=states) + if 'userName' in parts: + kw['userName'] = parts['userName'].comparisonValue + wi = self.workItems + return wi.query(**kw) def getAllSubtasks(self, concept): result = [] diff --git a/organize/work/results.pt b/organize/work/results.pt deleted file mode 100644 index 7115062..0000000 --- a/organize/work/results.pt +++ /dev/null @@ -1,61 +0,0 @@ - - - -
-

Work Items

- - - - - - - - -
-
-
- - -
- Work Items Listing - - - - - - - - - - - - - - - - - - - -
Task
2009-01
2007-03-3017:3020:002:30 - Task - JohnTitle
-
- - - From 0812b3975bb8452e41ea7af8257ff91fe4b08d11 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 28 Nov 2011 09:15:23 +0100 Subject: [PATCH 18/22] use events and projects also as subtasks for work statement report --- organize/work/report.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/organize/work/report.py b/organize/work/report.py index 9d3c8b6..d229bb2 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -170,8 +170,8 @@ class WorkReportInstance(ReportInstance): @property def queryCriteria(self): form = self.view.request.form - crit = self.context.queryCriteria - if crit is None: + crit = self.context.queryCriteria or [] + if not crit and 'tasks' not in form: f = self.fields['tasks'] tasks = baseObject(self.context).getChildren([self.hasReportPredicate]) tasks = [util.getUidForObject(task) for task in tasks] @@ -203,14 +203,16 @@ class WorkReportInstance(ReportInstance): def getAllSubtasks(self, concept): result = [] for c in concept.getChildren(): - if c.conceptType == self.taskType: + if c.conceptType in self.taskTypes: result.append(c) result.extend(self.getAllSubtasks(c)) return result @Lazy - def taskType(self): - return self.conceptManager['task'] + def taskTypes(self): + return (self.conceptManager['task'], + self.conceptManager['event'], + self.conceptManager['project']) @Lazy def workItems(self): From 35a442b171013ec9515de52ca5fac6a13499b5f6 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 29 Nov 2011 17:20:07 +0100 Subject: [PATCH 19/22] remove obsolete package-specific line from generic template --- expert/browser/results.pt | 1 - 1 file changed, 1 deletion(-) diff --git a/expert/browser/results.pt b/expert/browser/results.pt index 60c65be..227913c 100644 --- a/expert/browser/results.pt +++ b/expert/browser/results.pt @@ -15,7 +15,6 @@
-

Work Items

Date: Wed, 30 Nov 2011 11:39:07 +0100 Subject: [PATCH 20/22] define new UrlField for showing a link e.g. on a title; minor fix in handling dynamic parameters --- expert/field.py | 9 +++++++++ expert/report.py | 7 ++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/expert/field.py b/expert/field.py index 0a9d002..ff46650 100644 --- a/expert/field.py +++ b/expert/field.py @@ -24,6 +24,15 @@ from cybertools.composer.report.field import Field from loops import util +class UrlField(Field): + + renderer = 'target' + + def getDisplayValue(self, row): + nv = row.parent.context.view.nodeView + return dict(title=self.getValue(row), url=nv.getUrlForTarget(row.context)) + + class TargetField(Field): renderer = 'target' diff --git a/expert/report.py b/expert/report.py index 89a8925..c89a2cd 100644 --- a/expert/report.py +++ b/expert/report.py @@ -94,9 +94,10 @@ class ReportInstance(BaseReport): 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 + if dynaParams is not None: + 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) From eb46739b9392dac2bc91a3598a16b61729c914e3 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Wed, 30 Nov 2011 11:39:33 +0100 Subject: [PATCH 21/22] fix class variables --- organize/work/report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/organize/work/report.py b/organize/work/report.py index d229bb2..7dbefdf 100644 --- a/organize/work/report.py +++ b/organize/work/report.py @@ -155,8 +155,8 @@ class WorkRow(BaseRow): class WorkReportInstance(ReportInstance): - type = "deliverables" - label = u'Work Report' + type = "work_statement" + label = u'Work Statement' rowFactory = WorkRow From 8851759a11751664bdb21c34b19a87dc19e09116 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Fri, 2 Dec 2011 09:54:15 +0100 Subject: [PATCH 22/22] MicroArticle basically working --- browser/lobo/standard.py | 2 +- compound/README.txt | 26 ++++++++++++++++++++++-- compound/microart/base.py | 17 ++++++++-------- compound/microart/browser.py | 33 ++++++++++++++++++++++--------- compound/microart/interfaces.py | 8 +++++++- compound/microart/view_macros.pt | 22 +++++++++++++++------ locales/de/LC_MESSAGES/loops.mo | Bin 15279 -> 16160 bytes locales/de/LC_MESSAGES/loops.po | 32 +++++++++++++++++++++++++++++- 8 files changed, 112 insertions(+), 28 deletions(-) diff --git a/browser/lobo/standard.py b/browser/lobo/standard.py index 9acced9..1c636ac 100644 --- a/browser/lobo/standard.py +++ b/browser/lobo/standard.py @@ -139,7 +139,7 @@ class Layout(Base, ConceptView): parts = (self.options('parts') or self.typeOptions('parts') or ['h1', 'g3']) - ti = adapted(self.context.conceptType).typeInterface + #ti = adapted(self.context.conceptType).typeInterface for p in parts: viewName = 'lobo_' + p view = component.queryMultiAdapter((self.context, self.request), diff --git a/compound/README.txt b/compound/README.txt index 06b6e2e..58ac802 100644 --- a/compound/README.txt +++ b/compound/README.txt @@ -321,10 +321,32 @@ Micro Articles >>> from loops.compound.microart.base import MicroArt >>> from loops.compound.microart.interfaces import IMicroArt - >>> component.provideAdapter(BlogPost, provides=IMicroArt) + >>> component.provideAdapter(MicroArt, provides=IMicroArt) >>> tMicroArt = addAndConfigureObject(concepts, Concept, 'microart', - ... title=u'MicroArt', conceptType=tType) + ... title=u'MicroArt', conceptType=tType, + ... typeInterface=IMicroArt) + + >>> ma01 = addAndConfigureObject(concepts, Concept, 'ma01', + ... conceptType=tMicroArt, + ... title=u'Organizational Knowledge', + ... story=u'Systemic KM talks about organizational knowledge.', + ... insight=u'Organizational knowledge is not visible.', + ... consequences=u'Use examples. Look for strucure and rules. ' + ... u'Knowledge shows itself in actions.', + ... followUps=u'What about collective intelligence? ' + ... u'How does an organization express itself?') + + >>> ma01._insight + u'Organizational knowledge is not visible.' + >>> list(resources) + [..., u'ma01_story'] + + >>> adMa01 = adapted(ma01) + >>> adMa01.insight + u'Organizational knowledge is not visible.' + >>> adMa01.story + u'Systemic KM talks about organizational knowledge.' Fin de partie diff --git a/compound/microart/base.py b/compound/microart/base.py index 3b568be..0133ad0 100644 --- a/compound/microart/base.py +++ b/compound/microart/base.py @@ -43,25 +43,26 @@ class MicroArt(Compound): implements(IMicroArt) - _adapterAttributes = Compound._adapterAttributes + ('text',) - _noexportAttributes = ('text',) - _textIndexAttributes = ('text',) + _contextAttributes = list(IMicroArt) + _adapterAttributes = Compound._adapterAttributes + ('story',) + _noexportAttributes = ('story',) + _textIndexAttributes = ('story', 'insight', 'consequences', 'folloUps') - defaultTextContentType = 'text/restructured' + defaultTextContentType = 'text/html' textContentType = defaultTextContentType - def getText(self): + def getStory(self): res = self.getParts() if len(res) > 0: return adapted(res[0]).data return u'' - def setText(self, value): + def setStory(self, value): res = self.getParts() if len(res) > 0: res = adapted(res[0]) else: tTextDocument = self.conceptManager['textdocument'] - name = getName(self.context) + '_text' + name = getName(self.context) + '_story' res = addAndConfigureObject(self.resourceManager, Resource, name, title=self.title, contentType=self.defaultTextContentType, resourceType=tTextDocument) @@ -70,4 +71,4 @@ class MicroArt(Compound): res = adapted(res) res.data = value notify(ObjectModifiedEvent(res.context)) - text = property(getText, setText) + story = property(getStory, setStory) diff --git a/compound/microart/browser.py b/compound/microart/browser.py index f35c83a..816bc30 100755 --- a/compound/microart/browser.py +++ b/compound/microart/browser.py @@ -26,7 +26,7 @@ from zope import component from zope.app.pagetemplate import ViewPageTemplateFile from zope.cachedescriptors.property import Lazy -from loops.browser.concept import ConceptView, ConceptRelationView +from loops.browser.concept import ConceptView from loops.common import adapted from loops import util from loops.util import _ @@ -37,16 +37,31 @@ view_macros = ViewPageTemplateFile('view_macros.pt') class MicroArtView(ConceptView): + @Lazy + def contentType(self): + return 'text/restructured' + + @Lazy + def macros(self): + return self.controller.getTemplateMacros('microart.view', view_macros) + @Lazy def macro(self): - return view_macros.macros['microart'] + return self.macros['main'] - def render(self): - return self.renderText(self.data['text'], self.adapted.textContentType) + @Lazy + def story(self): + return self.renderText(self.adapted.story, self.contentType) - def resources(self): - stdPred = self.loopsRoot.getConceptManager().getDefaultPredicate() - rels = self.context.getResourceRelations([stdPred]) - for r in rels: - yield self.childViewFactory(r, self.request, contextIsSecond=True) + @Lazy + def insight(self): + return self.renderText(self.adapted.insight, self.contentType) + + @Lazy + def consequences(self): + return self.renderText(self.adapted.consequences, self.contentType) + + @Lazy + def followUps(self): + return self.renderText(self.adapted.followUps, self.contentType) diff --git a/compound/microart/interfaces.py b/compound/microart/interfaces.py index 691bff7..e85ec2b 100644 --- a/compound/microart/interfaces.py +++ b/compound/microart/interfaces.py @@ -27,6 +27,11 @@ from loops.compound.interfaces import ICompound from loops.util import _ +class HtmlField(schema.Text): + + __typeInfo__ = ('html',) + + class IMicroArt(ICompound): """ A short article with a few elements, for collecting relevant information in a knowledge management environment. @@ -34,6 +39,7 @@ class IMicroArt(ICompound): # title = Ueberschrift, Thema + #story = HtmlField( # Geschichte story = schema.Text( # Geschichte title=_(u'Story'), description=_(u'The story, i.e. the main text of your ' @@ -54,7 +60,7 @@ class IMicroArt(ICompound): followUps = schema.Text( #Anschlussfragen title=_(u'Follow-up Questions'), - description=_(u'Question for helping to solve or avoid ' + description=_(u'Questions for helping to solve or avoid ' u'similar problems in the future.'), required=False) diff --git a/compound/microart/view_macros.pt b/compound/microart/view_macros.pt index ff15b1f..7dadf5d 100755 --- a/compound/microart/view_macros.pt +++ b/compound/microart/view_macros.pt @@ -1,6 +1,8 @@ + -
@@ -9,9 +11,17 @@ tal:condition="description"> Description
-
Here comes the text...
- - - +
+
+
+
+

+
+ + + \ No newline at end of file diff --git a/locales/de/LC_MESSAGES/loops.mo b/locales/de/LC_MESSAGES/loops.mo index 72fad95c341c5318a67892cf39cac7935a17724d..bbd614853603afa33b85582abf26286edd13cdc4 100644 GIT binary patch delta 7005 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 AGynhq delta 6102 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 diff --git a/locales/de/LC_MESSAGES/loops.po b/locales/de/LC_MESSAGES/loops.po index a14d767..a5bea16 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: 2011-11-19 12:00 CET\n" +"PO-Revision-Date: 2011-12-02 12:00 CET\n" "Last-Translator: Helmut Merz \n" "Language-Team: loops developers \n" "MIME-Version: 1.0\n" @@ -83,6 +83,8 @@ msgstr "Thema bearbeiten..." msgid "Modify topic." msgstr "Thema ändern" +# blog + msgid "Edit Blog Post..." msgstr "Eintrag bearbeiten..." @@ -104,6 +106,34 @@ msgstr "Tagebucheintrag anlegen" msgid "Export Blog" msgstr "Tagebuch exportieren" +# micro article + +msgid "Story" +msgstr "Story" + +msgid "The story, i.e. the main text of your micro article. Who did what? What happend?" +msgstr "Die Geschichte, der Haupttext Ihres MikroArtikels. Wer hat was getan? Was geschah?" + +msgid "Insight" +msgstr "Einsicht" + +msgid "What can we learn from the story? What has gone wrong? What was good?" +msgstr "Was können wir aus der Geschichte lernen? Was ist schiefgegangen? Was war gut?" + +msgid "Consequences" +msgstr "Folgerungen" + +msgid "What we will do next time in a similar situation?" +msgstr "Was werden wir das nächste Mal in einer ähnlichen Situation tun?" + +msgid "Follow-up Questions" +msgstr "Anschlussfragen" + +msgid "Questions for helping to solve or avoid similar problems in the future." +msgstr "Fragen, die einem dabei helfen können, das Problem in der Zukunft zu lösen oder zu vermeiden." + +# glossary + msgid "Glossary Item" msgstr "Glossareintrag"