source: products/qPloneTabs/branches/quintagroup.plonetabs/trunk/quintagroup/plonetabs/browser/plonetabs.py @ 153

Last change on this file since 153 was 153, checked in by chervol, 18 years ago

review_state management added

  • Property svn:eol-style set to native
File size: 31.9 KB
Line 
1import copy, sys
2from Acquisition import aq_inner
3from OFS.CopySupport import CopyError
4
5from zope.interface import implements
6from zope.component import getUtility, getMultiAdapter
7from zope.i18n import translate
8from zope.schema.interfaces import IVocabularyFactory
9from zope.exceptions import UserError
10from zope.app.container.interfaces import INameChooser
11
12from Products.CMFCore.utils import getToolByName
13from Products.CMFCore.interfaces import IAction, IActionCategory
14from Products.CMFCore.ActionInformation import Action, ActionCategory
15from Products.CMFCore.Expression import Expression
16from Products.CMFPlone import PloneMessageFactory as _
17from Products.CMFPlone import utils
18from Products.CMFPlone.browser.navigation import get_view_url
19from Products.Five.browser import BrowserView
20from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
21from Products.statusmessages.interfaces import IStatusMessage
22
23from plone.app.layout.navigation.root import getNavigationRoot
24from plone.app.kss.plonekssview import PloneKSSView
25from plone.app.workflow.remap import remap_workflow
26from plone.memoize.instance import memoize
27from kss.core import kssaction, KSSExplicitError
28
29from quintagroup.plonetabs.config import *
30from interfaces import IPloneTabsControlPanel
31
32ACTION_ATTRS = ["id", "title", "url_expr", "available_expr", "visible"]
33UI_ATTRS = {"id": "id",
34            "title": "name",
35            "url_expr": "action",
36            "available_expr": "condition",
37            "visible": "visible"}
38
39class PloneTabsControlPanel(PloneKSSView):
40   
41    implements(IPloneTabsControlPanel)
42   
43    template = ViewPageTemplateFile("templates/plonetabs.pt")
44    actionslist_template = ViewPageTemplateFile("templates/actionslist.pt")
45    autogenerated_template = ViewPageTemplateFile("templates/autogenerated.pt")
46   
47    # custom templates used to update page sections
48    sections_template = ViewPageTemplateFile("templates/sections.pt")
49   
50    # configuration variables
51    prefix = "tabslist_"
52    sufix = ""
53   
54    def __call__(self):
55        """ Perform the update and redirect if necessary, or render the page """
56        postback = True
57        errors = {}
58        context = aq_inner(self.context)
59       
60        form = self.request.form
61        action = form.get("action", "")
62        submitted = form.get('form.submitted', False)
63       
64        # action handler def handler(self, form)
65        if submitted:
66            if form.has_key('add.add'):
67                postback = self.manage_addAction(form, errors)
68            elif form.has_key("edit.save"):
69                postback = self.manage_editAction(form, errors)
70            elif form.has_key("edit.delete"):
71                postback = self.manage_deleteAction(form, errors)
72            elif form.has_key("edit.moveup"):
73                postback = self.manage_moveUpAction(form, errors)
74            elif form.has_key("edit.movedown"):
75                postback = self.manage_moveDownAction(form, errors)
76            elif form.has_key("autogenerated.save"):
77                postback = self.manage_setAutogeneration(form, errors)
78            else:
79                postback = True
80       
81        if postback:
82            return self.template(errors=errors)
83   
84    ########################################
85    # Methods for processing configlet posts
86    ########################################
87   
88    def manage_setAutogeneration(self, form, errors):
89        """ Process managing autogeneration settings """
90       
91        # set excludeFromNav property for root objects
92        portal = getMultiAdapter((aq_inner(self.context), self.request), name='plone_portal_state').portal()
93        generated_tabs = form.get("generated_tabs", '0')
94        nonfolderish_tabs = form.get("nonfolderish_tabs", '0')
95       
96        for item in self.getRootTabs():
97            obj = getattr(portal, item['id'], None)
98            if obj is not None:
99                checked = form.get(item['id'], None)
100                if checked == '1':
101                    obj.update(excludeFromNav=False)
102                else:
103                    obj.update(excludeFromNav=True)
104
105        # set disable_folder_sections property
106        if int(generated_tabs) == 1:
107            self.setSiteProperties(disable_folder_sections=False)
108        else:
109            self.setSiteProperties(disable_folder_sections=True)
110       
111        # set disable_nonfolderish_sections property
112        if int(nonfolderish_tabs) == 1:
113            self.setSiteProperties(disable_nonfolderish_sections=False)
114        else:
115            self.setSiteProperties(disable_nonfolderish_sections=True)
116       
117        # after successfull form processing make redirect with good message
118        IStatusMessage(self.request).addStatusMessage(_(u"Changes saved!"), type="info")
119        self.redirect()
120        return False
121   
122    def manage_addAction(self, form, errs):
123        """ Manage method to add a new action to given category,
124            if category doesn't exist, create it """
125        # extract posted data
126        id, cat_name, data = self.parseAddForm(form)
127       
128        # validate posted data
129        errors = self.validateActionFields(cat_name, data)
130       
131        # if not errors find (or create) category and set action to it
132        if not errors:
133            action = self.addAction(cat_name, data)
134            IStatusMessage(self.request).addStatusMessage(_(u"'%s' action successfully added." % action.id), type="info")
135            self.redirect(search="category=%s" % cat_name)
136            return False
137        else:
138            errs.update(errors)
139            IStatusMessage(self.request).addStatusMessage(_(u"Please correct the indicated errors."), type="error")
140            return True
141   
142    def manage_editAction(self, form, errs):
143        """ Manage Method to update action """
144        # extract posted data
145        id, cat_name, data = self.parseEditForm(form)
146       
147        # get category and action to edit
148        category = self.getActionCategory(cat_name)
149        action = category[id]
150       
151        # validate posted data
152        errors = self.validateActionFields(cat_name, data, allow_dup=True)
153       
154        if not errors:
155            action = self.updateAction(id, cat_name, data)
156            IStatusMessage(self.request).addStatusMessage(_(u"'%s' action saved." % action.id), type="info")
157            self.redirect(search="category=%s" % cat_name)
158            return False
159        else:
160            errs.update(self.processErrors(errors, sufix='_%s' % id)) # add edit form sufix to error ids
161            IStatusMessage(self.request).addStatusMessage(_(u"Please correct the indicated errors."), type="error")
162            return True
163   
164    def manage_deleteAction(self, form, errs):
165        """ Manage Method to delete action """
166        # extract posted data
167        id, cat_name, data = self.parseEditForm(form)
168       
169        # get category and action to delete
170        category = self.getActionCategory(cat_name)
171        if id in category.objectIds():
172            self.deleteAction(id, cat_name)
173            IStatusMessage(self.request).addStatusMessage(_(u"'%s' action deleted." % id), type="info")
174            self.redirect(search="category=%s" % cat_name)
175            return False
176        else:
177            IStatusMessage(self.request).addStatusMessage(_(u"No '%s' action in '%s' category." % (id, cat_name)), type="error")
178            return True
179   
180    def manage_moveUpAction(self, form, errs):
181        """ Manage Method for moving up given action by one position """
182        # extract posted data
183        id, cat_name, data = self.parseEditForm(form)
184       
185        # get category and action to move
186        category = self.getActionCategory(cat_name)
187        if id in category.objectIds():
188            self.moveAction(id, cat_name, steps=1)
189            IStatusMessage(self.request).addStatusMessage(_(u"'%s' action moved up." % id), type="info")
190            self.redirect(search="category=%s" % cat_name)
191            return False
192        else:
193            IStatusMessage(self.request).addStatusMessage(_(u"No '%s' action in '%s' category." % (id, cat_name)), type="error")
194            return True
195   
196    def manage_moveDownAction(self, form, errs):
197        """ Manage Method for moving down given action by one position """
198        # extract posted data
199        id, cat_name, data = self.parseEditForm(form)
200       
201        # get category and action to move
202        category = self.getActionCategory(cat_name)
203        if id in category.objectIds():
204            self.moveAction(id, cat_name, steps=-1)
205            IStatusMessage(self.request).addStatusMessage(_(u"'%s' action moved down." % id), type="info")
206            self.redirect(search="category=%s" % cat_name)
207            return False
208        else:
209            IStatusMessage(self.request).addStatusMessage(_(u"No '%s' action in '%s' category." % (id, cat_name)), type="error")
210            return True
211   
212    def redirect(self, url="", search="", url_hash=""):
213        """ Redirect to @@plonetabs-controlpanel configlet """
214        portal_url =  getMultiAdapter((self.context, self.request), name=u"plone_portal_state").portal_url()
215        url = (url == "") and "%s/%s" % (portal_url, "@@plonetabs-controlpanel") or url
216        search = (search != "") and "?%s" % search or search
217        url_hash = (url_hash != "") and "#%s" % url_hash or url_hash
218        self.request.response.redirect("%s%s%s" % (url, search, url_hash))
219   
220    ###################################
221    #
222    #  Methods - providers for templates
223    #
224    ###################################
225   
226    def getPageTitle(self, category="portal_tabs"):
227        """ See interface """
228        portal_props = getToolByName(self.context, "portal_properties")
229        default_title = "Plone '%s' Configuration" % category
230       
231        if not hasattr(portal_props, PROPERTY_SHEET):
232            return default_title
233       
234        sheet = getattr(portal_props, PROPERTY_SHEET)
235        if not hasattr(sheet, FIELD_NAME):
236            return default_title
237       
238        field = sheet.getProperty(FIELD_NAME)
239        dict = {}
240        for line in field:
241            cat, title = line.split("|", 2)
242            dict[cat] = title
243       
244        return dict.get(category, None) or default_title
245   
246    def hasActions(self, category="portal_tabs"):
247        """ See interface """
248        return len(getToolByName(self.context, "portal_actions").listActions(categories=[category,])) > 0
249   
250    def getPortalActions(self, category="portal_tabs"):
251        """ See interface """
252        portal_actions = getToolByName(self.context, "portal_actions")
253       
254        if category not in portal_actions.objectIds():
255            return []
256       
257        actions = []
258        for item in portal_actions[category].objectValues():
259            if IAction.providedBy(item):
260                actions.append(item)
261       
262        return actions
263   
264    def isGeneratedTabs(self):
265        """ See interface """
266        site_properties = getToolByName(self.context, "portal_properties").site_properties
267        return not site_properties.getProperty("disable_folder_sections", False)
268   
269    def isNotFoldersGenerated(self):
270        """ See interface """
271        site_properties = getToolByName(self.context, "portal_properties").site_properties
272        return not site_properties.getProperty("disable_nonfolderish_sections", False)
273   
274    def getActionsList(self, category="portal_tabs", errors={}, tabs=[]):
275        """ See interface """
276        kw = {'category': category, 'errors': errors}
277        if tabs:
278            kw['tabs'] = tabs
279        return self.actionslist_template(**kw)
280   
281    def getGeneratedTabs(self):
282        """ See interface """
283        return self.autogenerated_template()
284   
285    def getRootTabs(self):
286        """ See interface """
287        context = aq_inner(self.context)
288       
289        portal_catalog = getToolByName(context, 'portal_catalog')
290        portal_properties = getToolByName(context, 'portal_properties')
291        navtree_properties = getattr(portal_properties, 'navtree_properties')
292       
293        # Build result dict
294        result = []
295       
296        # check whether tabs autogeneration is turned on
297        if not self.isGeneratedTabs():
298            return result
299       
300        query = {}
301        rootPath = getNavigationRoot(context)
302        query['path'] = {'query' : rootPath, 'depth' : 1}
303        query['portal_type'] = utils.typesToList(context)
304       
305        sortAttribute = navtree_properties.getProperty('sortAttribute', None)
306        if sortAttribute is not None:
307            query['sort_on'] = sortAttribute
308           
309            sortOrder = navtree_properties.getProperty('sortOrder', None)
310            if sortOrder is not None:
311                query['sort_order'] = sortOrder
312       
313        if navtree_properties.getProperty('enable_wf_state_filtering', False):
314            query['review_state'] = navtree_properties.getProperty('wf_states_to_show', [])
315       
316        query['is_default_page'] = False
317
318        if not self.isNotFoldersGenerated():
319            query['is_folderish'] = True
320
321        # Get ids not to list and make a dict to make the search fast
322        idsNotToList = navtree_properties.getProperty('idsNotToList', ())
323        excludedIds = {}
324        for id in idsNotToList:
325            excludedIds[id]=1
326
327        rawresult = portal_catalog.searchResults(**query)
328
329        # now add the content to results
330        for item in rawresult:
331            if not excludedIds.has_key(item.getId):
332                id, item_url = get_view_url(item)
333                data = {'name'       : utils.pretty_title_or_id(context, item),
334                        'id'         : id,
335                        'url'        : item_url,
336                        'description': item.Description,
337                        'exclude_from_nav' : item.exclude_from_nav}
338                result.append(data)
339       
340        return result
341   
342    def getCategories(self):
343        """ See interface """
344        portal_actions = getToolByName(self.context, "portal_actions")
345        return portal_actions.objectIds()
346   
347    #
348    # Methods to make this class looks like global sections viewlet
349    #
350   
351    def test(self, condition, ifTrue, ifFalse):
352        """ See interface """
353        if condition:
354            return ifTrue
355        else:
356            return ifFalse
357   
358    # methods for rendering global-sections viewlet via kss,
359    # due to bug in macroContent when global-section list is empty,
360    # ul have condition
361    def portal_tabs(self):
362        """ See global-sections viewlet """
363        actions = context_state = getMultiAdapter((self.context, self.request), name=u"plone_context_state").actions()
364        portal_tabs_view = getMultiAdapter((self.context, self.request), name="portal_tabs_view")
365       
366        return portal_tabs_view.topLevelTabs(actions=actions)
367   
368    def selected_portal_tab(self):
369        """ See global-sections viewlet """
370        selectedTabs = self.context.restrictedTraverse('selectedTabs')
371        selected_tabs = selectedTabs('index_html', self.context, self.portal_tabs())
372       
373        return selected_tabs['portal']
374   
375    ##########################
376    #
377    # KSS Server Actions
378    #
379    ##########################
380   
381    @kssaction
382    def kss_toggleGeneratedTabs(self, field, checked='0'):
383        """ Toggle autogenaration setting on configlet """
384       
385        changeProperties = getToolByName(self.context, "portal_properties").site_properties.manage_changeProperties
386        if checked == '1':
387            changeProperties(**{field : False})
388        else:
389            changeProperties(**{field : True})
390       
391        ksscore = self.getCommandSet("core")
392        replace_id = "roottabs"
393        content = self.getGeneratedTabs()
394       
395        ksscore.replaceInnerHTML(ksscore.getHtmlIdSelector(replace_id), content, withKssSetup="True")
396       
397        # update global-sections viewlet
398        self.updatePortalTabs()
399   
400    @kssaction
401    def kss_toggleRootsVisibility(self, id, checked='0'):
402        """ Toggle visibility for portal root objects (exclude_from_nav) """
403        portal = getMultiAdapter((aq_inner(self.context), self.request), name='plone_portal_state').portal()
404       
405        # remove prefix, added for making ids on configlet unique ("roottabs_")
406        obj_id = id[len("roottabs_"):]
407       
408        if obj_id not in portal.objectIds():
409            raise KSSExplicitError, "Object with %s id doesn't exist in portal root" % obj_id
410       
411        if checked == '1':
412            checked = True
413        else:
414            checked = False
415       
416        portal[obj_id].update(excludeFromNav=not checked)
417       
418        ksscore = self.getCommandSet("core")
419        if checked:
420            ksscore.removeClass(ksscore.getHtmlIdSelector(id), value="invisible")
421        else:
422            ksscore.addClass(ksscore.getHtmlIdSelector(id), value="invisible")
423       
424        # update global-sections viewlet
425        self.updatePortalTabs()
426   
427    @kssaction
428    def kss_toggleActionsVisibility(self, id, checked='0', cat_name=None):
429        """ Toggle visibility for portal actions """
430        # validate input
431        act_id, category, action = self.kss_validateAction(id, cat_name)
432        self.updateAction(act_id, cat_name, {'id': act_id, 'visible': (checked == '1') or False})
433       
434        # update client
435        ksscore = self.getCommandSet("core")
436        if checked == '1':
437            ksscore.removeClass(ksscore.getHtmlIdSelector(id), value="invisible")
438        else:
439            ksscore.addClass(ksscore.getHtmlIdSelector(id), value="invisible")
440        self.updatePage(cat_name)
441   
442    @kssaction
443    def kss_deleteAction(self, id, cat_name):
444        """ Delete portal action with given id & category """
445        # validate input
446        act_id, category, action = self.kss_validateAction(id, cat_name)
447        self.deleteAction(act_id, cat_name)
448       
449        # update client
450        ksscore = self.getCommandSet("core")
451        # XXX TODO: fade effect during removing, to do this we need kukit js action/command plugin
452        ksscore.deleteNode(ksscore.getHtmlIdSelector(id))
453        self.kss_checkReorderControls(cat_name)
454        self.updatePage(cat_name)
455   
456    @kssaction
457    def kss_addAction(self, id, category, title, url_expr, available_expr, visible=False):
458        """ KSS method to add new portal action """
459        # extract posted data
460        id, cat_name, data = self.parseAddForm({'id':id,
461                                                'category': category,
462                                                'title': title,
463                                                'url_expr': url_expr,
464                                                'available_expr': available_expr,
465                                                'visible': visible})
466       
467        # validate posted data
468        errors = self.validateActionFields(cat_name, data)
469       
470        # if not errors find (or create) category and set action to it
471        ksscore = self.getCommandSet('core')
472        kssplone = self.getCommandSet('plone')
473        if not errors:
474            action = self.addAction(cat_name, data)
475           
476            # update client
477            # add one more action to actions list
478            content = self.getActionsList(category=cat_name, tabs=[action,])
479            ksscore.insertHTMLAsLastChild(ksscore.getHtmlIdSelector('tabslist'), content)
480           
481            # update reorder controls
482            self.kss_checkReorderControls(cat_name)
483           
484            # hide adding form
485            ksscore.removeClass(ksscore.getHtmlIdSelector('addaction'), 'adding')
486            self.kss_toggleCollapsible(ksscore.getCssSelector('#addaction .collapseAdvanced .headerAdvanced'), collapse='true')
487           
488            # reset adding form
489            self.kss_resetForm(ksscore.getHtmlIdSelector('addaction'))
490           
491            # issue portal message
492            kssplone.issuePortalMessage(_(u"'%s' action successfully added." % action.id), msgtype="info")
493           
494            # update page
495            self.updatePage(cat_name)
496        else:
497            # expand advanced section if there are errors in id or condition
498            if errors.has_key('id') or errors.has_key('available_expr'):
499                self.kss_toggleCollapsible(ksscore.getCssSelector('#addaction .collapseAdvanced .headerAdvanced'), collapse='false')
500           
501            # send error message
502            kssplone.issuePortalMessage(_(u"Please correct the indicated errors."), msgtype="error")
503       
504        # update errors on client fomr
505        self.kss_issueErrors(errors)
506   
507    @kssaction
508    def editAction(self, id, category):
509        """ Show edit form for given action """
510        cat_container, act_id = self.validateAction(id, category)
511       
512        # collect data
513        action_info = self.copyAction(cat_container[act_id])
514        action_info["editing"] = True
515       
516        ksscore = self.getCommandSet("core")
517        content = self.actionslist_template(tabs=[action_info,])
518        replace_id = id
519       
520        ksscore.replaceHTML(ksscore.getHtmlIdSelector(replace_id), content)
521       
522        # focus name field
523        ksscore.focus(ksscore.getCssSelector("#%s input[name=name_%s]" % (id, act_id)))
524   
525    @kssaction
526    def editCancel(self, id, category):
527        """ Hide edit form for given action """
528        cat_container, act_id = self.validateAction(id, category)
529       
530        ksscore = self.getCommandSet("core")
531        content = self.actionslist_template(tabs=[cat_container[act_id],])
532        replace_id = id
533       
534        ksscore.replaceHTML(ksscore.getHtmlIdSelector(replace_id), content)
535   
536    #
537    # Utility Methods
538    #
539   
540    def copyAction(self, action):
541        """ Copyt action to dictionary """
542        action_info = {}
543        for attr in ACTION_ATTRS:
544            action_info[attr] = getattr(action, attr)
545        return action_info
546   
547    def validateActionFields(self, cat_name, data, allow_dup=False):
548        """ Check action fields on validity """
549        errors = {}
550       
551        if allow_dup:
552            category = ActionCategory(cat_name)           # create dummy category to avoid id duplication during action update
553        else:
554            category = self.getOrCreateCategory(cat_name) # get or create (if necessary) actions category
555       
556        # validate action id
557        chooser = INameChooser(category)
558        try:
559            chooser.checkName(data['id'], self.context)
560        except Exception, e:
561            errors['id'] = "%s" % str(e)
562       
563        # validate action name
564        if not data['title'].strip():
565            errors['title'] = 'Empty or invalid title specified'
566       
567        # validate condition expression
568        if data['available_expr']:
569            try:
570                Expression(data['available_expr'])
571            except Exception, e:
572                errors["available_expr"] = "%s" % str(e)
573       
574        # validate action expression
575        if data['url_expr']:
576            try:
577                Expression(data['url_expr'])
578            except Exception, e:
579                errors["url_expr"] = "%s" % str(e)
580       
581        return errors
582   
583    def processErrors(self, errors, prefix='', sufix=''):
584        """ Add prefixes, sufixes to error ids
585            This is necessary during edit form validation,
586            because every edit form on the page has it's own sufix (id) """
587        if not (prefix or sufix):
588            return errors
589       
590        result = {}
591        for key, value in errors.items():
592            result['%s%s%s' % (prefix, key, sufix)] = value
593       
594        return result
595   
596    def parseEditForm(self, form):
597        """ Extract all needed fields from edit form """
598        # get original id and category
599        info = {}
600        id = form['orig_id']
601        category = form['category']
602       
603        # preprocess 'visible' field (checkbox needs special checking)
604        if form.has_key('visible_%s' % id):
605            form['visible_%s' % id] = True
606        else:
607            form['visible_%s' % id] = False
608       
609        # collect all action fields
610        for attr in ACTION_ATTRS:
611            info[attr] = form['%s_%s' % (attr, id)]
612       
613        return (id, category, info)
614   
615    def parseAddForm(self, form):
616        """ Extract all needed fields from add form """
617        info = {}
618        id = form['id']
619        category = form['category']
620       
621        # preprocess 'visible' field (checkbox needs special checking)
622        if form.has_key('visible') and form['visible']:
623            form['visible'] = True
624        else:
625            form['visible'] = False
626       
627        # collect all action fields
628        for attr in ACTION_ATTRS:
629            info[attr] = form[attr]
630       
631        return (id, category, info)
632   
633    def getActionCategory(self, name):
634        portal_actions = getToolByName(self.context, 'portal_actions')
635        return portal_actions[name]
636   
637    def getOrCreateCategory(self, name):
638        """ Get or create (if necessary) category """
639        portal_actions = getToolByName(self.context, 'portal_actions')
640        if name not in map(lambda x: x.id, filter(lambda x: IActionCategory.providedBy(x), portal_actions.objectValues())):
641            portal_actions._setObject(name, ActionCategory(name))
642        return self.getActionCategory(name)
643   
644    def setSiteProperties(self, **kw):
645        """ Change site_properties """
646        site_properties = getToolByName(self.context, "portal_properties").site_properties
647        site_properties.manage_changeProperties(**kw)
648        return True
649   
650    #
651    # Utility methods for the kss actions management
652    #
653   
654    def kss_checkReorderControls(self, cat_name):
655        """ Add "noitems" class to Reorder controls to hide them if category is empty """
656        ksscore = self.getCommandSet('core')
657        selector = ksscore.getHtmlIdSelector("reorder")
658        category = self.getActionCategory(cat_name)
659        if filter(lambda x: IAction.providedBy(x), category.objectValues()):
660            ksscore.removeClass(selector, value="noitems")
661        else:
662            ksscore.addClass(selector, value="noitems")
663   
664    def kss_validateAction(self, id, cat_name):
665        """ Check whether action with given id exists in cat_name category """
666        try:
667            category = self.getActionCategory(cat_name)
668        except Exception:
669            raise KSSExplicitError, "'%s' action category does not exist" % cat_name
670       
671        # extract action id from given list item id on client
672        action_id = self.sufix and id[len(self.prefix):-len(self.sufix)] or id[len(self.prefix):]
673        try:
674            action = category[action_id]
675        except Exception:
676            raise KSSExplicitError, "No '%s' action in '%s' category." % (action_id, cat_name)
677       
678        return (action_id, category, action)
679   
680    def kss_issueErrors(self, errors, fields=ACTION_ATTRS):
681        """ Display error messages on the client """
682        ksscore = self.getCommandSet('core')
683        for field in fields:
684            self.kss_issueFieldError(ksscore, field, errors.get(field, False))
685   
686    def kss_issueFieldError(self, ksscore, name, error):
687        """ Issue this error message for the field """
688        field_selector = ksscore.getCssSelector('#addaction .field-%s' % UI_ATTRS.get(name, name))
689        field_error_selector = ksscore.getCssSelector('#addaction .field-%s .error-container' % UI_ATTRS.get(name, name))
690        if error:
691            ksscore.replaceInnerHTML(field_error_selector, _(error))
692            ksscore.addClass(field_selector, 'error')
693        else:
694            ksscore.clearChildNodes(field_error_selector)
695            ksscore.removeClass(field_selector, 'error')
696   
697    def kss_toggleCollapsible(self, selector, collapsed=None, expanded=None, collapse=None):
698        """ KSS Server command to add plonetabs-toggleCollapsible client action to response """
699        command = self.commands.addCommand('plonetabs-toggleCollapsible', selector)
700        if collapsed is not None:
701            data = command.addParam('collapsed', collapsed)
702        if expanded is not None:
703            data = command.addParam('expanded', expanded)
704        if collapse is not None:
705            data = command.addParam('collapse', collapse)
706   
707    def kss_resetForm(self, selector):
708        """ KSS Server command to reset form on client """
709        command = self.commands.addCommand('plonetabs-resetForm', selector)
710   
711    def validateAction(self, id, category, prefix="tabslist_"):
712        """ If action with given id and category doesn't exist - raise kss exception """
713        portal_actions = getToolByName(self.context, "portal_actions")
714       
715        # remove prefix, added for making ids on configlet unique self.prefix
716        act_id = id[len(self.prefix):]
717       
718        if category not in portal_actions.objectIds():
719            raise KSSExplicitError, "Unexistent root portal actions category %s" % category
720       
721        cat_container = portal_actions[category]
722        if act_id not in map(lambda x: x.id, filter(lambda x: IAction.providedBy(x), cat_container.objectValues())):
723            raise KSSExplicitError, "%s action does not exist in %s category" % (act_id, category)
724       
725        return (cat_container, act_id)
726   
727    #
728    # Basic API to work with portal actions tool in a more pleasent way
729    #
730   
731    def addAction(self, cat_name, data):
732        """ Create and add new action to category with given name """
733        id = data.pop('id')
734        category = self.getOrCreateCategory(cat_name)
735        action = Action(id, **data)
736        category._setObject(id, action)
737        return action
738   
739    def updateAction(self, id, cat_name, data):
740        """ Update action with given id and category """
741        new_id = data.pop('id')
742        category = self.getActionCategory(cat_name)
743       
744        # rename action if necessary
745        if id != new_id:
746            category.manage_renameObject(id, new_id)
747       
748        # get action
749        action = category[new_id]
750       
751        # update action properties
752        for attr in data.keys():
753            if data.has_key(attr):
754                action._setPropValue(attr, data[attr])
755       
756        return action
757   
758    def deleteAction(self, id, cat_name):
759        """ Delete action with given id from given category """
760        category = self.getActionCategory(cat_name)
761        category.manage_delObjects(ids=[id,])
762        return True
763   
764    def moveAction(self, id, cat_name, steps=0):
765        """ Move action by a given steps """
766        if steps != 0:
767            category = self.getActionCategory(cat_name)
768            if steps > 0:
769                category.moveObjectsUp([id,], steps)
770            else:
771                category.moveObjectsDown([id,], abs(steps))
772            return True
773        return False
774   
775    #
776    # KSS Methods that are used to update different parts of the page
777    # accordingly to category
778    #
779   
780    def updatePage(self, category):
781        """ Seek for according method in class and calls it if found
782            Example of making up method's name:
783                portal_tabs => updatePortalTabs """
784        method_name = 'update%sPageSection' % ''.join(map(lambda x: x.capitalize(), category.split('_')))
785        if hasattr(self, method_name):
786            getattr(self, method_name)()
787   
788    def updatePortalTabsPageSection(self):
789        """ Method for updating global-sections on client """
790        ksscore = self.getCommandSet("core")
791        ksscore.replaceHTML(
792            ksscore.getHtmlIdSelector("portal-globalnav"),
793            self.sections_template(),
794            withKssSetup="False")
795   
796    def updateSiteActionsPageSection(self):
797        """ Method for updating site action on client """
798        ksszope = self.getCommandSet("zope")
799        ksszope.refreshViewlet(
800            self.getCommandSet("core").getHtmlIdSelector("portal-siteactions"),
801            "plone.portalheader",
802            "plone.site_actions")
803   
804    def updateUserPageSection(self):
805        """ Method for updating site action on client """
806        ksszope = self.getCommandSet("zope")
807        ksszope.refreshViewlet(
808            self.getCommandSet("core").getHtmlIdSelector("portal-personaltools-wrapper"),
809            "plone.portaltop",
810            "plone.personal_bar")
811
812
Note: See TracBrowser for help on using the repository browser.