diff --git a/browser/loops/loops_layout.css b/browser/loops/loops_layout.css index 8023a5a..7091643 100644 --- a/browser/loops/loops_layout.css +++ b/browser/loops/loops_layout.css @@ -28,6 +28,11 @@ body { scrollbar-arrow-color: Black; } +textarea, input, select { + font-family: Verdana, Arial, Helvetica, "sans serif"; + font-size: 100%; +} + table { border-collapse: collapse; font-size: 100%; @@ -244,7 +249,7 @@ pre { /* Styles for xmltree */ -#navtreecontents { +#navtreecontents { padding-right: 35px; } @@ -281,7 +286,7 @@ pre { } -/* Structural elements +/* Structural elements */ #top { diff --git a/stateful/README.txt b/stateful/README.txt new file mode 100644 index 0000000..2927d78 --- /dev/null +++ b/stateful/README.txt @@ -0,0 +1,16 @@ +================ +Stateful objects +================ + + ($Id$) + + >>> from cybertools.stateful.definition import StatesDefinition + >>> from cybertools.stateful.definition import State, Transition + >>> from cybertools.stateful.base import Stateful + + >>> class Demo(Stateful): + ... pass + + >>> demo = Demo() + >>> demo.getState() + 'started' diff --git a/stateful/__init__.py b/stateful/__init__.py new file mode 100644 index 0000000..38314f3 --- /dev/null +++ b/stateful/__init__.py @@ -0,0 +1,3 @@ +""" +$Id$ +""" diff --git a/stateful/base.py b/stateful/base.py new file mode 100644 index 0000000..f4b76e0 --- /dev/null +++ b/stateful/base.py @@ -0,0 +1,56 @@ +# +# Copyright (c) 2007 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 +# + +""" +State definition implementation. + +$Id$ +""" + +from zope.interface import implements + +from cybertools.stateful.interfaces import IStateful +from cybertools.stateful.definition import statesDefinitions + + +class Stateful: + + implements(IStateful) + + _statesDefinition = 'default' + _state = None + + def getState(self): + if self._state is None: + self._state = self.getStatesDefinition()._initialState + return self._state + + def doTransition(self, transition): + """ execute transition. + """ + sd = self.getStatesDefinition() + sd.doTransitionFor(self, transition) + + def getAvailableTransitions(self): + sd = self.getStatesDefinition() + return sd.getAvailableTransitionsFor(self) + + def getStatesDefinition(self): + return statesDefinitions.get(self._statesDefinition, None) + + diff --git a/stateful/definition.py b/stateful/definition.py new file mode 100644 index 0000000..e8dfa25 --- /dev/null +++ b/stateful/definition.py @@ -0,0 +1,70 @@ +# +# Copyright (c) 2007 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 +# + +""" +State definition implementation. + +$Id$ +""" + +from zope.interface import implements + +from cybertools.stateful.interfaces import IStatesDefinition + + +class State(object): + + def __init__(self, id, title, transitions): + self.id = id + self.title = title + self.transitions = transitions + + +class Transition(object): + + def __init__(self, id, title, targetState): + self.id = id + self.title = title + self.targetState = targetState + + +class StatesDefinition(object): + + implements(IStatesDefinition) + + # Basic/example states definition: + _states = { + 'started': State('started', 'Started', ('finish',)), + 'finished': State('finished', 'Finished', ()), + } + _transitions = { + 'finish': Transition('finish', 'Finish', 'finished') + } + _initialState = 'started' + + def doTransitionFor(self, object, transition): + object._state = self._transitions[transition].targetState + + def getAvailableTransitionsFor(self, object): + state = object.getState() + return [ self._transitions[t] for t in self._states[state].transitions ] + + +statesDefinitions = { + 'default': StatesDefinition(), +} diff --git a/stateful/interfaces.py b/stateful/interfaces.py new file mode 100644 index 0000000..e562de8 --- /dev/null +++ b/stateful/interfaces.py @@ -0,0 +1,61 @@ +# +# Copyright (c) 2007 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 +# + +""" +Interfaces for the `stateful` package. + +$Id$ +""" + +from zope.interface import Interface + + +class IStateful(Interface): + """ Provides basic methods for stateful objects. + """ + + def getState(): + """ Return the workflow state of the object. + """ + + def doTransition(transition): + """ Execute a transition; the transition is specified by its id. + """ + + def getAvailableTransitions(): + """ Return the transitions for this object that are available in + the current state. The implementation of the returned transition + objects is not specified, they may be an action dictionaries or + special Transition objects. + """ + + +class IStatesDefinition(Interface): + """ A simple definition for a set of states and transitions between them, + Similar to an entity-based workflow definition. + """ + + def doTransitionFor(object, transition): + """ Execute a transition for the object given; + the transition is specified by its id. + """ + + def getAvailableTransitionsFor(object): + """ Return the transitions available for this object in its current state. + """ + diff --git a/stateful/tests.py b/stateful/tests.py new file mode 100755 index 0000000..d2b0d81 --- /dev/null +++ b/stateful/tests.py @@ -0,0 +1,28 @@ +#! /usr/bin/python + +""" +Tests for the 'cybertools.stateful' package. + +$Id$ +""" + +import unittest, doctest +from zope.testing.doctestunit import DocFileSuite + + +class Test(unittest.TestCase): + "Basic tests for the storage package." + + def testBasicStuff(self): + pass + + +def test_suite(): + flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS + return unittest.TestSuite(( + unittest.makeSuite(Test), + DocFileSuite('README.txt', optionflags=flags), + )) + +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff --git a/view/README.txt b/view/README.txt index ab4b4c7..f113647 100644 --- a/view/README.txt +++ b/view/README.txt @@ -10,18 +10,19 @@ Generic Views OK, there aren't really generic views. Already the first implementation we want to look at is a specic one: It is based on Zope Page Templates and -uses the classic CMF/Zope 3 approach: The template belonging to a view -calls a `main` macro and fills a slot there. But at least the template -implementation is decoupled from the view, so we are able to put a lot of -generic functionality into the view. +uses the classic CMF/Zope 3 approach: The template belonging to a view - more +precisely a page - calls a `main` macro and fills a slot there. But at least +the template implementation is decoupled from the view, so we are able to +put a lot of generic functionality into the view. In order to make a ZPT work we need a Zope-compatible request, so we use the standard Zope 3 TestRequest. >>> from zope.publisher.browser import TestRequest - >>> from cybertools.view.web.base import View - >>> view = View(None, TestRequest()) + >>> from cybertools.view.web.base import Page, Content + >>> request = TestRequest() + >>> view = Page(None, request) >>> view.render() - u'< html...>............' + u'...............' diff --git a/view/base.py b/view/base.py index 98e3553..62a463e 100644 --- a/view/base.py +++ b/view/base.py @@ -30,7 +30,16 @@ class View(object): def __init__(self, context, request): self.context = context self.request = request + self.parentView = self.rootView = None + self.childViews = [] + self.setUp() + + def setUp(self): + pass def render(self, *args, **kw): - return '' + raise NotImplementedError + + def add(self, factory): + self.childViews.append(factory(self.context, self.request)) diff --git a/view/web/base.py b/view/web/base.py index aaf32dc..ff48839 100644 --- a/view/web/base.py +++ b/view/web/base.py @@ -17,7 +17,7 @@ # """ -Generic view base class. +Generic base classes for web-based views. $Id$ """ @@ -28,12 +28,15 @@ from cybertools.view.web.zpt.template import Template class View(base.View): + """ A generic web-based view - not necessarily HTML-/browser-based. + """ - templateFactory = Template - def __init__(self, context, request): - self.context = context - self.request = request +class BrowserView(View): + """ A browser-based view (i.e. a view using HTML as presentation language). + """ + + templateFactory = None @Lazy def template(self): @@ -42,3 +45,19 @@ class View(base.View): def render(self, *args, **kw): return self.template.render(*args, **kw) + +class Page(BrowserView): + """ A browser-based view that renders a full web page. + """ + + templateFactory = Template + + def setUp(self): + super(Page, self).setUp() + self.add(Content) + + +class Content(BrowserView): + """ A browser-based view that renders the content region of a web page. + """ + diff --git a/view/web/zpt/configure.zcml b/view/web/zpt/configure.zcml index 6338685..d92875c 100644 --- a/view/web/zpt/configure.zcml +++ b/view/web/zpt/configure.zcml @@ -8,7 +8,7 @@