Ejemplo n.º 1
0
class ControllerBase:
    """Common functions for objects controlled by portal_form_controller"""

    security = ClassSecurityInfo()
    security.declareObjectProtected(View)

    security.declareProtected(ManagePortal, 'manage_formActionsForm')
    manage_formActionsForm = PageTemplateFile('www/manage_formActionsForm', globals())
    manage_formActionsForm.__name__ = 'manage_formActionsForm'

    security.declareProtected(ManagePortal, 'manage_formValidatorsForm')
    manage_formValidatorsForm = PageTemplateFile('www/manage_formValidatorsForm', globals())
    manage_formValidatorsForm.__name__ = 'manage_formValidatorsForm'

    def _updateActions(self, container, old_id, new_id, move):
        """Copy action overrides stored in portal_form_controller from one 
        object id to another"""
        actions = container.getFiltered(object_id=old_id)
        for a in actions:
            # if not container.match(new_id, a.getStatus(), a.getContextType(), a.getButton()):
            container.set(FormAction(new_id, a.getStatus(), a.getContextType(),
                           a.getButton(), a.getActionType(), a.getActionArg()))
        if move:
            for a in actions:
                container.delete(a.getKey())
                
    def _updateValidators(self, container, old_id, new_id, move):
        """Copy validator overrides stored in portal_form_controller from one 
        object id to another"""
        validators = container.getFiltered(object_id=old_id)
        for v in validators:
            # if not container.match(new_id, v.getContextType(), v.getButton()):
            container.set(FormValidator(new_id, v.getContextType(), v.getButton(), v.getValidators()))
        if move:
            for v in validators:
                container.delete(v.getKey())
        
    def _base_notifyOfCopyTo(self, container, op=0):
        self._old_id = self.getId()
        if op==0:  # copy
            self._cloned_object_path = self.getPhysicalPath()

    def _fixup_old_ids(self, old_id):
        fc = getToolByName(self, 'portal_form_controller')
        id = self.getId()
        if old_id != id:
            if hasattr(aq_base(self), 'actions'):
                self._updateActions(self.actions, old_id, id, move=1) # swap the ids for the default actions
                self._updateActions(fc.actions, old_id, id, move=0) # copy the overrides
            if hasattr(aq_base(self), 'validators'):
                self._updateValidators(self.validators, old_id, id, move=1) # swap the ids for the default validators
                self._updateValidators(fc.validators, old_id, id, move=0) # copy the overrides

    def _base_manage_afterAdd(self, object, container):
        old_id = getattr(self, '_old_id', None)
        if old_id:
            self._fixup_old_ids(old_id)
            delattr(self, '_old_id')

    def _base_manage_afterClone(self, object):
        # clean up the old object
        cloned_object_path = getattr(self, '_cloned_object_path')
        cloned_object = self.getPhysicalRoot().unrestrictedTraverse(cloned_object_path)
        delattr(cloned_object, '_old_id')
        delattr(cloned_object, '_cloned_object_path')
        # clean up the new object
        delattr(self, '_cloned_object_path')

    security.declareProtected(ManagePortal, 'listActionTypes')
    def listActionTypes(self):
        """Return a list of available action types."""
        return getToolByName(self, 'portal_form_controller').listActionTypes()

    security.declareProtected(ManagePortal, 'listFormValidators')
    def listFormValidators(self, override, **kwargs):
        """Return a list of existing validators.  Validators can be filtered by
           specifying required attributes via kwargs"""
        controller = getToolByName(self, 'portal_form_controller')
        if override:
            return controller.validators.getFiltered(**kwargs)
        else:
            return self.validators.getFiltered(**kwargs)


    security.declareProtected(ManagePortal, 'listFormActions')
    def listFormActions(self, override, **kwargs):
        """Return a list of existing actions.  Actions can be filtered by
           specifying required attributes via kwargs"""
        controller = getToolByName(self, 'portal_form_controller')
        if override:
            return controller.actions.getFiltered(**kwargs)
        else:
            return self.actions.getFiltered(**kwargs)


    security.declareProtected(ManagePortal, 'listContextTypes')
    def listContextTypes(self):
        """Return list of possible types for template context objects"""
        return getToolByName(self, 'portal_form_controller').listContextTypes()


    security.declareProtected(ManagePortal, 'manage_editFormValidators')
    def manage_editFormValidators(self, REQUEST):
        """Process form validator edit form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.validators
        else:
            container = self.validators
        controller._editFormValidators(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formValidatorsForm')


    security.declareProtected(ManagePortal, 'manage_addFormValidators')
    def manage_addFormValidators(self, REQUEST):
        """Process form validator add form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.validators
        else:
            container = self.validators
        controller._addFormValidators(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formValidatorsForm')


    security.declareProtected(ManagePortal, 'manage_delFormValidators')
    def manage_delFormValidators(self, REQUEST):
        """Process form validator delete form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.validators
        else:
            container = self.validators
        controller._delFormValidators(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formValidatorsForm')


    security.declareProtected(ManagePortal, 'manage_editFormActions')
    def manage_editFormActions(self, REQUEST):
        """Process form action edit form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.actions
        else:
            container = self.actions
        controller._editFormActions(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formActionsForm')


    security.declareProtected(ManagePortal, 'manage_addFormAction')
    def manage_addFormAction(self, REQUEST):
        """Process form action add form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.actions
        else:
            container = self.actions
        controller._addFormAction(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formActionsForm')


    security.declareProtected(ManagePortal, 'manage_delFormActions')
    def manage_delFormActions(self, REQUEST):
        """Process form action delete form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.actions
        else:
            container = self.actions
        controller._delFormActions(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formActionsForm')


    def getNext(self, controller_state, REQUEST):
        id = self.getId()
        status = controller_state.getStatus()
        context = controller_state.getContext()
        context_base = aq_base(context)

        context_type = getattr(context_base, 'portal_type', None)
        if context_type is None:
            context_type = getattr(context_base, '__class__', None)
            if context_type:
                context_type = getattr(context_type, '__name__', None)

        button = controller_state.getButton()
        controller = getToolByName(aq_inner(self), 'portal_form_controller')

        next_action = None
        try:
            next_action = controller.getAction(id, status, context_type, button)
        except ValueError:
            pass
        if next_action is None:
            try:
                if getattr(context_base, 'formcontroller_actions', None) is not None:
                    next_action = context.formcontroller_actions.match(id, status, context_type, button)
            except ValueError:
                pass
        if next_action is None:
            try:
                next_action = self.actions.match(id, status, context_type, button)
            except ValueError:
                pass
            if next_action is None:
                next_action = controller_state.getNextAction()
                if next_action is None:
                    # default for failure is to traverse to the form
                    if status == 'failure':
                        next_action=FormAction(id, status, ANY_CONTEXT, ANY_BUTTON, 'traverse_to', 'string:%s' % id, controller)
                    if next_action is None:
                        metadata_actions = [str(a) for a in self.actions.getFiltered(object_id=id)]
                        zmi_actions = [str(a) for a in controller.actions.getFiltered(object_id=id)]
                        raise ValueError, 'No next action found for %s.%s.%s.%s\nMetadata actions:\n%s\n\nZMI actions:\n%s\n' % \
                            (id, status, context_type, button, '\n'.join(metadata_actions), '\n'.join(zmi_actions))

        REQUEST.set('controller_state', controller_state)
        return next_action.getAction()(controller_state)


    def getButton(self, controller_state, REQUEST):
        buttons = []
        for k in REQUEST.form.keys():
            if k.startswith('form.button.'):
                buttons.append(k)
        if buttons:
            # Clicking on an image button results in 2 button variables in REQUEST.form
            # (maybe 3),namely form.button.button_name.x, form.button.button_name.y, and
            # possibly form.button.button_name (not for IE, though)
            # If we see more than one key with the button prefix, try to handle sensibly.
            if len(buttons) > 1:
                buttons.sort(lambda x, y: cmp(len(x), len(y)))
                if buttons[0].endswith('.x') or buttons[0].endswith('.y'):
                    buttons[0] = buttons[0][:-2]
            button = buttons[0][len('form.button.'):]
            controller_state.setButton(button)
        return controller_state


    def getValidators(self, controller_state, REQUEST):
        controller = getToolByName(self, 'portal_form_controller')
        context = controller_state.getContext()
        context_type = controller._getTypeName(context)
        button = controller_state.getButton()

        validators = None
        try:
            validators = controller.validators.match(self.id, context_type, button)
            if validators is not None:
                return validators
        except ValueError:
            pass
        try:
            if hasattr(aq_base(context), 'formcontroller_validators'):
                validators = context.formcontroller_validators.match(self.id, context_type, button)
                if validators is not None:
                    return validators
        except ValueError:
            pass
        try:
            validators = self.validators.match(self.id, context_type, button)
            if validators is not None:
                return validators
        except ValueError:
            pass
        return FormValidator(self.id, ANY_CONTEXT, ANY_BUTTON, [])


    def _read_action_metadata(self, id, filepath):
        self.actions = FormActionContainer()

        metadata = FSMetadata(filepath)
        cfg = CMFConfigParser()
        if os.path.exists(filepath + '.metadata'):
            cfg.read(filepath + '.metadata')
            _buttons_for_status = {}

            actions = metadata._getSectionDict(cfg, 'actions')
            if actions is None:
                actions = {}

            for (k, v) in actions.items():
                # action.STATUS.CONTEXT_TYPE.BUTTON = ACTION_TYPE:ACTION_ARG
                component = k.split('.')
                while len(component) < 4:
                    component.append('')
                if component[0] != 'action':
                    raise ValueError, '%s: Format for .metadata actions is action.STATUS.CONTEXT_TYPE.BUTTON = ACTION_TYPE:ACTION_ARG (not %s)' % (filepath, k)
                act = v.split(':',1)
                while len(act) < 2:
                    act.append('')

                context_type = component[2]
                self.actions.set(FormAction(id, component[1], component[2], component[3], act[0], act[1]))

                status_key = str(component[1])+'.'+str(context_type)
                if _buttons_for_status.has_key(status_key):
                    _buttons_for_status[status_key].append(component[3])
                else:
                    _buttons_for_status[status_key] = [component[3]]

            for (k, v) in _buttons_for_status.items():
                if v and not '' in v:
                    sk = k.split('.')
                    status = sk[0]
                    content_type = sk[1]
                    if not status:
                        status = 'ANY'
                    if not content_type:
                        content_type = 'ANY'
                    log('%s: No default action specified for status %s, content type %s.  Users of IE can submit pages using the return key, resulting in no button in the REQUEST.  Please specify a default action for this case.' % (str(filepath), status, content_type))
                    

    def _read_validator_metadata(self, id, filepath):
        self.validators = FormValidatorContainer()

        metadata = FSMetadata(filepath)
        cfg = CMFConfigParser()
        if os.path.exists(filepath + '.metadata'):
            cfg.read(filepath + '.metadata')
            _buttons_for_status = {}

            validators = metadata._getSectionDict(cfg, 'validators')
            if validators is None:
                validators = {}
            for (k, v) in validators.items():
                # validators.CONTEXT_TYPE.BUTTON = LIST
                component = k.split('.')
                while len(component) < 3:
                    component.append('')
                if component[0] != 'validators':
                    raise ValueError, '%s: Format for .metadata validators is validators.CONTEXT_TYPE.BUTTON = LIST (not %s)' % (filepath, k)

                context_type = component[1]
                self.validators.set(FormValidator(id, component[1], component[2], v))

                status_key = str(context_type)
                if _buttons_for_status.has_key(status_key):
                    _buttons_for_status[status_key].append(component[2])
                else:
                    _buttons_for_status[status_key] = [component[2]]

            for (k, v) in _buttons_for_status.items():
                if v and not '' in v:
                    content_type = k
                    if not content_type:
                        content_type = 'ANY'
                    log('%s: No default validators specified for content type %s.  Users of IE can submit pages using the return key, resulting in no button in the REQUEST.  Please specify default validators for this case.' % (str(filepath), content_type))


    security.declarePublic('writableDefaults')
    def writableDefaults(self):
        """Can default actions and validators be modified?"""
        return 1
Ejemplo n.º 2
0
class FormController(UniqueObject, SimpleItemWithProperties):
    """ """

    security = ClassSecurityInfo()

    id = 'portal_form_controller'
    title = 'Manages form validation and post-validation actions'
    meta_type = 'Form Controller Tool'

    manage_options = ((
        {
            'label': 'Overview',
            'action': 'manage_overview'
        },
        {
            'label': 'Documentation',
            'action': 'manage_docs'
        },
        {
            'label': 'Validation',
            'action': 'manage_formValidatorsForm'
        },
        {
            'label': 'Actions',
            'action': 'manage_formActionsForm'
        },
        {
            'label': 'Purge',
            'action': 'manage_purgeForm'
        },
    ) + SimpleItemWithProperties.manage_options)

    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = PageTemplateFile(os.path.join('www', 'manage_overview'),
                                       globals())
    manage_overview.__name__ = 'manage_overview'
    manage_overview._need__name__ = 0

    security.declareProtected(ManagePortal, 'manage_docs')
    manage_docs = PageTemplateFile(os.path.join('www', 'manage_docs'),
                                   globals())
    manage_docs.__name__ = 'manage_docs'

    security.declareProtected(ManagePortal, 'manage_formActionsForm')
    manage_formActionsForm = PageTemplateFile(
        os.path.join('www', 'manage_formActionsForm'), globals())
    manage_formActionsForm.__name__ = 'manage_formActionsForm'

    security.declareProtected(ManagePortal, 'manage_formValidatorsForm')
    manage_formValidatorsForm = PageTemplateFile(
        os.path.join('www', 'manage_formValidatorsForm'), globals())
    manage_formValidatorsForm.__name__ = 'manage_formValidatorsForm'

    security.declareProtected(ManagePortal, 'manage_purgeForm')
    manage_purgeForm = PageTemplateFile(
        os.path.join('www', 'manage_purgeForm'), globals())
    manage_purgeForm.__name__ = 'manage_purgeForm'

    # some aliases
    security.declareProtected(ManagePortal, 'manage_main')
    manage_main = manage_overview

    security.declareProtected(ManagePortal, 'index_html')
    index_html = None

    wwwpath = os.path.join(package_home(fc_globals), 'www')
    f = open(os.path.join(wwwpath, 'docs.stx'), 'r')
    _docs = f.read()
    f.close()
    _docs = stx2html(_docs)

    def __init__(self):
        self.actions = FormActionContainer()
        self.validators = FormValidatorContainer()

    security.declarePublic('docs')

    def docs(self):
        """Returns FormController docs formatted as HTML"""
        return self._docs

    def view(self, REQUEST, RESPONSE):
        """Invokes the default view."""
        return self.__call__(REQUEST, RESPONSE)

    def __call__(self, REQUEST, RESPONSE):
        """Invokes the default view."""
        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_main' % self.absolute_url())

    def _checkId(self, id):
        """See if an id is valid CMF/Plone id"""
        portal = getToolByName(self, 'portal_url').getPortalObject()
        if not id:
            return 'Empty id'
        s = bad_id(id)
        if s:
            return '\'%s\' is not a valid id' % (id)
        # extra checks for Plone sites
        if portal.__class__.__name__ == 'PloneSite':
            props = getToolByName(portal, 'portal_properties', None)
            if props is not None:
                if hasattr(props, 'site_properties') and \
                   hasattr(props.site_properties, 'invalid_ids'):
                    if id in props.site_properties.invalid_ids:
                        return '\'%s\' is a reserved id' % (id)

    # Web-accessible methods
    security.declareProtected(ManagePortal, 'listActionTypes')

    def listActionTypes(self):
        """Return a list of available action types."""
        keys = form_action_types.keys()
        keys.sort()
        action_types = []
        for k in keys:
            action_types.append(form_action_types.get(k))
        return action_types

    def validActionTypes(self):
        return form_action_types.keys()

    security.declareProtected(ManagePortal, 'listContextTypes')

    def listContextTypes(self):
        """Return list of possible types for template context objects"""
        types_tool = getToolByName(self, 'portal_types')
        return types_tool.listContentTypes()

    security.declareProtected(ManagePortal, 'listFormValidators')

    def listFormValidators(self, override=None, **kwargs):
        """Return a list of existing validators.  Validators can be filtered by
           specifying required attributes via kwargs"""
        return self.validators.getFiltered(**kwargs)

    security.declareProtected(ManagePortal, 'listFormActions')

    def listFormActions(self, override=None, **kwargs):
        """Return a list of existing actions.  Actions can be filtered by
           specifying required attributes via kwargs"""
        return self.actions.getFiltered(**kwargs)

    security.declareProtected(ManagePortal, 'manage_editFormValidators')

    def manage_editFormValidators(self, REQUEST):
        """Process form validator edit form"""
        self._editFormValidators(self.validators, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formValidatorsForm')

    security.declarePrivate('_editFormValidators')

    def _editFormValidators(self, container, REQUEST):
        for k in REQUEST.form.keys():
            if k.startswith('old_object_id_'):
                n = k[len('old_object_id_'):]
                old_object_id = REQUEST.form.get('old_object_id_' + n)
                old_context_type = REQUEST.form.get('old_context_type_' + n)
                old_button = REQUEST.form.get('old_button_' + n)
                container.delete(
                    FormValidatorKey(old_object_id, old_context_type,
                                     old_button, self))
                object_id = REQUEST.form.get('object_id_' + n)
                context_type = REQUEST.form.get('context_type_' + n)
                button = REQUEST.form.get('button_' + n)
                validators = REQUEST.form.get('validators_' + n)
                container.set(
                    FormValidator(object_id, context_type, button, validators,
                                  self))

    # Method for programmatically adding validators
    security.declareProtected(ManagePortal, 'addFormValidators')

    def addFormValidators(self, object_id, context_type, button, validators):
        self.validators.set(
            FormValidator(object_id, context_type, button, validators, self))

    security.declareProtected(ManagePortal, 'manage_addFormValidators')

    def manage_addFormValidators(self, REQUEST):
        """Process form validator add form"""
        self._addFormValidators(self.validators, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formValidatorsForm')

    security.declarePrivate('_addFormValidators')

    def _addFormValidators(self, container, REQUEST):
        object_id = REQUEST.form.get('new_object_id')
        context_type = REQUEST.form.get('new_context_type')
        button = REQUEST.form.get('new_button')
        validators = REQUEST.form.get('new_validators')
        container.set(
            FormValidator(object_id, context_type, button, validators, self))

    security.declareProtected(ManagePortal, 'manage_delFormValidators')

    def manage_delFormValidators(self, REQUEST):
        """Process form validator delete form"""
        self._delFormValidators(self.validators, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formValidatorsForm')

    security.declarePrivate('_delFormValidators')

    def _delFormValidators(self, container, REQUEST):
        for k in REQUEST.form.keys():
            if k.startswith('del_id_'):
                n = k[len('del_id_'):]
                old_object_id = REQUEST.form.get('old_object_id_' + n)
                old_context_type = REQUEST.form.get('old_context_type_' + n)
                old_button = REQUEST.form.get('old_button_' + n)
                container.delete(
                    FormValidatorKey(old_object_id, old_context_type,
                                     old_button, self))

    security.declareProtected(ManagePortal, 'manage_editFormActions')

    def manage_editFormActions(self, REQUEST):
        """Process form action edit form"""
        self._editFormActions(self.actions, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formActionsForm')

    security.declarePrivate('_editFormActions')

    def _editFormActions(self, container, REQUEST):
        for k in REQUEST.form.keys():
            if k.startswith('old_object_id_'):
                n = k[len('old_object_id_'):]
                old_object_id = REQUEST.form.get('old_object_id_' + n)
                old_status = REQUEST.form.get('old_status_' + n)
                old_context_type = REQUEST.form.get('old_context_type_' + n)
                old_button = REQUEST.form.get('old_button_' + n)
                container.delete(
                    FormActionKey(old_object_id, old_status, old_context_type,
                                  old_button, self))
                object_id = REQUEST.form.get('object_id_' + n)
                status = REQUEST.form.get('status_' + n)
                context_type = REQUEST.form.get('context_type_' + n)
                button = REQUEST.form.get('button_' + n)
                action_type = REQUEST.form.get('action_type_' + n)
                action_arg = REQUEST.form.get('action_arg_' + n)
                container.set(
                    FormAction(object_id, status, context_type, button,
                               action_type, action_arg, self))

    # Method for programmatically adding actions
    security.declareProtected(ManagePortal, 'addFormAction')

    def addFormAction(self, object_id, status, context_type, button,
                      action_type, action_arg):
        self.actions.set(
            FormAction(object_id, status, context_type, button, action_type,
                       action_arg, self))

    security.declareProtected(ManagePortal, 'manage_addFormAction')

    def manage_addFormAction(self, REQUEST):
        """Process form action add form"""
        self._addFormAction(self.actions, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formActionsForm')

    security.declarePrivate('_addFormAction')

    def _addFormAction(self, container, REQUEST):
        object_id = REQUEST.form.get('new_object_id')
        status = REQUEST.form.get('new_status').strip()
        context_type = REQUEST.form.get('new_context_type').strip()
        button = REQUEST.form.get('new_button').strip()
        action_type = REQUEST.form.get('new_action_type').strip()
        action_arg = REQUEST.form.get('new_action_arg').strip()
        container.set(
            FormAction(object_id, status, context_type, button, action_type,
                       action_arg, self))

    security.declareProtected(ManagePortal, 'manage_delFormActions')

    def manage_delFormActions(self, REQUEST):
        """Process form action delete form"""
        self._delFormActions(self.actions, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formActionsForm')

    security.declarePrivate('_delFormActions')

    def _delFormActions(self, container, REQUEST):
        for k in REQUEST.form.keys():
            if k.startswith('del_id_'):
                n = k[len('del_id_'):]
                old_object_id = REQUEST.form.get('old_object_id_' + n)
                old_status = REQUEST.form.get('old_status_' + n)
                old_context_type = REQUEST.form.get('old_context_type_' + n)
                old_button = REQUEST.form.get('old_button_' + n)
                container.delete(
                    FormActionKey(old_object_id, old_status, old_context_type,
                                  old_button, self))

    def getValidators(self, id, context_type, button):
        return self.validators.match(id, context_type, button)

    def getAction(self, id, status, context_type, button):
        return self.actions.match(id, status, context_type, button)

    def getState(self, obj, is_validator):
        id = obj.id
        controller_state = self.REQUEST.get('controller_state', None)
        # The variable 'env' is a dictionary that is passed
        # along using record variables on forms so that you can keep
        # some state between different forms.
        env = self.REQUEST.get('form_env', {})
        # Make sure controller_state is something generated by us, not something submitted via POST or GET
        if controller_state and getattr(controller_state, '__class__',
                                        None) != ControllerState:
            controller_state = None

        if not is_validator:
            # Construct a new controller state object or clear out the existing
            # one unless we are in a validator script.
            if controller_state is None:
                # XXX - errors={} shouldn't need to be set here, but without it
                # I encountered what looks like a weird Zope caching bug.
                # To reproduce, install CMFFormControllerDemo, go to portal_skins
                # then to CMFFormControllerDemo, then click on test_form and
                # click the Test tab.  Submit the form with an empty text box.
                # Now back up to the ZMI and click the Test tab again.  If
                # errors={} is left out in the line below, ControllerState.__init__()
                # gets called with errors set to a non-empty value (more
                # precisely, it is set to the value that was in REQUEST.controller_state.
                # Yikes!
                controller_state = ControllerState(errors={})
            else:
                # clear out values we don't want to carry over from previous states.
                controller_state.setStatus('success')
                controller_state.setNextAction(None)
                #controller_state.setButton(None)
            controller_state.set(id=id, context=obj.__parent__)
        else:
            if controller_state is None:
                raise ValueError, 'No controller state available.  ' + \
                    'This commonly occurs when a ControllerValidator (.vpy) ' + \
                    'script is invoked via the validation mechanism in the ' + \
                    'portal_form tool.  If you are using a package designed to ' + \
                    'be used with portal_form, you are probably inadvertently ' + \
                    'invoking a validator designed for use with CMFFormController (e.g. validate_id).  ' + \
                    'If you are using a package designed to be used with CMFFormController, you probably ' + \
                    'have a "portal_form" in your URL that needs to be removed.'
        controller_state._setValidating(is_validator)
        # Pass environment along, with care so we don't override
        # existing variables.
        for k, v in env.items():
            controller_state.kwargs.setdefault(k, v)
        self.REQUEST.set('controller_state', controller_state)
        return controller_state

    def validate(self, controller_state, REQUEST, validators, argdict=None):
        if argdict is None:
            args = REQUEST.args
            kwargs = REQUEST
        else:
            args = ()
            if REQUEST is None:
                kwargs = argdict
            else:
                kwargs = {}
                for k in REQUEST.keys():
                    if k in ('SESSION', ):
                        continue
                    kwargs[k] = REQUEST[k]
                kwargs.update(argdict)
        context = controller_state.getContext()
        if validators is None:
            REQUEST.set('controller_state', controller_state)
            return controller_state
        for v in validators:
            REQUEST.set('controller_state', controller_state)
            if controller_state.hasValidated(v):
                continue
            try:
                # make sure validator exists
                obj = context.restrictedTraverse(v, default=None)
                if obj is None:
                    raise ValueError, 'Unable to find validator %s\n' % str(v)
                if not getattr(obj, 'is_validator', 1):
                    raise ValueError, '%s is not a CMFFormController validator' % str(
                        v)
                REQUEST = controller_state.getContext().REQUEST
                controller_state = mapply(obj,
                                          args,
                                          kwargs,
                                          call_object,
                                          1,
                                          missing_name,
                                          dont_publish_class,
                                          REQUEST,
                                          bind=1)
                if controller_state is None or getattr(
                        controller_state, '__class__',
                        None) != ControllerState:
                    raise ValueError, 'Validator %s did not return the state object' % str(
                        v)
                controller_state._addValidator(v)
            except ValidationError, e:
                # if a validator raises a ValidatorException, execution of
                # validators is halted and the controller_state is set to
                # the controller_state embedded in the exception
                controller_state = e.controller_state
                state_class = getattr(controller_state, '__class__', None)
                if state_class != ControllerState:
                    raise Exception, 'Bad ValidationError state (type = %s)' % str(
                        state_class)
                break
            state_class = getattr(controller_state, '__class__', None)
            if state_class != ControllerState:
                raise Exception, 'Bad validator return type from validator %s (%s)' % (
                    str(v), str(state_class))
            REQUEST.set('controller_state', controller_state)

        REQUEST.set('controller_state', controller_state)
        return controller_state
Ejemplo n.º 3
0
class ControllerBase:
    """Common functions for objects controlled by portal_form_controller"""

    security = ClassSecurityInfo()
    security.declareObjectProtected(View)

    security.declareProtected(ManagePortal, 'manage_formActionsForm')
    manage_formActionsForm = PageTemplateFile('www/manage_formActionsForm',
                                              globals())
    manage_formActionsForm.__name__ = 'manage_formActionsForm'

    security.declareProtected(ManagePortal, 'manage_formValidatorsForm')
    manage_formValidatorsForm = PageTemplateFile(
        'www/manage_formValidatorsForm', globals())
    manage_formValidatorsForm.__name__ = 'manage_formValidatorsForm'

    def _updateActions(self, container, old_id, new_id, move):
        """Copy action overrides stored in portal_form_controller from one
        object id to another"""
        actions = container.getFiltered(object_id=old_id)
        for a in actions:
            # if not container.match(new_id, a.getStatus(), a.getContextType(), a.getButton()):
            container.set(
                FormAction(new_id, a.getStatus(), a.getContextType(),
                           a.getButton(), a.getActionType(), a.getActionArg()))
        if move:
            for a in actions:
                container.delete(a.getKey())

    def _updateValidators(self, container, old_id, new_id, move):
        """Copy validator overrides stored in portal_form_controller from one
        object id to another"""
        validators = container.getFiltered(object_id=old_id)
        for v in validators:
            # if not container.match(new_id, v.getContextType(), v.getButton()):
            container.set(
                FormValidator(new_id, v.getContextType(), v.getButton(),
                              v.getValidators()))
        if move:
            for v in validators:
                container.delete(v.getKey())

    def _base_notifyOfCopyTo(self, container, op=0):
        self._old_id = self.getId()
        if op == 0:  # copy
            self._cloned_object_path = self.getPhysicalPath()

    def _fixup_old_ids(self, old_id):
        fc = getToolByName(self, 'portal_form_controller')
        id = self.getId()
        if old_id != id:
            if hasattr(aq_base(self), 'actions'):
                self._updateActions(
                    self.actions, old_id, id,
                    move=1)  # swap the ids for the default actions
                self._updateActions(fc.actions, old_id, id,
                                    move=0)  # copy the overrides
            if hasattr(aq_base(self), 'validators'):
                self._updateValidators(
                    self.validators, old_id, id,
                    move=1)  # swap the ids for the default validators
                self._updateValidators(fc.validators, old_id, id,
                                       move=0)  # copy the overrides

    def _base_manage_afterAdd(self, object, container):
        old_id = getattr(self, '_old_id', None)
        if old_id:
            self._fixup_old_ids(old_id)
            delattr(self, '_old_id')

    def _base_manage_afterClone(self, object):
        # clean up the old object
        cloned_object_path = getattr(self, '_cloned_object_path')
        cloned_object = self.getPhysicalRoot().unrestrictedTraverse(
            cloned_object_path)
        delattr(cloned_object, '_old_id')
        delattr(cloned_object, '_cloned_object_path')
        # clean up the new object
        delattr(self, '_cloned_object_path')

    security.declareProtected(ManagePortal, 'listActionTypes')

    def listActionTypes(self):
        """Return a list of available action types."""
        return getToolByName(self, 'portal_form_controller').listActionTypes()

    security.declareProtected(ManagePortal, 'listFormValidators')

    def listFormValidators(self, override, **kwargs):
        """Return a list of existing validators.  Validators can be filtered by
           specifying required attributes via kwargs"""
        controller = getToolByName(self, 'portal_form_controller')
        if override:
            return controller.validators.getFiltered(**kwargs)
        else:
            return self.validators.getFiltered(**kwargs)

    security.declareProtected(ManagePortal, 'listFormActions')

    def listFormActions(self, override, **kwargs):
        """Return a list of existing actions.  Actions can be filtered by
           specifying required attributes via kwargs"""
        controller = getToolByName(self, 'portal_form_controller')
        if override:
            return controller.actions.getFiltered(**kwargs)
        else:
            return self.actions.getFiltered(**kwargs)

    security.declareProtected(ManagePortal, 'listContextTypes')

    def listContextTypes(self):
        """Return list of possible types for template context objects"""
        return getToolByName(self, 'portal_form_controller').listContextTypes()

    security.declareProtected(ManagePortal, 'manage_editFormValidators')

    def manage_editFormValidators(self, REQUEST):
        """Process form validator edit form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.validators
        else:
            container = self.validators
        controller._editFormValidators(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formValidatorsForm')

    security.declareProtected(ManagePortal, 'manage_addFormValidators')

    def manage_addFormValidators(self, REQUEST):
        """Process form validator add form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.validators
        else:
            container = self.validators
        controller._addFormValidators(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formValidatorsForm')

    security.declareProtected(ManagePortal, 'manage_delFormValidators')

    def manage_delFormValidators(self, REQUEST):
        """Process form validator delete form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.validators
        else:
            container = self.validators
        controller._delFormValidators(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formValidatorsForm')

    security.declareProtected(ManagePortal, 'manage_editFormActions')

    def manage_editFormActions(self, REQUEST):
        """Process form action edit form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.actions
        else:
            container = self.actions
        controller._editFormActions(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formActionsForm')

    security.declareProtected(ManagePortal, 'manage_addFormAction')

    def manage_addFormAction(self, REQUEST):
        """Process form action add form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.actions
        else:
            container = self.actions
        controller._addFormAction(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formActionsForm')

    security.declareProtected(ManagePortal, 'manage_delFormActions')

    def manage_delFormActions(self, REQUEST):
        """Process form action delete form"""
        controller = getToolByName(self, 'portal_form_controller')
        if REQUEST.form.get('override', 0):
            container = controller.actions
        else:
            container = self.actions
        controller._delFormActions(container, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url() +
                                         '/manage_formActionsForm')

    def getNext(self, controller_state, REQUEST):
        id = self.getId()
        status = controller_state.getStatus()
        context = controller_state.getContext()
        context_base = aq_base(context)

        context_type = getattr(context_base, 'portal_type', None)
        if context_type is None:
            context_type = getattr(context_base, '__class__', None)
            if context_type:
                context_type = getattr(context_type, '__name__', None)

        button = controller_state.getButton()
        controller = getToolByName(aq_inner(self), 'portal_form_controller')

        next_action = None
        try:
            next_action = controller.getAction(id, status, context_type,
                                               button)
        except ValueError:
            pass
        if next_action is None:
            try:
                if getattr(context_base, 'formcontroller_actions',
                           None) is not None:
                    next_action = context.formcontroller_actions.match(
                        id, status, context_type, button)
            except ValueError:
                pass
        if next_action is None:
            try:
                next_action = self.actions.match(id, status, context_type,
                                                 button)
            except ValueError:
                pass
            if next_action is None:
                next_action = controller_state.getNextAction()
                if next_action is None:
                    # default for failure is to traverse to the form
                    if status == 'failure':
                        next_action = FormAction(id, status, ANY_CONTEXT,
                                                 ANY_BUTTON, 'traverse_to',
                                                 'string:%s' % id, controller)
                    if next_action is None:
                        metadata_actions = [
                            str(a)
                            for a in self.actions.getFiltered(object_id=id)
                        ]
                        zmi_actions = [
                            str(a) for a in controller.actions.getFiltered(
                                object_id=id)
                        ]
                        raise ValueError, 'No next action found for %s.%s.%s.%s\nMetadata actions:\n%s\n\nZMI actions:\n%s\n' % \
                            (id, status, context_type, button, '\n'.join(metadata_actions), '\n'.join(zmi_actions))

        REQUEST.set('controller_state', controller_state)
        return next_action.getAction()(controller_state)

    def getButton(self, controller_state, REQUEST):
        buttons = []
        for k in REQUEST.form.keys():
            if k.startswith('form.button.'):
                buttons.append(k)
        if buttons:
            # Clicking on an image button results in 2 button variables in REQUEST.form
            # (maybe 3),namely form.button.button_name.x, form.button.button_name.y, and
            # possibly form.button.button_name (not for IE, though)
            # If we see more than one key with the button prefix, try to handle sensibly.
            if len(buttons) > 1:
                buttons.sort(lambda x, y: cmp(len(x), len(y)))
                if buttons[0].endswith('.x') or buttons[0].endswith('.y'):
                    buttons[0] = buttons[0][:-2]
            button = buttons[0][len('form.button.'):]
            controller_state.setButton(button)
        return controller_state

    def getValidators(self, controller_state, REQUEST):
        controller = getToolByName(self, 'portal_form_controller')
        context = controller_state.getContext()
        context_type = controller._getTypeName(context)
        button = controller_state.getButton()

        validators = None
        try:
            validators = controller.validators.match(self.id, context_type,
                                                     button)
            if validators is not None:
                return validators
        except ValueError:
            pass
        try:
            if hasattr(aq_base(context), 'formcontroller_validators'):
                validators = context.formcontroller_validators.match(
                    self.id, context_type, button)
                if validators is not None:
                    return validators
        except ValueError:
            pass
        try:
            validators = self.validators.match(self.id, context_type, button)
            if validators is not None:
                return validators
        except ValueError:
            pass
        return FormValidator(self.id, ANY_CONTEXT, ANY_BUTTON, [])

    def _read_action_metadata(self, id, filepath):
        self.actions = FormActionContainer()

        metadata = FSMetadata(filepath)
        cfg = CMFConfigParser()
        if os.path.exists(filepath + '.metadata'):
            cfg.read(filepath + '.metadata')
            _buttons_for_status = {}

            actions = metadata._getSectionDict(cfg, 'actions')
            if actions is None:
                actions = {}

            for (k, v) in actions.items():
                # action.STATUS.CONTEXT_TYPE.BUTTON = ACTION_TYPE:ACTION_ARG
                component = k.split('.')
                while len(component) < 4:
                    component.append('')
                if component[0] != 'action':
                    raise ValueError, '%s: Format for .metadata actions is action.STATUS.CONTEXT_TYPE.BUTTON = ACTION_TYPE:ACTION_ARG (not %s)' % (
                        filepath, k)
                act = v.split(':', 1)
                while len(act) < 2:
                    act.append('')

                context_type = component[2]
                self.actions.set(
                    FormAction(id, component[1], component[2], component[3],
                               act[0], act[1]))

                status_key = str(component[1]) + '.' + str(context_type)
                if _buttons_for_status.has_key(status_key):
                    _buttons_for_status[status_key].append(component[3])
                else:
                    _buttons_for_status[status_key] = [component[3]]

            for (k, v) in _buttons_for_status.items():
                if v and not '' in v:
                    sk = k.split('.')
                    status = sk[0]
                    content_type = sk[1]
                    if not status:
                        status = 'ANY'
                    if not content_type:
                        content_type = 'ANY'
                    log('%s: No default action specified for status %s, content type %s.  Users of IE can submit pages using the return key, resulting in no button in the REQUEST.  Please specify a default action for this case.'
                        % (str(filepath), status, content_type),
                        log_level=logging.DEBUG)

    def _read_validator_metadata(self, id, filepath):
        self.validators = FormValidatorContainer()

        metadata = FSMetadata(filepath)
        cfg = CMFConfigParser()
        if os.path.exists(filepath + '.metadata'):
            cfg.read(filepath + '.metadata')
            _buttons_for_status = {}

            validators = metadata._getSectionDict(cfg, 'validators')
            if validators is None:
                validators = {}
            for (k, v) in validators.items():
                # validators.CONTEXT_TYPE.BUTTON = LIST
                component = k.split('.')
                while len(component) < 3:
                    component.append('')
                if component[0] != 'validators':
                    raise ValueError, '%s: Format for .metadata validators is validators.CONTEXT_TYPE.BUTTON = LIST (not %s)' % (
                        filepath, k)

                context_type = component[1]
                self.validators.set(
                    FormValidator(id, component[1], component[2], v))

                status_key = str(context_type)
                if _buttons_for_status.has_key(status_key):
                    _buttons_for_status[status_key].append(component[2])
                else:
                    _buttons_for_status[status_key] = [component[2]]

            for (k, v) in _buttons_for_status.items():
                if v and not '' in v:
                    content_type = k
                    if not content_type:
                        content_type = 'ANY'
                    log('%s: No default validators specified for content type %s.  Users of IE can submit pages using the return key, resulting in no button in the REQUEST.  Please specify default validators for this case.'
                        % (str(filepath), content_type),
                        log_level=logging.DEBUG)

    security.declarePublic('writableDefaults')

    def writableDefaults(self):
        """Can default actions and validators be modified?"""
        return 1
Ejemplo n.º 4
0
class FormController(UniqueObject, SimpleItemWithProperties):
    """ """

    security = ClassSecurityInfo()

    id = 'portal_form_controller'
    title = 'Manages form validation and post-validation actions'
    meta_type= 'Form Controller Tool'

    implements(IFormControllerTool)

    manage_options = ( ({'label':'Overview', 'action':'manage_overview'},
                        {'label':'Documentation', 'action':'manage_docs'},
                        {'label': 'Validation', 'action': 'manage_formValidatorsForm'},
                        {'label': 'Actions', 'action': 'manage_formActionsForm'},
                        {'label': 'Purge', 'action': 'manage_purgeForm'},) +
                       SimpleItemWithProperties.manage_options)

    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = PageTemplateFile(os.path.join('www','manage_overview'), globals())
    manage_overview.__name__ = 'manage_overview'
    manage_overview._need__name__ = 0

    security.declareProtected(ManagePortal, 'manage_docs')
    manage_docs = PageTemplateFile(os.path.join('www','manage_docs'), globals())
    manage_docs.__name__ = 'manage_docs'

    security.declareProtected(ManagePortal, 'manage_formActionsForm')
    manage_formActionsForm = PageTemplateFile(os.path.join('www','manage_formActionsForm'), globals())
    manage_formActionsForm.__name__ = 'manage_formActionsForm'

    security.declareProtected(ManagePortal, 'manage_formValidatorsForm')
    manage_formValidatorsForm = PageTemplateFile(os.path.join('www','manage_formValidatorsForm'), globals())
    manage_formValidatorsForm.__name__ = 'manage_formValidatorsForm'

    security.declareProtected(ManagePortal, 'manage_purgeForm')
    manage_purgeForm = PageTemplateFile(os.path.join('www','manage_purgeForm'), globals())
    manage_purgeForm.__name__ = 'manage_purgeForm'

    # some aliases
    security.declareProtected(ManagePortal, 'manage_main')
    manage_main = manage_overview

    security.declareProtected(ManagePortal, 'index_html')
    index_html = None

    wwwpath = os.path.join(Globals.package_home(fc_globals), 'www')
    f = open(os.path.join(wwwpath, 'docs.stx'), 'r')
    _docs = f.read()
    f.close()
    _docs = HTML(_docs)


    def __init__(self):
        self.actions = FormActionContainer()
        self.validators = FormValidatorContainer()


    security.declarePublic('docs')
    def docs(self):
        """Returns FormController docs formatted as HTML"""
        return self._docs


    def view(self, REQUEST, RESPONSE):
        """Invokes the default view."""
        return self.__call__(REQUEST, RESPONSE)


    def __call__(self, REQUEST, RESPONSE):
        """Invokes the default view."""
        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_main' % self.absolute_url())


    def _checkId(self, id):
        """See if an id is valid CMF/Plone id"""
        portal = getToolByName(self, 'portal_url').getPortalObject()
        if not id:
            return 'Empty id'
        s = bad_id(id)
        if s:
            return '\'%s\' is not a valid id' % (id)
        # extra checks for Plone sites
        if portal.__class__.__name__ == 'PloneSite':
            if hasattr(portal, 'portal_properties') and \
                hasattr(portal.portal_properties, 'site_properties') and \
                hasattr(portal.portal_properties.site_properties, 'invalid_ids'):
                if id in portal.portal_properties.site_properties.invalid_ids:
                    return '\'%s\' is a reserved id' % (id)


    # Web-accessible methods
    security.declareProtected(ManagePortal, 'listActionTypes')
    def listActionTypes(self):
        """Return a list of available action types."""
        keys = form_action_types.keys()
        keys.sort()
        action_types = []
        for k in keys:
            action_types.append(form_action_types.get(k))
        return action_types


    def validActionTypes(self):
        return form_action_types.keys()


    security.declareProtected(ManagePortal, 'listContextTypes')
    def listContextTypes(self):
        """Return list of possible types for template context objects"""
        types_tool = getToolByName(self, 'portal_types')
        return types_tool.listContentTypes()


    security.declareProtected(ManagePortal, 'listFormValidators')
    def listFormValidators(self, override=None, **kwargs):
        """Return a list of existing validators.  Validators can be filtered by
           specifying required attributes via kwargs"""
        return self.validators.getFiltered(**kwargs)


    security.declareProtected(ManagePortal, 'listFormActions')
    def listFormActions(self, override=None, **kwargs):
        """Return a list of existing actions.  Actions can be filtered by
           specifying required attributes via kwargs"""
        return self.actions.getFiltered(**kwargs)


    security.declareProtected(ManagePortal, 'manage_editFormValidators')
    def manage_editFormValidators(self, REQUEST):
        """Process form validator edit form"""
        self._editFormValidators(self.validators, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formValidatorsForm')


    security.declarePrivate('_editFormValidators')
    def _editFormValidators(self, container, REQUEST):
        for k in REQUEST.form.keys():
            if k.startswith('old_object_id_'):
                n = k[len('old_object_id_'):]
                old_object_id = REQUEST.form.get('old_object_id_'+n)
                old_context_type = REQUEST.form.get('old_context_type_'+n)
                old_button = REQUEST.form.get('old_button_'+n)
                container.delete(FormValidatorKey(old_object_id, old_context_type, old_button, self))
                object_id = REQUEST.form.get('object_id_'+n)
                context_type = REQUEST.form.get('context_type_'+n)
                button = REQUEST.form.get('button_'+n)
                validators = REQUEST.form.get('validators_'+n)
                container.set(FormValidator(object_id, context_type, button, validators, self))


    # Method for programmatically adding validators
    security.declareProtected(ManagePortal, 'addFormValidators')
    def addFormValidators(self, object_id, context_type, button, validators):
        self.validators.set(FormValidator(object_id, context_type, button, validators, self))


    security.declareProtected(ManagePortal, 'manage_addFormValidators')
    def manage_addFormValidators(self, REQUEST):
        """Process form validator add form"""
        self._addFormValidators(self.validators, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formValidatorsForm')


    security.declarePrivate('_addFormValidators')
    def _addFormValidators(self, container, REQUEST):
        object_id = REQUEST.form.get('new_object_id')
        context_type = REQUEST.form.get('new_context_type')
        button = REQUEST.form.get('new_button')
        validators = REQUEST.form.get('new_validators')
        container.set(FormValidator(object_id, context_type, button, validators, self))


    security.declareProtected(ManagePortal, 'manage_delFormValidators')
    def manage_delFormValidators(self, REQUEST):
        """Process form validator delete form"""
        self._delFormValidators(self.validators, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formValidatorsForm')


    security.declarePrivate('_delFormValidators')
    def _delFormValidators(self, container, REQUEST):
        for k in REQUEST.form.keys():
            if k.startswith('del_id_'):
                n = k[len('del_id_'):]
                old_object_id = REQUEST.form.get('old_object_id_'+n)
                old_context_type = REQUEST.form.get('old_context_type_'+n)
                old_button = REQUEST.form.get('old_button_'+n)
                container.delete(FormValidatorKey(old_object_id, old_context_type, old_button, self))


    security.declareProtected(ManagePortal, 'manage_editFormActions')
    def manage_editFormActions(self, REQUEST):
        """Process form action edit form"""
        self._editFormActions(self.actions, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formActionsForm')


    security.declarePrivate('_editFormActions')
    def _editFormActions(self, container, REQUEST):
        for k in REQUEST.form.keys():
            if k.startswith('old_object_id_'):
                n = k[len('old_object_id_'):]
                old_object_id = REQUEST.form.get('old_object_id_'+n)
                old_status = REQUEST.form.get('old_status_'+n)
                old_context_type = REQUEST.form.get('old_context_type_'+n)
                old_button = REQUEST.form.get('old_button_'+n)
                container.delete(FormActionKey(old_object_id, old_status, old_context_type, old_button, self))
                object_id = REQUEST.form.get('object_id_'+n)
                status = REQUEST.form.get('status_'+n)
                context_type = REQUEST.form.get('context_type_'+n)
                button = REQUEST.form.get('button_'+n)
                action_type = REQUEST.form.get('action_type_'+n)
                action_arg = REQUEST.form.get('action_arg_'+n)
                container.set(FormAction(object_id, status, context_type, button, action_type, action_arg, self))


    # Method for programmatically adding actions
    security.declareProtected(ManagePortal, 'addFormAction')
    def addFormAction(self, object_id, status, context_type, button, action_type, action_arg):
        self.actions.set(FormAction(object_id, status, context_type, button, action_type, action_arg, self))


    security.declareProtected(ManagePortal, 'manage_addFormAction')
    def manage_addFormAction(self, REQUEST):
        """Process form action add form"""
        self._addFormAction(self.actions, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formActionsForm')


    security.declarePrivate('_addFormAction')
    def _addFormAction(self, container, REQUEST):
        object_id = REQUEST.form.get('new_object_id')
        status = REQUEST.form.get('new_status').strip()
        context_type = REQUEST.form.get('new_context_type').strip()
        button = REQUEST.form.get('new_button').strip()
        action_type = REQUEST.form.get('new_action_type').strip()
        action_arg = REQUEST.form.get('new_action_arg').strip()
        container.set(FormAction(object_id, status, context_type, button, action_type, action_arg, self))


    security.declareProtected(ManagePortal, 'manage_delFormActions')
    def manage_delFormActions(self, REQUEST):
        """Process form action delete form"""
        self._delFormActions(self.actions, REQUEST)
        return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formActionsForm')


    security.declarePrivate('_delFormActions')
    def _delFormActions(self, container, REQUEST):
        for k in REQUEST.form.keys():
            if k.startswith('del_id_'):
                n = k[len('del_id_'):]
                old_object_id = REQUEST.form.get('old_object_id_'+n)
                old_status = REQUEST.form.get('old_status_'+n)
                old_context_type = REQUEST.form.get('old_context_type_'+n)
                old_button = REQUEST.form.get('old_button_'+n)
                container.delete(FormActionKey(old_object_id, old_status, old_context_type, old_button, self))


    def getValidators(self, id, context_type, button):
        return self.validators.match(id, context_type, button)


    def getAction(self, id, status, context_type, button):
        return self.actions.match(id, status, context_type, button)


    def getState(self, obj, is_validator):
        id = obj.id
        controller_state = self.REQUEST.get('controller_state', None)
        # The variable 'env' is a dictionary that is passed
        # along using record variables on forms so that you can keep
        # some state between different forms.
        env = self.REQUEST.get('form_env', {})
        # Make sure controller_state is something generated by us, not something submitted via POST or GET
        if controller_state and getattr(controller_state, '__class__', None) != ControllerState:
            controller_state = None

        if not is_validator:
            # Construct a new controller state object or clear out the existing
            # one unless we are in a validator script.
            if controller_state is None:
                # XXX - errors={} shouldn't need to be set here, but without it
                # I encountered what looks like a weird Zope caching bug.
                # To reproduce, install CMFFormControllerDemo, go to portal_skins
                # then to CMFFormControllerDemo, then click on test_form and
                # click the Test tab.  Submit the form with an empty text box.
                # Now back up to the ZMI and click the Test tab again.  If
                # errors={} is left out in the line below, ControllerState.__init__()
                # gets called with errors set to a non-empty value (more
                # precisely, it is set to the value that was in REQUEST.controller_state.
                # Yikes!
                controller_state = ControllerState(errors={})
            else:
                # clear out values we don't want to carry over from previous states.
                controller_state.setStatus('success')
                controller_state.setNextAction(None)
                #controller_state.setButton(None)
            controller_state.set(id=id, context=obj.getParentNode())
        else:
            if controller_state is None:
                raise ValueError, 'No controller state available.  ' + \
                    'This commonly occurs when a ControllerValidator (.vpy) ' + \
                    'script is invoked via the validation mechanism in the ' + \
                    'portal_form tool.  If you are using a package designed to ' + \
                    'be used with portal_form, you are probably inadvertently ' + \
                    'invoking a validator designed for use with CMFFormController (e.g. validate_id).  ' + \
                    'If you are using a package designed to be used with CMFFormController, you probably ' + \
                    'have a "portal_form" in your URL that needs to be removed.'
        controller_state._setValidating(is_validator)
        # Pass environment along, with care so we don't override
        # existing variables.
        for k, v in env.items():
            controller_state.kwargs.setdefault(k, v)
        self.REQUEST.set('controller_state', controller_state)
        return controller_state


    def validate(self, controller_state, REQUEST, validators, argdict=None):
        if argdict is None:
            args = REQUEST.args
            kwargs = REQUEST
        else:
            args = ()
            if REQUEST is None:
                kwargs = argdict
            else:
                kwargs = {}
                for k in REQUEST.keys():
                    if k in ('SESSION',):
                        continue
                    kwargs[k] = REQUEST[k]
                kwargs.update(argdict)
        context = controller_state.getContext()
        if validators is None:
            REQUEST.set('controller_state', controller_state)
            return controller_state
        for v in validators:
            REQUEST.set('controller_state', controller_state)
            if controller_state.hasValidated(v):
                continue
            try:
                # make sure validator exists
                obj = context.restrictedTraverse(v, default=None)
                if obj is None:
                    raise ValueError, 'Unable to find validator %s\n' % str(v)
                if not getattr(obj, 'is_validator', 1):
                    raise ValueError, '%s is not a CMFFormController validator' % str(v)
                REQUEST = controller_state.getContext().REQUEST
                controller_state = mapply(obj, args, kwargs,
                                          call_object, 1, missing_name, dont_publish_class,
                                          REQUEST, bind=1)
                if controller_state is None or getattr(controller_state, '__class__', None) != ControllerState:
                    raise ValueError, 'Validator %s did not return the state object' % str(v)
                controller_state._addValidator(v)
            except ValidationError, e:
                # if a validator raises a ValidatorException, execution of
                # validators is halted and the controller_state is set to
                # the controller_state embedded in the exception
                controller_state = e.controller_state
                state_class = getattr(controller_state, '__class__', None)
                if state_class != ControllerState:
                    raise Exception, 'Bad ValidationError state (type = %s)' % str(state_class)
                break
            state_class = getattr(controller_state, '__class__', None)
            if state_class != ControllerState:
                raise Exception, 'Bad validator return type from validator %s (%s)' % (str(v), str(state_class))
            REQUEST.set('controller_state', controller_state)

        REQUEST.set('controller_state', controller_state)
        return controller_state