import Globals import AccessControl from AccessControl import ClassSecurityInfo from Acquisition import aq_inner, aq_parent, aq_chain, aq_self, Implicit from Products.CMFCore.utils import getToolByName from FormAction import FormAction from globalVars import ANY_CONTEXT, ANY_BUTTON, STRING_TYPES from types import ListType, TupleType, DictType class ControllerState(AccessControl.Role.RoleManager): security = ClassSecurityInfo() security.declareObjectPublic() security.setDefaultAccess('allow') id = None # The status variable used for determining state transitions status = 'success' # A set of i18n-friendly error messages. Each key corresponds to a tuple # of the form (default_error_message, msgid), where msgid is used for i18n, # if present. errors = {} # The new context (use None if the context should remain the same) context = None # The button that was pressed (None if no named button was pressed) button = None # The default next action. This can be overridden by values in the form # controller. next_action = None # A dict of variables including such things as portal_status_message. Again, # each key corresponding to a message should have an i18n friendly value, # e.g. something like (id, default string) kwargs = {} # a dict of ids of validators already executed _validators = {} def __init__(self, id=None, context=None, button=None, status='success', \ errors={}, next_action=None, **kwargs): self.setId(id) self.setButton(button) self.setStatus(status) self.setErrors(errors) self.setContext(context) self.setKwargs(kwargs) self.setNextAction(next_action) self._is_validating = 0 self._validators = {} def set(self, **kwargs): """Set state object properties""" if kwargs.has_key('id'): self.setId(kwargs['id']) del kwargs['id'] if kwargs.has_key('button'): self.setButton(kwargs['button']) del kwargs['button'] if kwargs.has_key('status'): self.setStatus(kwargs['status']) del kwargs['status'] if kwargs.has_key('errors'): self.setErrors(kwargs['errors']) del kwargs['errors'] if kwargs.has_key('context'): self.setContext(kwargs['context']) del kwargs['context'] if kwargs.has_key('next_action'): self.setNextAction(kwargs['next_action']) del kwargs['next_action'] self.kwargs.update(kwargs) return self def setError(self, id, message, msgid=None, new_status=None): """Add an error message to the current state object. The new_status argument allows you to optionally change the object's status.""" self.errors[id] = (message, msgid) if new_status: self.status = new_status def getError(self, id): """Return the error message associated with the form variable id""" err = self.errors.get(id, None) if err: return err[0] return None def getI18NError(self, id): """Return the error message and i18n msgid associated with the form variable id. The return value is the tuple (errmsg, i18n_msgid).""" return self.errors.get(id, None) def getId(self): """Get the id of the calling script/page template""" return self.id def setId(self, id): """Set the id of the calling script/page template""" self.id = id def getButton(self): """Get the name of the named button pressed. You can name a button NAME in a template by giving it the name form.button.NAME""" return self.button def setButton(self, button): """Set the name of the named button pressed. You can name a button NAME in a template by giving it the name form.button.NAME""" self.button = button def getStatus(self): """Get the current status""" return self.status def setStatus(self, status): """Get the current status""" self.status = status def getErrors(self): """Return all errors in a dict of the form dict['name'] = errmsg""" err = {} for k,v in self.errors.items(): # make allowances for old-style string errors if type(v) in STRING_TYPES: err[k] = v else: err[k] = v[0] return err def getI18NErrors(self): """Return all errors in a dict of the form dict['name'] = (errmsg, i18n_msgid)""" err = {} for k,v in self.errors.items(): # make allowances for old-style string errors if type(v) in STRING_TYPES: err[k] = (v, None) else: err[k] = v return err def setErrors(self, errors): """Set the error dictionary. The dictionary should have entries of the form dict['name'] = (errmsg, i18n_msgid). The msgid can be None""" self.errors = {} # make allowances for old-style errors for k,v in errors.items(): if type(v) in STRING_TYPES: self.errors[k] = (v, None) else: self.errors[k] = v def getContext(self): """Get the context of the current form/script""" if self.context is None: return self.context return self.context[0] #return aq_inner(self.context) def setContext(self, context): """Set the context of the current form/script""" # Store the context in a list so that we don't nuke its acquisition chain. # This is kind of an evil hack, but since we aren't persisting # ControllerState objects, it shouldn't cause any big problems. self.context = [context] # self.context = context def getKwargs(self): """Get any extra arguments (e.g. portal_status_message) that should be passed along to the next template/script""" return self.kwargs def setKwargs(self, kwargs): """Set any extra arguments (e.g. portal_status_message) that should be passed along to the next template/script""" self.kwargs = kwargs def getNextAction(self): """Get the default next action (this action can be overridden in portal_form_controller). The action will have the form {'action_type':action_type, 'args':args} where action_type is a string (from portal_form_controller.validActionTypes), and args is a string that will be passed to the constructor when generating an action object""" return self.next_action def setNextAction(self, action): """Set the default next action (this action can be overridden in portal_form_controller). setNextAction can be called either with a dictionary argument of the form {'action_type':action_type, 'args':args} or with a string of the form 'action_type:args'. Here action_type is a string (from form_controller.validActionTypes) and args is a string that will be passed to the constructor when generating an action object""" if action is None: self.next_action = action return if type(action) == DictType: action_type = action['action_type'] args = action['args'] else: split_action = action.split(':',1) action_type = split_action[0].strip() if len(split_action) == 2: args = split_action[1].strip() else: args = None controller = getToolByName(self.getContext(), 'portal_form_controller') if not action_type in controller.validActionTypes(): raise KeyError, 'Unknown action type %s\n' % action_type self.next_action = FormAction(self.getId(), self.getStatus(), ANY_CONTEXT, ANY_BUTTON, action_type, args, controller) # indicate that a validator has been executed def _addValidator(self, validator): self._validators[validator] = 1 # remove a validator from the list of already-executed validators def clearValidator(self, validator): if self._validators.has_key(validator): del self._validators[validator] # clear the list of already-executed validators def clearValidators(self): self._validators = {} # see if given validators have been executed def hasValidated(self, validators=[]): if validators is None: validators = [] elif type(validators) != ListType: validators = [validators] for v in validators: if not self._validators.has_key(v): return 0 return 1 def _setValidating(self, is_validating): self._is_validating = is_validating def _isValidating(self): return self._is_validating def __str__(self): return 'id = %s\nstatus = %s\nbutton=%s\nerrors=%s\ncontext=%s\nkwargs=%s\nnext_action=%s\n' % \ (self.id, str(self.getStatus()), str(self.getButton()), str(self.getErrors()), str(self.getContext()), str(self.kwargs), str(self.next_action)) Globals.InitializeClass(ControllerState)