First usable version of view.Node stuff for simple web sites

git-svn-id: svn://svn.cy55.de/Zope3/src/loops/trunk@1003 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
helmutm 2006-01-12 20:45:57 +00:00
parent c2806a8c10
commit d61d424009
7 changed files with 320 additions and 19 deletions

View file

@ -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()

View file

@ -5,6 +5,10 @@
xmlns="http://namespaces.zope.org/browser"
i18n_domain="zope">
<!-- resources -->
<resource name="view.css" file="view.css" />
<!-- macros -->
<page
@ -197,6 +201,7 @@
<containerViews
for="loops.interfaces.IViewManager"
index="zope.View"
contents="zope.ManageContent"
add="zope.ManageContent"
/>
@ -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">
<widget field="description" height="2" />
<widget field="body" height="4" />
<widget field="body" height="8" />
</addform>
@ -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">
<widget field="description" height="2" />
<widget field="body" height="4" />
<widget field="body" height="15" />
</editform>
@ -242,6 +247,7 @@
name="node.html"
for="loops.interfaces.INode"
template="node.pt"
class=".node.NodeView"
permission="zope.View"
/>

View file

@ -3,22 +3,64 @@
<head></head>
<body>
<metal:css fill-slot="style_slot">
<style type="text/css" media="all"
tal:content="string:@import url(${context/++resource++view.css});">
@import url(view.css);
</style>
</metal:css>
<metal:body fill-slot="body">
<tal:content define="item context;
<tal:content define="item view/getContent;
level level|python: 1">
<metal:block define-macro="content">
<div tal:content="structure item/body">The body</div>
<div tal:define="level python:level+1">
<tal:items repeat="item item/values">
<tal:body define="body item/body"
condition="body">
<div class="content-1"
tal:content="structure body"
tal:attributes="class string:content-$level">The body</div>
</tal:body>
<tal:sub define="level python:level+1">
<tal:items repeat="item item/items">
<metal:portlet use-macro="views/node_macros/content" />
</tal:items>
</div>
</tal:sub>
</metal:block>
</tal:content>
</metal:body>
<div class="box" metal:fill-slot="navigators">
<h4>Navigation</h4>
<tal:menu define="item view/getMenu;
level level|python: 1"
condition="item">
<div class="body">
<metal:menu define-macro="menu">
<div class="menu-3"
tal:attributes="class python: 'content '
+ (item['selected'] and 'even' or 'odd')
+ ' menu-%i' % level">
<a href="#" class=""
tal:content="item/title"
tal:attributes="href item/url">Menu Text</a>
</div>
<tal:sub tal:define="level python:level+1">
<tal:items repeat="item item/items">
<metal:portlet use-macro="views/node_macros/menu" />
</tal:items>
</tal:sub>
</metal:menu>
</div>
</tal:menu>
</div>
</body>
</html>
</tal:show>

75
browser/node.py Normal file
View file

@ -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

48
browser/view.css Normal file
View file

@ -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%
}

View file

@ -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.

38
view.py
View file

@ -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):