import urllib import re import json from Acquisition import aq_inner from DateTime import DateTime from zope.interface import implements from zope.component import getMultiAdapter # BBB: compatibility with older plone versions try: # Plone < 4.3 from zope.app.container import interfaces INameChooser = interfaces.INameChooser except ImportError: # Plone >= 4.3 from zope.container.interfaces import INameChooser from zope.viewlet.interfaces import IViewletManager, IViewlet from plone.app.layout.navigation.root import getNavigationRoot from Products.CMFCore.utils import getToolByName from Products.CMFCore.interfaces import IAction, IActionCategory from Products.CMFCore.ActionInformation import Action, ActionCategory from Products.CMFCore.Expression import Expression from Products.CMFPlone import utils from Products.CMFPlone.browser.navigation import get_view_url from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from Products.Five.browser import BrowserView from Products.statusmessages.interfaces import IStatusMessage from quintagroup.plonetabs.config import PROPERTY_SHEET, FIELD_NAME from quintagroup.plonetabs.utils import setupViewletByName from quintagroup.plonetabs import messageFactory as _ from interfaces import IPloneTabsControlPanel ACTION_ATTRS = ["id", "title", "url_expr", "available_expr", "visible"] UI_ATTRS = {"id": "id", "title": "name", "url_expr": "action", "available_expr": "condition", "visible": "visible"} bad_id = re.compile(r'[^a-zA-Z0-9-_~,.$\(\)# @]').search cookie_name = 'ploneTabsMode' class PloneTabsControlPanel(): implements(IPloneTabsControlPanel) template = ViewPageTemplateFile("templates/plonetabs.pt") actionslist_template = ViewPageTemplateFile("templates/actionslist.pt") autogenerated_template = ViewPageTemplateFile("templates/autogenerated.pt") autogenerated_list = ViewPageTemplateFile("templates/autogeneratedlist.pt") link_template = ViewPageTemplateFile("templates/changemodelink.pt") # custom templates used to update page sections sections_template = ViewPageTemplateFile("templates/sections.pt") # configuration variables prefix = "tabslist_" sufix = "" def __call__(self): """Perform the update and redirect if necessary, or render the page""" postback = True ajaxback = False errors = {} form = self.request.form submitted = form.get('form.submitted', False) ajax_request = form.get('ajax_request', False) # action handler def handler(self, form) if ajax_request: ajaxback = self.ajax_postback(form, errors) if submitted: postback = self.submitted_postback(form, errors) mode = self.request.get(cookie_name, False) if mode in ('plain', 'rich'): # set cookie to remember the choice expires = (DateTime() + 365).toZone('GMT').rfc822() self.request.response.setCookie(cookie_name, mode, path='/', expires=expires) if ajaxback: return json.dumps(ajaxback) elif postback: return self.template(errors=errors) def submitted_postback(self, form, errors): """submitted postback""" if 'add.add' in form.keys(): postback = self.manage_addAction(form, errors) elif "edit.save" in form.keys(): postback = self.manage_editAction(form, errors) elif "edit.delete" in form.keys(): postback = self.manage_deleteAction(form, errors) elif "edit.moveup" in form.keys(): postback = self.manage_moveUpAction(form, errors) elif "edit.movedown" in form.keys(): postback = self.manage_moveDownAction(form, errors) elif "autogenerated.save" in form.keys(): postback = self.manage_setAutogeneration(form, errors) else: postback = True return postback def ajax_postback(self, form, errors): """ajax_postback ajaxback""" conv_dict = { "edit_moveact": "manage_ajax_moveAction", "category_change": "manage_ajax_changeCategory", "edit_delete": "manage_ajax_deleteAction", "edit_save": "manage_ajax_saveAction", "edit_cancel": "manage_ajax_cancelEditting", "tabslist_visible": "manage_ajax_toggleActionsVisibility", "roottabs_visible": "manage_ajax_toggleRootsVisibility", "generated_tabs": "manage_ajax_toggleGeneratedTabs", "add_add": "manage_ajax_addAction", } for method in conv_dict.keys(): if method in form.keys(): method_val = conv_dict.get(method) return getattr(self, method_val)(form, errors) return False @property def plone_portal_state(self): """plone_portal_state""" return getMultiAdapter((aq_inner(self.context), self.request), name='plone_portal_state') @property def portal_properties(self): """portal_properties""" return getToolByName(self.context, 'portal_properties', None) @property def portal_actions(self): """portal_actions""" return getToolByName(self.context, 'portal_actions') ######################################## # AJAX Methods ######################################## def manage_ajax_addAction(self, form, errs): # extract posted data resp_dict = {} cat_name = form['cat_name'] id, ie7bad_category, data = self.parseAddForm(self.request.form) # validate posted data errors = self.validateActionFields(cat_name, data) if not errors: action = self.addAction(cat_name, data) # add one more action to actions list content = self.getActionsList(category=cat_name, tabs=[action, ]) resp_dict['content'] = content resp_dict['status_code'] = 200 resp_dict['status_message'] = "%s action successfully added." % action.id else: resp_dict['status_message'] = "Please correct the indicated errors." resp_dict['status_code'] = 500 resp_dict['content'] = errors return resp_dict def manage_ajax_toggleGeneratedTabs(self, form, errs): """Toggle autogenaration setting on configlet""" resp_dict = {} errors = [] #TODO: parse, validate form checked = form['generated_tabs'] field = form['field'] if not errors: if checked == 'true': self.setSiteProperties(**{field: False}) message = _(u"Generated tabs switched on.") else: self.setSiteProperties(**{field: True}) message = _(u"Generated tabs switched off.") content = self.getGeneratedTabs() resp_dict['content'] = content resp_dict['status_code'] = 200 resp_dict['status_message'] = message else: resp_dict['status_message'] = errors resp_dict['status_code'] = 500 return resp_dict def manage_ajax_toggleRootsVisibility(self, form, errs): #Toggle visibility for portal actions resp_dict = {} errors = [] #TODO: parse, validate form id = form['orig_id'] checked = form['visibility'] portal = getMultiAdapter((aq_inner(self.context), self.request), name='plone_portal_state').portal() # remove prefix, added for making ids on configlet unique ("roottabs_") obj_id = id[len("roottabs_"):] if obj_id not in portal.objectIds(): errors.append("Object with %s id doesn't exist in portal root." % obj_id) checked = True if checked == 'true' else False if not errors: portal[obj_id].update(excludeFromNav=not checked) if checked: message = "%s object was included into navigation." % obj_id else: message = "%s object was excluded from navigation." % obj_id resp_dict['status_message'] = message resp_dict['status_code'] = 200 else: resp_dict['status_message'] = errors resp_dict['status_code'] = 500 return resp_dict def manage_ajax_toggleActionsVisibility(self, form, errs): #Toggle visibility for portal actions resp_dict = {} #TODO: parse, validate form id = form['orig_id'] cat_name = form['category'] checked = form['visibility'] act_id, category, action, errors = self.manage_validateAction(id, cat_name) if not errors: self.updateAction(act_id, cat_name, {'id': act_id, 'visible': (checked == 'true') or False}) if checked == 'true': message = "%s action is now visible." % act_id else: message = "%s action is now invisible." % act_id resp_dict['status_message'] = message resp_dict['status_code'] = 200 else: resp_dict['status_message'] = errors resp_dict['status_code'] = 500 return resp_dict def manage_ajax_deleteAction(self, form, errs): """Delete portal action with given id & category""" resp_dict = {} #TODO: parse, validate form id = form['orig_id'] cat_name = form['category'] act_id, category, action, errors = self.manage_validateAction(id, cat_name) if not errors: self.deleteAction(act_id, cat_name) resp_dict['status_message'] = "%s action successfully removed." % act_id resp_dict['status_code'] = 200 else: resp_dict['status_message'] = errors resp_dict['status_code'] = 500 return resp_dict def manage_ajax_cancelEditting(self, form, errs): """Hide edit form for given action""" resp_dict = {} #TODO: parse, validate form id = form['orig_id'] cat_name = form['category'] act_id, category, action, errors = self.manage_validateAction(id, cat_name) if not errors: content = self.getActionsList(category=cat_name, tabs=[action, ]) resp_dict['content'] = content resp_dict['status_message'] = "Changes discarded." resp_dict['status_code'] = 200 else: resp_dict['status_message'] = errors resp_dict['status_code'] = 500 return resp_dict def manage_validateAction(self, action_id, cat_name): """Check whether action with given id exists in cat_name category""" errors = [] act_id = "" category = "" action = "" act_id = action_id try: category = self.getActionCategory(cat_name) except Exception: errors.append("%s action category does not exist." % cat_name) try: action = category[act_id] except Exception: errors.append("No %s action in %s category." % (act_id, cat_name)) return (act_id, category, action, errors) def manage_ajax_saveAction(self, form, errs): """Manage Method to update action""" # extract posted data resp_dict = {} id, cat_name, data = self.parseEditForm(form) # get category and action to edit category = self.getActionCategory(cat_name) action = category[id] # validate posted data errors = self.validateActionFields( cat_name, data, allow_dup=(id == data['id'])) if not errors: action = self.updateAction(id, cat_name, data) content = self.getActionsList(category=cat_name, tabs=[action, ]) resp_dict['content'] = content resp_dict['status_code'] = 200 resp_dict['status_message'] = "%s action successfully updated." % action.id else: resp_dict['status_code'] = 500 resp_dict['content'] = errors resp_dict['status_message'] = "Please correct the indicated errors." return resp_dict def manage_ajax_moveAction(self, form, errs): resp_dict = {} cat_name = form['cat_name'] category = self.getActionCategory(cat_name) components = urllib.unquote(form['actions']).split('&') if self.sufix == '': ids = [component[len(self.prefix):] for component in components] else: ids = [component[len(self.prefix):-len(self.sufix)] for component in components] # do actual sorting resp = category.moveObjectsByDelta(ids, -len(category.objectIds())) if resp: resp_dict['status_code'] = 200 resp_dict['status_message'] = "Actions successfully sorted." else: resp_dict['status_code'] = 500 resp_dict['status_message'] = "There was error while sorting, or list not changed" return resp_dict def manage_ajax_changeCategory(self, form, errs): resp_dict = {} errors = [] """Change action category to manage""" cat_name = form['category'] # update actions list resp_dict['actionslist'] = self.getActionsList(category=cat_name) # update autogenerated sections resp_dict['section'] = self.getAutoGenereatedSection(cat_name) # and title ts = getToolByName(self.context, 'translation_service') resp_dict['title'] = ts.translate(self.getPageTitle(cat_name), context=self.context) if not errors: resp_dict['status_code'] = 200 resp_dict['status_message'] = "Category changed successfully" else: resp_dict['status_code'] = 500 resp_dict['status_message'] = "There was error while changed category" return resp_dict ######################################## # Methods for processing configlet posts ######################################## def manage_setAutogeneration(self, form, errors): """Process managing autogeneration settings""" # set excludeFromNav property for root objects portal = self.plone_portal_state.portal() generated_tabs = form.get("generated_tabs", '0') nonfolderish_tabs = form.get("nonfolderish_tabs", '0') for item in self.getRootTabs(): obj = getattr(portal, item['id'], None) if obj is not None: checked = form.get(item['id'], None) if checked == '1': obj.update(excludeFromNav=False) else: obj.update(excludeFromNav=True) # set disable_folder_sections property if int(generated_tabs) == 1: self.setSiteProperties(disable_folder_sections=False) else: self.setSiteProperties(disable_folder_sections=True) # set disable_nonfolderish_sections property if int(nonfolderish_tabs) == 1: self.setSiteProperties(disable_nonfolderish_sections=False) else: self.setSiteProperties(disable_nonfolderish_sections=True) # after successfull form processing make redirect with good message IStatusMessage(self.request).addStatusMessage(_(u"Changes saved!"), type="info") self.redirect() return False def manage_addAction(self, form, errs): """Manage method to add a new action to given category, if category doesn't exist, create it """ # extract posted data id, cat_name, data = self.parseAddForm(form) # validate posted data errors = self.validateActionFields(cat_name, data) # if not errors find (or create) category and set action to it if not errors: action = self.addAction(cat_name, data) IStatusMessage(self.request).addStatusMessage( _(u"'${id}' action successfully added.", mapping={'id': action.id}), type="info") self.redirect(search="category=%s" % cat_name) return False else: errs.update(errors) IStatusMessage(self.request).addStatusMessage( _(u"Please correct the indicated errors."), type="error") return True def manage_editAction(self, form, errs): """Manage Method to update action""" # extract posted data id, cat_name, data = self.parseEditForm(form) # get category and action to edit category = self.getActionCategory(cat_name) action = category[id] # validate posted data errors = self.validateActionFields( cat_name, data, allow_dup=(id == data['id'])) if not errors: action = self.updateAction(id, cat_name, data) IStatusMessage(self.request).addStatusMessage( _(u"'${id}' action saved.", mapping={'id': action.id}), type="info") self.redirect(search="category=%s" % cat_name) return False else: errs.update(self.processErrors( errors, sufix='_%s' % id)) # add edit form sufix to error ids IStatusMessage(self.request).addStatusMessage(_( u"Please correct the indicated errors."), type="error") return True def manage_deleteAction(self, form, errs): """Manage Method to delete action""" # extract posted data id, cat_name, data = self.parseEditForm(form) # get category and action to delete category = self.getActionCategory(cat_name) if id in category.objectIds(): self.deleteAction(id, cat_name) IStatusMessage(self.request).addStatusMessage( _(u"'${id}' action deleted.", mapping={'id': id}), type="info") self.redirect(search="category=%s" % cat_name) return False else: IStatusMessage(self.request).addStatusMessage( _(u"No '${id}' action in '${cat_name}' category.", mapping={'id': id, 'cat_name': cat_name}), type="error") return True def manage_moveUpAction(self, form, errs): """Manage Method for moving up given action by one position""" # extract posted data id, cat_name, data = self.parseEditForm(form) # get category and action to move category = self.getActionCategory(cat_name) if id in category.objectIds(): self.moveAction(id, cat_name, steps=1) IStatusMessage(self.request).addStatusMessage( _(u"'${id}' action moved up.", mapping={'id': id}), type="info") self.redirect(search="category=%s" % cat_name) return False else: IStatusMessage(self.request).addStatusMessage( _(u"No '${id}' action in '${cat_name}' category.", mapping={'id': id, 'cat_name': cat_name}), type="error") return True def manage_moveDownAction(self, form, errs): """Manage Method for moving down given action by one position""" # extract posted data id, cat_name, data = self.parseEditForm(form) # get category and action to move category = self.getActionCategory(cat_name) if id in category.objectIds(): self.moveAction(id, cat_name, steps=-1) IStatusMessage(self.request).addStatusMessage( _(u"'${id}' action moved down.", mapping={'id': id}), type="info") self.redirect(search="category=%s" % cat_name) return False else: IStatusMessage(self.request).addStatusMessage( _(u"No '${id}' action in '${cat_name}' category.", mapping={'id': id, 'cat_name': cat_name}), type="error") return True def redirect(self, url="", search="", url_hash=""): """Redirect to @@plonetabs-controlpanel configlet""" if not url: portal_url = self.plone_portal_state.portal_url() url = '%s/%s' % (portal_url, "@@plonetabs-controlpanel") if search: search = '?%s' % search if url_hash: url_hash = '#%s' % url_hash self.request.response.redirect("%s%s%s" % (url, search, url_hash)) ################################### # # Methods - providers for templates # ################################### def _charset(self): pp = self.portal_properties if pp is not None: site_properties = getattr(pp, 'site_properties', None) if site_properties is not None: return site_properties.getProperty('default_charset', 'utf-8') return 'utf-8' def getPageTitle(self, category="portal_tabs"): """See interface""" portal_props = self.portal_properties default_title = _(u"Plone '${cat_name}' Configuration", mapping={'cat_name': category}) if not hasattr(portal_props, PROPERTY_SHEET): return default_title sheet = getattr(portal_props, PROPERTY_SHEET) if not hasattr(sheet, FIELD_NAME): return default_title field = sheet.getProperty(FIELD_NAME) dict = {} for line in field: cat, title = line.split("|", 2) dict[cat] = title title = dict.get(category, None) if title is None: return default_title charset = self._charset() if not isinstance(title, unicode): title = title.decode(charset) return _(title) def hasActions(self, category="portal_tabs"): """See interface""" tool = self.portal_actions return len(tool.listActions(categories=[category, ])) > 0 def getPortalActions(self, category="portal_tabs"): """See interface""" portal_actions = self.portal_actions if category not in portal_actions.objectIds(): return [] actions = [] for item in portal_actions[category].objectValues(): if IAction.providedBy(item): actions.append(item) return actions def isGeneratedTabs(self): """See interface""" site_properties = self.portal_properties.site_properties return not site_properties.getProperty("disable_folder_sections", False) def isNotFoldersGenerated(self): """See interface""" site_properties = self.portal_properties.site_properties prop = site_properties.getProperty("disable_nonfolderish_sections", False) return not prop def getActionsList(self, category="portal_tabs", errors={}, tabs=[]): """See interface""" kw = {'category': category, 'errors': errors} if tabs: kw['tabs'] = tabs return self.actionslist_template(**kw) def getAutoGenereatedSection(self, cat_name, errors={}): """See interface""" return self.autogenerated_template(category=cat_name, errors=errors) def getGeneratedTabs(self): """See interface""" return self.autogenerated_list() def _add_sort_option(self, query): """ add sort option """ navtree_props = getattr(self.portal_properties, 'navtree_properties') sortAttribute = navtree_props.getProperty('sortAttribute', None) if sortAttribute is not None: query['sort_on'] = sortAttribute sortOrder = navtree_props.getProperty('sortOrder', None) if sortOrder is not None: query['sort_order'] = sortOrder def getRootTabs(self): """See interface""" context = aq_inner(self.context) portal_catalog = getToolByName(context, 'portal_catalog') portal_properties = self.portal_properties navtree_properties = getattr(portal_properties, 'navtree_properties') # Build result dict result = [] # check whether tabs autogeneration is turned on if not self.isGeneratedTabs(): return result query = {} rootPath = getNavigationRoot(context) query['path'] = {'query': rootPath, 'depth': 1} query['portal_type'] = utils.typesToList(context) self._add_sort_option(query) if navtree_properties.getProperty('enable_wf_state_filtering', False): query['review_state'] = navtree_properties.getProperty( 'wf_states_to_show', []) query['is_default_page'] = False if not self.isNotFoldersGenerated(): query['is_folderish'] = True # Get ids not to list and make a dict to make the search fast idsNotToList = navtree_properties.getProperty('idsNotToList', ()) excludedIds = {} for id in idsNotToList: excludedIds[id] = 1 rawresult = portal_catalog.searchResults(**query) # now add the content to results for item in rawresult: if item.getId not in excludedIds: result.append(self.getItem(item)) return result def getItem(self, item): """get item""" context = aq_inner(self.context) id_, item_url = get_view_url(item) data = { 'name': utils.pretty_title_or_id(context, item), 'id': id_, 'url': item_url, 'description': item.Description, 'exclude_from_nav': item.exclude_from_nav, } return data def getCategories(self): """See interface""" portal_actions = self.portal_actions return portal_actions.objectIds() # # Methods to make this class looks like global sections viewlet # def test(self, condition, ifTrue, ifFalse): """See interface""" if condition: return ifTrue else: return ifFalse # methods for rendering global-sections viewlet via kss, # due to bug in macroContent when global-section list is empty, # ul have condition def portal_tabs(self): """See global-sections viewlet""" actions = getMultiAdapter((self.context, self.request), name=u'plone_context_state').actions() actions_tabs = [] try: # Plone 4 and higher import plone.app.upgrade plone.app.upgrade # pyflakes except ImportError: actions_tabs = actions if not actions_tabs and 'portal_tabs' in actions: actions_tabs = actions['portal_tabs'] portal_tabs_view = getMultiAdapter((self.context, self.request), name="portal_tabs_view") return portal_tabs_view.topLevelTabs(actions=actions_tabs) def selected_portal_tab(self): """See global-sections viewlet""" # BBB: compatibility with older plone versions. # ``selectedTabs`` Python script was merged into the # GlobalSectionsViewlet. section_viewlet = setupViewletByName(self, self.context, self.request, 'plone.global_sections') if hasattr(section_viewlet, 'selectedTabs'): # Plone >= 4.3 selected_tabs = section_viewlet.selectedTabs( default_tab='index_html', portal_tabs=self.portal_tabs()) else: # Plone < 4.3 selectedTabs = self.context.restrictedTraverse('selectedTabs') selected_tabs = selectedTabs('index_html', self.context, self.portal_tabs()) return selected_tabs['portal'] # # Utility Methods # def fixExpression(self, expr): """Fix expression appropriately for tal format""" if expr.find('/') == 0: return 'string:${portal_url}%s' % expr elif re.compile('^(ht|f)tps?\:', re.I).search(expr): return 'string:%s' % expr # elif re.compile('^(python:|string:|not:|exists:|nocall:|path:)', # re.I).search(expr): # return expr elif expr.find(':') != -1: return expr else: return 'string:${object_url}/%s' % expr def copyAction(self, action): """Copy action to dictionary""" action_info = {'description': action.description} for attr in ACTION_ATTRS: action_info[attr] = getattr(action, attr) return action_info def _validate_expression(self, name, data, errors): """ validate expression """ if data[name]: try: Expression(data[name]) except Exception, e: mapping = {'expr': data[name]} idx = data[name].find(':') if idx != -1: mapping['expr_type'] = data[name][:idx] errors[name] = self._formatError(e, **mapping) def validateActionFields(self, cat_name, data, allow_dup=False): """Check action fields on validity""" errors = {} if allow_dup: # create dummy category to avoid id # duplication during action update category = ActionCategory(cat_name) else: # get or create (if necessary) actions category category = self.getOrCreateCategory(cat_name) # validate action id chooser = INameChooser(category) try: chooser.checkName(data['id'], self.context) except Exception, e: errors['id'] = self._formatError(e, **{'id': data['id']}) # validate action name if not data['title'].strip(): errors['title'] = _(u"Empty or invalid title specified") # validate condition expression self._validate_expression('available_expr', data, errors) # validate action expression self._validate_expression('url_expr', data, errors) return errors def _formatError(self, message, **kw): """Make error message a little bit prettier to ease translation""" charset = self._charset() message = str(message) message = message.replace('"', "'") mapping = {} for key, value in kw.items(): message = message.replace("'%s'" % value, "'${%s}'" % key) # trying to work around zope.i18n issue mapping[key] = unicode(value, charset) message = message.strip() return _(unicode(message, charset), mapping=mapping) def processErrors(self, errors, prefix='', sufix=''): """Add prefixes, sufixes to error ids This is necessary during edit form validation, because every edit form on the page has it's own sufix (id) """ if not (prefix or sufix): return errors result = {} for key, value in errors.items(): result['%s%s%s' % (prefix, key, sufix)] = value return result def parseEditForm(self, form): """Extract all needed fields from edit form""" # get original id and category info = {} id = form['orig_id'] category = form['category'] # preprocess 'visible' field (checkbox needs special checking) if 'visible_%s' % id in form: form['visible_%s' % id] = True else: form['visible_%s' % id] = False # collect all action fields for attr in ACTION_ATTRS: info[attr] = form['%s_%s' % (attr, id)] return (id, category, info) def parseAddForm(self, form): """Extract all needed fields from add form""" info = {} id = form['id'] category = form['category'] # preprocess 'visible' field (checkbox needs special checking) if 'visible' in form and form['visible']: form['visible'] = True else: form['visible'] = False # fix expression fields form['url_expr'] = self.fixExpression(form['url_expr']) # collect all action fields for attr in ACTION_ATTRS: info[attr] = form[attr] return (id, category, info) def getActionCategory(self, name): portal_actions = self.portal_actions return portal_actions[name] def getOrCreateCategory(self, name): """Get or create (if necessary) category""" portal_actions = self.portal_actions if name not in map(lambda x: x.id, filter(lambda x: IActionCategory.providedBy(x), portal_actions.objectValues())): portal_actions._setObject(name, ActionCategory(name)) return self.getActionCategory(name) def setSiteProperties(self, **kw): """Change site_properties""" site_properties = self.portal_properties.site_properties site_properties.manage_changeProperties(**kw) return True def renderViewlet(self, manager, name): if isinstance(manager, basestring): manager = getMultiAdapter((self.context, self.request, self,), IViewletManager, name=manager) renderer = getMultiAdapter((self.context, self.request, self, manager), IViewlet, name=name) renderer = renderer.__of__(self.context) renderer.update() return renderer.render() # # Basic API to work with portal actions tool in a more pleasent way # def addAction(self, cat_name, data): """Create and add new action to category with given name""" id = data.pop('id') category = self.getOrCreateCategory(cat_name) action = Action(id, **data) category._setObject(id, action) return action def updateAction(self, id, cat_name, data): """Update action with given id and category""" new_id = data.pop('id') category = self.getActionCategory(cat_name) # rename action if necessary if id != new_id: category.manage_renameObject(id, new_id) # get action action = category[new_id] # update action properties for attr in data.keys(): if attr in data: action._setPropValue(attr, data[attr]) return action def deleteAction(self, id, cat_name): """Delete action with given id from given category""" category = self.getActionCategory(cat_name) category.manage_delObjects(ids=[id, ]) return True def moveAction(self, id, cat_name, steps=0): """Move action by a given steps""" if steps != 0: category = self.getActionCategory(cat_name) if steps > 0: category.moveObjectsUp([id, ], steps) else: category.moveObjectsDown([id, ], abs(steps)) return True return False class PloneTabsMode(BrowserView): def __call__(self): mode = self.request.get(cookie_name, False) if mode in ('plain', 'rich'): return mode mode = self.request.cookies.get(cookie_name, False) if mode in ('plain', 'rich'): return mode return 'rich'