Back to index

plone3  3.1.7
ControllerBase.py
Go to the documentation of this file.
00001 import os
00002 from Acquisition import aq_base, aq_inner
00003 from Globals import InitializeClass
00004 from AccessControl import ClassSecurityInfo
00005 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
00006 from Products.CMFCore.permissions import View, ManagePortal
00007 from Products.CMFCore.utils import getToolByName
00008 from Products.CMFCore.FSMetadata import FSMetadata, CMFConfigParser
00009 from FormAction import FormAction, FormActionContainer
00010 from FormValidator import FormValidator, FormValidatorContainer
00011 from globalVars import ANY_CONTEXT, ANY_BUTTON
00012 from utils import log
00013 
00014 class ControllerBase:
00015     """Common functions for objects controlled by portal_form_controller"""
00016 
00017     security = ClassSecurityInfo()
00018     security.declareObjectProtected(View)
00019 
00020     security.declareProtected(ManagePortal, 'manage_formActionsForm')
00021     manage_formActionsForm = PageTemplateFile('www/manage_formActionsForm', globals())
00022     manage_formActionsForm.__name__ = 'manage_formActionsForm'
00023 
00024     security.declareProtected(ManagePortal, 'manage_formValidatorsForm')
00025     manage_formValidatorsForm = PageTemplateFile('www/manage_formValidatorsForm', globals())
00026     manage_formValidatorsForm.__name__ = 'manage_formValidatorsForm'
00027 
00028     def _updateActions(self, container, old_id, new_id, move):
00029         """Copy action overrides stored in portal_form_controller from one 
00030         object id to another"""
00031         actions = container.getFiltered(object_id=old_id)
00032         for a in actions:
00033             # if not container.match(new_id, a.getStatus(), a.getContextType(), a.getButton()):
00034             container.set(FormAction(new_id, a.getStatus(), a.getContextType(),
00035                            a.getButton(), a.getActionType(), a.getActionArg()))
00036         if move:
00037             for a in actions:
00038                 container.delete(a.getKey())
00039                 
00040     def _updateValidators(self, container, old_id, new_id, move):
00041         """Copy validator overrides stored in portal_form_controller from one 
00042         object id to another"""
00043         validators = container.getFiltered(object_id=old_id)
00044         for v in validators:
00045             # if not container.match(new_id, v.getContextType(), v.getButton()):
00046             container.set(FormValidator(new_id, v.getContextType(), v.getButton(), v.getValidators()))
00047         if move:
00048             for v in validators:
00049                 container.delete(v.getKey())
00050         
00051     def _base_notifyOfCopyTo(self, container, op=0):
00052         self._old_id = self.getId()
00053         if op==0:  # copy
00054             self._cloned_object_path = self.getPhysicalPath()
00055 
00056     def _fixup_old_ids(self, old_id):
00057         fc = getToolByName(self, 'portal_form_controller')
00058         id = self.getId()
00059         if old_id != id:
00060             if hasattr(aq_base(self), 'actions'):
00061                 self._updateActions(self.actions, old_id, id, move=1) # swap the ids for the default actions
00062                 self._updateActions(fc.actions, old_id, id, move=0) # copy the overrides
00063             if hasattr(aq_base(self), 'validators'):
00064                 self._updateValidators(self.validators, old_id, id, move=1) # swap the ids for the default validators
00065                 self._updateValidators(fc.validators, old_id, id, move=0) # copy the overrides
00066 
00067     def _base_manage_afterAdd(self, object, container):
00068         old_id = getattr(self, '_old_id', None)
00069         if old_id:
00070             self._fixup_old_ids(old_id)
00071             delattr(self, '_old_id')
00072 
00073     def _base_manage_afterClone(self, object):
00074         # clean up the old object
00075         cloned_object_path = getattr(self, '_cloned_object_path')
00076         cloned_object = self.getPhysicalRoot().unrestrictedTraverse(cloned_object_path)
00077         delattr(cloned_object, '_old_id')
00078         delattr(cloned_object, '_cloned_object_path')
00079         # clean up the new object
00080         delattr(self, '_cloned_object_path')
00081 
00082     security.declareProtected(ManagePortal, 'listActionTypes')
00083     def listActionTypes(self):
00084         """Return a list of available action types."""
00085         return getToolByName(self, 'portal_form_controller').listActionTypes()
00086 
00087     security.declareProtected(ManagePortal, 'listFormValidators')
00088     def listFormValidators(self, override, **kwargs):
00089         """Return a list of existing validators.  Validators can be filtered by
00090            specifying required attributes via kwargs"""
00091         controller = getToolByName(self, 'portal_form_controller')
00092         if override:
00093             return controller.validators.getFiltered(**kwargs)
00094         else:
00095             return self.validators.getFiltered(**kwargs)
00096 
00097 
00098     security.declareProtected(ManagePortal, 'listFormActions')
00099     def listFormActions(self, override, **kwargs):
00100         """Return a list of existing actions.  Actions can be filtered by
00101            specifying required attributes via kwargs"""
00102         controller = getToolByName(self, 'portal_form_controller')
00103         if override:
00104             return controller.actions.getFiltered(**kwargs)
00105         else:
00106             return self.actions.getFiltered(**kwargs)
00107 
00108 
00109     security.declareProtected(ManagePortal, 'listContextTypes')
00110     def listContextTypes(self):
00111         """Return list of possible types for template context objects"""
00112         return getToolByName(self, 'portal_form_controller').listContextTypes()
00113 
00114 
00115     security.declareProtected(ManagePortal, 'manage_editFormValidators')
00116     def manage_editFormValidators(self, REQUEST):
00117         """Process form validator edit form"""
00118         controller = getToolByName(self, 'portal_form_controller')
00119         if REQUEST.form.get('override', 0):
00120             container = controller.validators
00121         else:
00122             container = self.validators
00123         controller._editFormValidators(container, REQUEST)
00124         return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formValidatorsForm')
00125 
00126 
00127     security.declareProtected(ManagePortal, 'manage_addFormValidators')
00128     def manage_addFormValidators(self, REQUEST):
00129         """Process form validator add form"""
00130         controller = getToolByName(self, 'portal_form_controller')
00131         if REQUEST.form.get('override', 0):
00132             container = controller.validators
00133         else:
00134             container = self.validators
00135         controller._addFormValidators(container, REQUEST)
00136         return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formValidatorsForm')
00137 
00138 
00139     security.declareProtected(ManagePortal, 'manage_delFormValidators')
00140     def manage_delFormValidators(self, REQUEST):
00141         """Process form validator delete form"""
00142         controller = getToolByName(self, 'portal_form_controller')
00143         if REQUEST.form.get('override', 0):
00144             container = controller.validators
00145         else:
00146             container = self.validators
00147         controller._delFormValidators(container, REQUEST)
00148         return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formValidatorsForm')
00149 
00150 
00151     security.declareProtected(ManagePortal, 'manage_editFormActions')
00152     def manage_editFormActions(self, REQUEST):
00153         """Process form action edit form"""
00154         controller = getToolByName(self, 'portal_form_controller')
00155         if REQUEST.form.get('override', 0):
00156             container = controller.actions
00157         else:
00158             container = self.actions
00159         controller._editFormActions(container, REQUEST)
00160         return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formActionsForm')
00161 
00162 
00163     security.declareProtected(ManagePortal, 'manage_addFormAction')
00164     def manage_addFormAction(self, REQUEST):
00165         """Process form action add form"""
00166         controller = getToolByName(self, 'portal_form_controller')
00167         if REQUEST.form.get('override', 0):
00168             container = controller.actions
00169         else:
00170             container = self.actions
00171         controller._addFormAction(container, REQUEST)
00172         return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formActionsForm')
00173 
00174 
00175     security.declareProtected(ManagePortal, 'manage_delFormActions')
00176     def manage_delFormActions(self, REQUEST):
00177         """Process form action delete form"""
00178         controller = getToolByName(self, 'portal_form_controller')
00179         if REQUEST.form.get('override', 0):
00180             container = controller.actions
00181         else:
00182             container = self.actions
00183         controller._delFormActions(container, REQUEST)
00184         return REQUEST.RESPONSE.redirect(self.absolute_url()+'/manage_formActionsForm')
00185 
00186 
00187     def getNext(self, controller_state, REQUEST):
00188         id = self.getId()
00189         status = controller_state.getStatus()
00190         context = controller_state.getContext()
00191         context_base = aq_base(context)
00192 
00193         context_type = getattr(context_base, 'portal_type', None)
00194         if context_type is None:
00195             context_type = getattr(context_base, '__class__', None)
00196             if context_type:
00197                 context_type = getattr(context_type, '__name__', None)
00198 
00199         button = controller_state.getButton()
00200         controller = getToolByName(aq_inner(self), 'portal_form_controller')
00201 
00202         next_action = None
00203         try:
00204             next_action = controller.getAction(id, status, context_type, button)
00205         except ValueError:
00206             pass
00207         if next_action is None:
00208             try:
00209                 if getattr(context_base, 'formcontroller_actions', None) is not None:
00210                     next_action = context.formcontroller_actions.match(id, status, context_type, button)
00211             except ValueError:
00212                 pass
00213         if next_action is None:
00214             try:
00215                 next_action = self.actions.match(id, status, context_type, button)
00216             except ValueError:
00217                 pass
00218             if next_action is None:
00219                 next_action = controller_state.getNextAction()
00220                 if next_action is None:
00221                     # default for failure is to traverse to the form
00222                     if status == 'failure':
00223                         next_action=FormAction(id, status, ANY_CONTEXT, ANY_BUTTON, 'traverse_to', 'string:%s' % id, controller)
00224                     if next_action is None:
00225                         metadata_actions = [str(a) for a in self.actions.getFiltered(object_id=id)]
00226                         zmi_actions = [str(a) for a in controller.actions.getFiltered(object_id=id)]
00227                         raise ValueError, 'No next action found for %s.%s.%s.%s\nMetadata actions:\n%s\n\nZMI actions:\n%s\n' % \
00228                             (id, status, context_type, button, '\n'.join(metadata_actions), '\n'.join(zmi_actions))
00229 
00230         REQUEST.set('controller_state', controller_state)
00231         return next_action.getAction()(controller_state)
00232 
00233 
00234     def getButton(self, controller_state, REQUEST):
00235         buttons = []
00236         for k in REQUEST.form.keys():
00237             if k.startswith('form.button.'):
00238                 buttons.append(k)
00239         if buttons:
00240             # Clicking on an image button results in 2 button variables in REQUEST.form
00241             # (maybe 3),namely form.button.button_name.x, form.button.button_name.y, and
00242             # possibly form.button.button_name (not for IE, though)
00243             # If we see more than one key with the button prefix, try to handle sensibly.
00244             if len(buttons) > 1:
00245                 buttons.sort(lambda x, y: cmp(len(x), len(y)))
00246                 if buttons[0].endswith('.x') or buttons[0].endswith('.y'):
00247                     buttons[0] = buttons[0][:-2]
00248             button = buttons[0][len('form.button.'):]
00249             controller_state.setButton(button)
00250         return controller_state
00251 
00252 
00253     def getValidators(self, controller_state, REQUEST):
00254         controller = getToolByName(self, 'portal_form_controller')
00255         context = controller_state.getContext()
00256         context_type = controller._getTypeName(context)
00257         button = controller_state.getButton()
00258 
00259         validators = None
00260         try:
00261             validators = controller.validators.match(self.id, context_type, button)
00262             if validators is not None:
00263                 return validators
00264         except ValueError:
00265             pass
00266         try:
00267             if hasattr(aq_base(context), 'formcontroller_validators'):
00268                 validators = context.formcontroller_validators.match(self.id, context_type, button)
00269                 if validators is not None:
00270                     return validators
00271         except ValueError:
00272             pass
00273         try:
00274             validators = self.validators.match(self.id, context_type, button)
00275             if validators is not None:
00276                 return validators
00277         except ValueError:
00278             pass
00279         return FormValidator(self.id, ANY_CONTEXT, ANY_BUTTON, [])
00280 
00281 
00282     def _read_action_metadata(self, id, filepath):
00283         self.actions = FormActionContainer()
00284 
00285         metadata = FSMetadata(filepath)
00286         cfg = CMFConfigParser()
00287         if os.path.exists(filepath + '.metadata'):
00288             cfg.read(filepath + '.metadata')
00289             _buttons_for_status = {}
00290 
00291             actions = metadata._getSectionDict(cfg, 'actions')
00292             if actions is None:
00293                 actions = {}
00294 
00295             for (k, v) in actions.items():
00296                 # action.STATUS.CONTEXT_TYPE.BUTTON = ACTION_TYPE:ACTION_ARG
00297                 component = k.split('.')
00298                 while len(component) < 4:
00299                     component.append('')
00300                 if component[0] != 'action':
00301                     raise ValueError, '%s: Format for .metadata actions is action.STATUS.CONTEXT_TYPE.BUTTON = ACTION_TYPE:ACTION_ARG (not %s)' % (filepath, k)
00302                 act = v.split(':',1)
00303                 while len(act) < 2:
00304                     act.append('')
00305 
00306                 context_type = component[2]
00307                 self.actions.set(FormAction(id, component[1], component[2], component[3], act[0], act[1]))
00308 
00309                 status_key = str(component[1])+'.'+str(context_type)
00310                 if _buttons_for_status.has_key(status_key):
00311                     _buttons_for_status[status_key].append(component[3])
00312                 else:
00313                     _buttons_for_status[status_key] = [component[3]]
00314 
00315             for (k, v) in _buttons_for_status.items():
00316                 if v and not '' in v:
00317                     sk = k.split('.')
00318                     status = sk[0]
00319                     content_type = sk[1]
00320                     if not status:
00321                         status = 'ANY'
00322                     if not content_type:
00323                         content_type = 'ANY'
00324                     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))
00325                     
00326 
00327     def _read_validator_metadata(self, id, filepath):
00328         self.validators = FormValidatorContainer()
00329 
00330         metadata = FSMetadata(filepath)
00331         cfg = CMFConfigParser()
00332         if os.path.exists(filepath + '.metadata'):
00333             cfg.read(filepath + '.metadata')
00334             _buttons_for_status = {}
00335 
00336             validators = metadata._getSectionDict(cfg, 'validators')
00337             if validators is None:
00338                 validators = {}
00339             for (k, v) in validators.items():
00340                 # validators.CONTEXT_TYPE.BUTTON = LIST
00341                 component = k.split('.')
00342                 while len(component) < 3:
00343                     component.append('')
00344                 if component[0] != 'validators':
00345                     raise ValueError, '%s: Format for .metadata validators is validators.CONTEXT_TYPE.BUTTON = LIST (not %s)' % (filepath, k)
00346 
00347                 context_type = component[1]
00348                 self.validators.set(FormValidator(id, component[1], component[2], v))
00349 
00350                 status_key = str(context_type)
00351                 if _buttons_for_status.has_key(status_key):
00352                     _buttons_for_status[status_key].append(component[2])
00353                 else:
00354                     _buttons_for_status[status_key] = [component[2]]
00355 
00356             for (k, v) in _buttons_for_status.items():
00357                 if v and not '' in v:
00358                     content_type = k
00359                     if not content_type:
00360                         content_type = 'ANY'
00361                     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))
00362 
00363 
00364     security.declarePublic('writableDefaults')
00365     def writableDefaults(self):
00366         """Can default actions and validators be modified?"""
00367         return 1
00368 
00369 InitializeClass(ControllerBase)