diff --git a/README.txt b/README.txt index 0c78ff6..5db12af 100755 --- a/README.txt +++ b/README.txt @@ -4,6 +4,14 @@ loops - Linked Objects for Organization and Processing Services ($Id$) + >>> from zope.app.testing.setup import placefulSetUp, placefulTearDown + >>> site = placefulSetUp(True) + + >>> from zope.app import zapi + >>> from zope.app.tests import ztapi + >>> from zope.publisher.browser import TestRequest + + Concepts and Relations ====================== @@ -12,6 +20,8 @@ top-level loops container and a concept manager: >>> from loops import Loops >>> loops = Loops() + >>> site['loops'] = Loops() + >>> loops = site['loops'] >>> from loops.concept import ConceptManager, Concept >>> loops['concepts'] = ConceptManager() @@ -97,8 +107,8 @@ below) via the getClients() method: >>> conc[0] is zope True -Views: Menus, Menu Items, Listings, etc -======================================= +Views: Menus, Menu Items, Listings, Simple Content, etc +======================================================= We first need a view manager: @@ -122,6 +132,57 @@ menu that may contain other nodes as menu or content items: >>> m112.description u'' +There are a few convienence methods for accessing parent and child nodes: + + >>> m1.getParentNode() is None + True + >>> m11.getParentNode() is m1 + True + >>> [zapi.getName(child) for child in m11.getChildNodes()] + [u'm111', u'm112'] + +What is returned by these may be controlled by the nodeType attribute: + + >>> m1.nodeType = 'menu' + >>> m11.nodeType = 'page' + >>> m11.getParentNode('menu') is m1 + True + >>> m11.getParentNode('page') is None + True + >>> m111.nodeType = 'info' + >>> m112.nodeType = 'text' + >>> len(m11.getChildNodes('text')) + 1 + +There are also shortcut methods to retrieve certain types of nodes +in a simple and logical way: + + >>> m1.getMenu() is m1 + True + >>> m111.getMenu() is m1 + True + >>> m1.getPage() is m1 + True + >>> m111.getPage() is m111 + True + >>> m112.getPage() is m11 + True + >>> len(m1.getMenuItems()) + 1 + >>> len(m11.getMenuItems()) + 0 + >>> len(m111.getMenuItems()) + 0 + >>> len(m1.getTextItems()) + 0 + >>> len(m11.getTextItems()) + 1 + >>> len(m111.getTextItems()) + 0 + +Targets +------- + We can associate a node with a concept or directly with a resource via the view class's target attribute: @@ -135,3 +196,9 @@ view class's target attribute: >>> m111.target is zope3 True + +Fin de partie +============= + + >>> placefulTearDown() + diff --git a/browser/configure.zcml b/browser/configure.zcml index bbac24d..b63d3a3 100644 --- a/browser/configure.zcml +++ b/browser/configure.zcml @@ -5,6 +5,10 @@ xmlns="http://namespaces.zope.org/browser" i18n_domain="zope"> + + + + @@ -208,11 +213,11 @@ name="AddLoopsNode.html" content_factory="loops.view.Node" schema="loops.interfaces.INode" - fields="title description type body" + fields="title description nodeType body" permission="zope.ManageContent"> - + @@ -228,13 +233,13 @@ label="Edit Node" name="edit.html" schema="loops.interfaces.INode" - fields="title description type body" + fields="title description nodeType body" for="loops.interfaces.INode" permission="zope.ManageContent" menu="zmi_views" title="Edit"> - + @@ -242,6 +247,7 @@ name="node.html" for="loops.interfaces.INode" template="node.pt" + class=".node.NodeView" permission="zope.View" /> diff --git a/browser/node.pt b/browser/node.pt index 986c2a1..76127d6 100644 --- a/browser/node.pt +++ b/browser/node.pt @@ -3,22 +3,64 @@ + + + + + + - -
The body
-
- + +
The body
+
+ + -
+
+ +
+

Navigation

+ + +
+ + + + + + + + +
+ +
+
+ diff --git a/browser/node.py b/browser/node.py new file mode 100644 index 0000000..d98e3a4 --- /dev/null +++ b/browser/node.py @@ -0,0 +1,75 @@ +# +# Copyright (c) 2006 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 class for Node objects. + +$Id$ +""" + +from zope.cachedescriptors.property import Lazy +from zope.app import zapi +from zope.app.dublincore.interfaces import ICMFDublinCore +from zope.proxy import removeAllProxies +from zope.security.proxy import removeSecurityProxy + +from loops.interfaces import IConcept + +class NodeView(object): + + def render(self, text): + if not text: + return u'' + if text.startswith('<'): # seems to be HTML + return text + source = zapi.createObject(self.context.contentType, text) + view = zapi.getMultiAdapter((removeAllProxies(source), self.request)) + return view.render() + + @Lazy + def modified(self): + """ get date/time of last modification + """ + dc = ICMFDublinCore(self.context) + d = dc.modified or dc.created + return d and d.strftime('%Y-%m-%d %H:%M') or '' + + def getContent(self, item=None): + if item is None: + item = self.context.getPage() + if item is None: + item = self.context + result = {'title': item.title, + 'url': zapi.absoluteURL(item, self.request), + 'body': self.render(item.body), + 'items': [self.getContent(child) + for child in item.getTextItems()]} + return result + + def getMenu(self, item=None): + if item is None: + item = self.context.getMenu() + if item is None: + return None + result = {'title': item.title, + 'url': zapi.absoluteURL(item, self.request), + 'selected': item == self.context, + 'items': [self.getMenu(child) + for child in item.getMenuItems()]} + return result + diff --git a/browser/view.css b/browser/view.css new file mode 100644 index 0000000..c664cb7 --- /dev/null +++ b/browser/view.css @@ -0,0 +1,48 @@ +/* + $Id$ + + settings specific for view / node objects + +*/ + +.box { + margin: 5px; +} + +#body { + margin-left: 5px; +} + +.content-1 h1 { + font-size: 160%; + font-weight: bold; +} + +.content-2 h1, .content-1 h2, .content-1 h3 { + font-size: 130%; + font-weight: bold; +} + +.content-3 h1, .content-2 h2, .content-2 h3 { + font-size: 120%; +} + +.content-4 h1, .content-3 h2, .content-3 h3 { + font-size: 110%; + border: none; +} + +.content-5 h1, .content-4 h2, .content-4 h3 { + font-size: 100%; + border: none; +} + +.box div.body div.menu-3 { + padding-left: 1.5em; +} + +.box div.body div.menu-4 { + padding-left: 3em; + font-size: 90% +} + diff --git a/interfaces.py b/interfaces.py index e275135..a2dfaea 100644 --- a/interfaces.py +++ b/interfaces.py @@ -180,11 +180,11 @@ class INode(IView, IOrderedContainer): """ contains(IView) - type = schema.Choice( - title=_(u'Type'), + nodeType = schema.Choice( + title=_(u'Node Type'), description=_(u'Type of the node'), - values=('page', 'text', 'menu', 'menuitem'), - default='page', + values=('text', 'page', 'menu', 'info'), + default='info', required=True) body = schema.Text( @@ -194,6 +194,39 @@ class INode(IView, IOrderedContainer): default=u'', required=False) + contentType = Attribute(_(u'Content type (format) of the body')) + + def getParentNode(nodeTypes=None): + """ Return the next node up the node hierarchy. If the nodeTypes + parameter is given, search for the next node that has one of + the types in the nodeTypes list. + + Return None if no suitable node can be found. + """ + + def getChildNodes(nodeTypes=None): + """ Return a sequence of nodes contained in this node. If the + nodeTypes parameter is given return only nodes of these types. + """ + + def getMenu(): + """ Return the menu node this node belongs to or None if not found. + """ + + def getPage(): + """ Return a page node or None if not found. + """ + + def getMenuItems(): + """ Return the menu items belonging to this object (that should be + a menu). + """ + + def getTextItems(): + """ Return the menu items belonging to this object (that should be + a menu). + """ + class IViewManager(IContainer): """ A manager/container for views. diff --git a/view.py b/view.py index d93901c..2beb1bc 100644 --- a/view.py +++ b/view.py @@ -82,16 +82,46 @@ class Node(View, OrderedContainer): implements(INode) - _type = 'page' - def getType(self): return self._type - def setType(self, type): self._type = type - type = property(getType, setType) + _nodeType = 'info' + def getNodeType(self): return self._nodeType + def setNodeType(self, nodeType): self._nodeType = nodeType + nodeType = property(getNodeType, setNodeType) _body = u'' def getBody(self): return self._body def setBody(self, body): self._body = body body = property(getBody, setBody) + contentType = u'zope.source.rest' + + def getParentNode(self, nodeTypes=None): + parent = zapi.getParent(self) + while INode.providedBy(parent): + if not nodeTypes or parent.nodeType in nodeTypes: + return parent + parent = zapi.getParent(parent) + return None + + def getChildNodes(self, nodeTypes=None): + return [item for item in self.values() + if INode.providedBy(item) + and (not nodeTypes or item.nodeType in nodeTypes)] + + def getMenu(self): + return self.nodeType == 'menu' and self or self.getParentNode(['menu']) + + def getPage(self): + pageTypes = ['page', 'menu', 'info'] + if self.nodeType in pageTypes: + return self + return self.getParentNode(pageTypes) + + def getMenuItems(self): + return self.getChildNodes(['page', 'menu']) + + def getTextItems(self): + return self.getChildNodes(['text']) + class ViewManager(BTreeContainer):