diff --git a/browser/README.txt b/browser/README.txt index 9dbceda..7bedce4 100644 --- a/browser/README.txt +++ b/browser/README.txt @@ -206,3 +206,39 @@ packages. >>> gvc = component.getUtility(IViewConfigurator) + +Processing form input +--------------------- + +GenericView also provides an update() method that may be called from +templates that might receive form information. + + >>> view.update() + True + +Real work can only be done by an adapter to GenericView that provides the +IFormController interface with its update(). There also must be a +form variable (typically coming from a hidden field) with the name +'form.action' that provides the name under which the form controller is +registered. + + >>> from cybertools.browser.controller import IFormController, FormController + >>> class MyController(FormController): + ... def update(self): + ... print 'updating...' + + >>> component.provideAdapter(MyController, (View, IBrowserRequest), + ... IFormController, name='save') + + >>> request = TestRequest(form={'form.action': 'save'}) + >>> view = View(obj, request) + >>> view.update() + updating... + True + +The update() method will only be executed once: + + >>> view.update() + True + + diff --git a/browser/controller.py b/browser/controller.py index ad3d378..1690804 100644 --- a/browser/controller.py +++ b/browser/controller.py @@ -23,12 +23,15 @@ $Id$ """ from zope import component +from zope.interface import Interface, implements from zope.app.pagetemplate import ViewPageTemplateFile from zope.cachedescriptors.property import Lazy from cybertools.browser.configurator import IViewConfigurator, IMacroViewProperty +# layout controller: collects information about head elements, skins, portlets, etc + class Controller(object): def __init__(self, context, request): @@ -115,3 +118,29 @@ class Macro(object): def __call__(self): return self.macro + +# form processing: +# the part of the model/view/controller pattern that deals with +# form input + +class IFormController(Interface): + """ Used as a named adapter by GenericView for processing form input. + """ + + def update(): + """ Processing form input... + """ + + +class FormController(object): + + implements(IFormController) + + def __init__(self, context, request): + self.view = view = context # the controller is adapted to a view + self.context = context.context + self.request = request + + def update(self): + pass + diff --git a/browser/main.pt b/browser/main.pt index ef2dd90..7f134f3 100644 --- a/browser/main.pt +++ b/browser/main.pt @@ -3,6 +3,7 @@ i18n:domain="zope" tal:define="controller nocall:view/@@controller; resourceBase controller/resourceBase; + dummy view/update; body view/pageBody"> diff --git a/browser/view.py b/browser/view.py index b644a6e..75b8261 100644 --- a/browser/view.py +++ b/browser/view.py @@ -51,6 +51,8 @@ class GenericView(object): template = macro = menu = skin = None + _updated = False + def setController(self, controller): # make the (one and only controller) available via the request viewAnnotations = self.request.annotations.setdefault('cybertools.browser', {}) @@ -68,11 +70,30 @@ class GenericView(object): def __init__(self, context, request): self.context = context self.request = request + #cont = self.controller # check: leads to strange AttributeError in doctest + #if cont is not None: + # self.setupController() def __call__(self, *args, **kw): return self.index(*args, **kw) + #def render(self, *args, **kw): + # return self.index(*args, **kw) + + def update(self): + if not self._updated: + action = self.request.form.get('form.action') + if action: + fc = component.getMultiAdapter((self, self.request), + name=action) + fc.update() + self._updated = True + return True + def setupController(self): + """ May be called by __init__() if there is already a controller + or when the controller is set. May be implemented by subclass. + """ pass @Lazy