try: from WebKit.Page import Page except ImportError: from paste.webkit import wsgiapp wsgiapp.sys_path_install() from WebKit.Page import Page try: from WebKit.HTTPExceptions import * hasExceptions = True except ImportError: hasExceptions = False try: from WebKit import AppServer except ImportError: AppServer = None class CPage(Page): """ CPage is an alternative to WebKit.Page, which adds support for Components. Components can contain functionality like user logins, widgets and layout generation, template handling... whatever. Components for a servlet are listed in the components class variable, like:: class SitePage(CPage): components = [UserComponent(), OtherFancyComponent()] You can also run ``servlet.addComponent(component)``. Components can respond to events, and add methods to the servlet. See the ServletComponent class for more about writing components. To fire your own event use callEvent, like:: self.callEvent('eventname', ...) Use whatever arguments you want. Interested components will implement a method eventnameEvent(), which will be passed those arguments. callEvent will return a false value if a component wants to abort this event (because the component deals with the event on its own). """ # Events listed in _standardEvents will be run a bit faster, # as CPage keeps track of these events specially. You can # add your custom events here. _standardEvents = ['awake', 'sleep', 'exception'] def __init__(self): Page.__init__(self) self._instanceComponents = [] self._standardEventHandlers = {} for eventName in self._standardEvents: self._standardEventHandlers[eventName] = [] for componentFactory in self.components: self.addComponent(componentFactory) if hasattr(self, '__traceback_supplement_hide_vars__'): hide_vars = self.__traceback_supplement_hide_vars__ for var in ['_standardEventHandlers']: if var not in hide_vars: hide_vars.append(var) def actions(self): return [] def addComponent(self, componentFactory): component = componentFactory.componentFor(self) if not component: return component.addComponentTo(self) self._instanceComponents.append(component) for name in self._standardEventHandlers.keys(): if getattr(component, '%sEvent' % name, None): self._standardEventHandlers[name].append(getattr(component, '%sEvent' % name)) def callEvent(self, eventName, *args, **kw): """ Fire an event, to interested components. @@: maybe this should be replaced with PyDispatcher: http://pydispatcher.sourceforge.net/ """ if self._standardEventHandlers.has_key(eventName): for handler in self._standardEventHandlers[eventName]: if not handler(*args, **kw): return 0 else: for component in self._instanceComponents: meth = getattr(component, "%sEvent" % eventName, None) if meth and not meth(*args, **kw): return 0 return 1 def awake(self, trans): Page.awake(self, trans) self._title = self.__class__.__name__ self.setView('writeContent') self.callEvent('awake', trans) def sleep(self, trans): self.callEvent('sleep', trans) self._title = None self._view = None Page.sleep(self, trans) def runTransaction(self, trans): try: try: self.awake(trans) self.respond(trans) except Exception, e: if self.handleException(e) == 'break': return for component in self._instanceComponents: if component.handleExceptionEvent(e) == 'break': return raise finally: self.sleep(trans) def handleException(self, exc): pass def servletLink(self, name, absolute=False, extraURLPath=None, args=None): """ Links to the named servlet; or use 'path/to/servlet'; do not include a leading / """ assert not name.startswith('/'), 'The link name must not start with a / (%r)' % name url = '' req = self.request() env = req.environ() if absolute: # @@: Should detect HTTPS url = 'http://%s' % env.get('HTTP_HOST', env.get('SERVER_NAME')) port = env.get('SERVER_PORT', '80') if port != '80': url = url + ':' + port url = url + req.adapterName() + '/' + name if extraURLPath: if not extraURLPath.startswith('/'): extraURLPath = '/' + extraURLPath url = url + extraURLPath if args: fields = [] if isinstance(args, dict): args = args.items() for name, value in args: if value is None: continue fields.append('%s=%s' % (self.urlEncode(str(name)), self.urlEncode(str(value)))) url += '?' + '&'.join(fields) return url def linkToSelf(self, absolute=False, extraURLPath=None, args=None): name = self.__class__.__name__ contextName = self.request().contextName() app = self.transaction().application() if (contextName != app.setting('Contexts', {}).get('default') and contextName != 'default'): if contextName: name = contextName + '/' + name return self.servletLink( name, absolute=absolute, extraURLPath=extraURLPath, args=args) ############################################################ ## Reorganized action system: ############################################################ def writeHeader(self): pass def writeFooter(self): pass def title(self): return self._title def viewMethod(self): """ Return the method object for the current view. """ if type(self._view) in (type(""), type(u"")): return getattr(self, self._view) else: return self._view def view(self): """ Return the string name of the current view (the method name) """ if type(self._view) in (type(""), type(u"")): return self._view else: view = self._view if view is None: return None try: view = view.im_func except AttributeError: pass try: return view.func_name except AttributeError: raise AttributeError, "I don't know what kind of view this is: %r" % view def setView(self, view): """ Set the 'view'. The view is the method that is used to write the content (typically writeContent -- but you can override this). You can set the view to a string, which will be a method of self, or you can provide a function-like object. """ self._view = view def writeBodyParts(self): self.writeHeader() self.viewMethod()() self.writeFooter() def _respond(self, transaction): """ Handles actions if an ``_action_`` or ``_action_XXX`` field is defined, otherwise invokes `writeHTML`. Invoked by both `respondToGet` and `respondToPost`. Copied with slight modification from Page._respond """ req = transaction.request() # Support old style actions from 0.5.x and below. # Use setting OldStyleActions in Application.config # to use this. if transaction.application().setting('OldStyleActions', False) \ and req.hasField('_action_'): action = self.methodNameForAction(req.field('_action_')) actions = self._actionSet() if actions.has_key(action): self.preAction(action) apply(getattr(self, action), (transaction,)) self.postAction(action) return else: raise PageError, "Action '%s' is not in the public list of actions, %s, for %s." % (action, actions.keys(), self) # Check for actions # @@: This is the only change for CPage: for action in self._actionSet().keys(): if req.hasField('_action_%s' % action) or \ req.field('_action_', None) == action or \ (req.hasField('_action_%s.x' % action) and \ req.hasField('_action_%s.y' % action)): self.handleAction(action) return self.defaultAction() if not hasattr(Page, 'defaultAction'): def defaultAction(self): self.writeHTML() def _actionSet(self): if not hasattr(self, '_actionDict'): self._actionDict = {} for action in self.actions(): self._actionDict[action] = 1 for component in self._instanceComponents: for action in component.actions(): self._actionDict[action] = 1 return self._actionDict def preAction(self, actionName): pass def postAction(self, actionName): pass components = [] def sendResponseAndEnd(self, status, headers=None, body=None): res = self.response() res.setHeader('Status', str(status)) if headers: for name, value in headers.items(): res.setHeader(name, value) if body: res.write(body) else: res.write('%s

%s

' % (status, status)) if headers and headers.get('location'): res.write('See %s' % headers['location']) res.write('') self.endResponse()