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

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

review state chack updated

  • Property svn:eol-style set to native
File size: 34.0 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       
454        # check reorder controls, whether we should hide them
455        self.kss_checkReorderControls(cat_name)
456       
457        # issue portal message
458        self.getCommandSet('plone').issuePortalMessage(_(u"'%s' action successfully deleted." % act_id), msgtype="info")
459       
460        # update different sections of page depending on actions category
461        self.updatePage(cat_name)
462   
463    @kssaction
464    def kss_addAction(self):
465        """ KSS method to add new portal action """
466        # extract posted data
467        id, cat_name, data = self.parseAddForm(self.request.form)
468       
469        # validate posted data
470        errors = self.validateActionFields(cat_name, data)
471       
472        # if not errors find (or create) category and set action to it
473        ksscore = self.getCommandSet('core')
474        kssplone = self.getCommandSet('plone')
475        if not errors:
476            action = self.addAction(cat_name, data)
477           
478            # update client
479            # add one more action to actions list
480            content = self.getActionsList(category=cat_name, tabs=[action,])
481            ksscore.insertHTMLAsLastChild(ksscore.getHtmlIdSelector('tabslist'), content)
482           
483            # update reorder controls
484            self.kss_checkReorderControls(cat_name)
485           
486            # hide adding form
487            ksscore.removeClass(ksscore.getHtmlIdSelector('addaction'), 'adding')
488            self.kss_toggleCollapsible(ksscore.getCssSelector('#addaction .collapseAdvanced .headerAdvanced'), collapse='true')
489           
490            # set client state var 'plonetabs-addingTitle' to empty string for correct id autogeneration functionality
491            ksscore.setStateVar('plonetabs-addingTitle', '')
492           
493            # reset adding form
494            self.kss_resetForm(ksscore.getHtmlIdSelector('addaction'))
495           
496            # issue portal message
497            kssplone.issuePortalMessage(_(u"'%s' action successfully added." % action.id), msgtype="info")
498           
499            # update page
500            self.updatePage(cat_name)
501        else:
502            # expand advanced section if there are errors in id or condition
503            if errors.has_key('id') or errors.has_key('available_expr'):
504                self.kss_toggleCollapsible(ksscore.getCssSelector('#addaction .collapseAdvanced .headerAdvanced'), collapse='false')
505           
506            # send error message
507            kssplone.issuePortalMessage(_(u"Please correct the indicated errors."), msgtype="error")
508       
509        # update errors on client form
510        self.kss_issueErrors(errors)
511   
512    @kssaction
513    def kss_showEditForm(self, id, cat_name):
514        """ Show edit form for given action """
515        act_id, category, action = self.kss_validateAction(id, cat_name)
516       
517        # fetch data
518        action_info = self.copyAction(action)
519        action_info["editing"] = True
520       
521        # update client
522        ksscore = self.getCommandSet("core")
523        content = self.actionslist_template(tabs=[action_info,])
524        ksscore.replaceHTML(ksscore.getHtmlIdSelector(id), content)
525       
526        # focus name field
527        ksscore.focus(ksscore.getCssSelector("#%s input[name=title_%s]" % (id, act_id)))
528   
529    @kssaction
530    def kss_hideEditForm(self, id, cat_name):
531        """ Hide edit form for given action """
532        act_id, category, action = self.kss_validateAction(id, cat_name)
533       
534        # update client
535        ksscore = self.getCommandSet("core")
536        content = self.actionslist_template(tabs=[action,])
537        ksscore.replaceHTML(ksscore.getHtmlIdSelector(id), content)
538   
539    @kssaction
540    def kss_editAction(self):
541        """ KSS Method to update action """
542        id, cat_name, data = self.parseEditForm(self.request.form)
543       
544        # get category and action to edit
545        category = self.getActionCategory(cat_name)
546        action = category[id]
547       
548        # validate posted data
549        errors = self.validateActionFields(cat_name, data, allow_dup=True)
550       
551        html_id = '%s%s%s' % (self.prefix, id, self.sufix)
552        ksscore = self.getCommandSet('core')
553        kssplone = self.getCommandSet('plone')
554        if not errors:
555            action = self.updateAction(id, cat_name, data)
556           
557            # update client
558            # replace action item with updated one
559            content = self.getActionsList(category=cat_name, tabs=[action,])
560            ksscore.replaceHTML(ksscore.getHtmlIdSelector(html_id), content)
561           
562            # issue portal message
563            kssplone.issuePortalMessage(_(u"'%s' action saved." % action.id), msgtype="info")
564           
565            # update page
566            self.updatePage(cat_name)
567        else:
568            # issue error messages
569            self.kss_issueErrors(errors, editform=id)
570           
571            # expand advanced section if there are errors in id, action url or condition
572            if errors.has_key('id') or errors.has_key('available_expr') or errors.has_key('url_expr'):
573                self.kss_toggleCollapsible(ksscore.getCssSelector('#%s .headerAdvanced' % html_id), collapse='false')
574           
575            # send error message
576            kssplone.issuePortalMessage(_(u"Please correct the indicated errors."), msgtype="error")
577   
578    #
579    # Utility Methods
580    #
581   
582    def copyAction(self, action):
583        """ Copyt action to dictionary """
584        action_info = {'description':action.description}
585        for attr in ACTION_ATTRS:
586            action_info[attr] = getattr(action, attr)
587        return action_info
588   
589    def validateActionFields(self, cat_name, data, allow_dup=False):
590        """ Check action fields on validity """
591        errors = {}
592       
593        if allow_dup:
594            category = ActionCategory(cat_name)           # create dummy category to avoid id duplication during action update
595        else:
596            category = self.getOrCreateCategory(cat_name) # get or create (if necessary) actions category
597       
598        # validate action id
599        chooser = INameChooser(category)
600        try:
601            chooser.checkName(data['id'], self.context)
602        except Exception, e:
603            errors['id'] = "%s" % str(e)
604       
605        # validate action name
606        if not data['title'].strip():
607            errors['title'] = 'Empty or invalid title specified'
608       
609        # validate condition expression
610        if data['available_expr']:
611            try:
612                Expression(data['available_expr'])
613            except Exception, e:
614                errors["available_expr"] = "%s" % str(e)
615       
616        # validate action expression
617        if data['url_expr']:
618            try:
619                Expression(data['url_expr'])
620            except Exception, e:
621                errors["url_expr"] = "%s" % str(e)
622       
623        return errors
624   
625    def processErrors(self, errors, prefix='', sufix=''):
626        """ Add prefixes, sufixes to error ids
627            This is necessary during edit form validation,
628            because every edit form on the page has it's own sufix (id) """
629        if not (prefix or sufix):
630            return errors
631       
632        result = {}
633        for key, value in errors.items():
634            result['%s%s%s' % (prefix, key, sufix)] = value
635       
636        return result
637   
638    def parseEditForm(self, form):
639        """ Extract all needed fields from edit form """
640        # get original id and category
641        info = {}
642        id = form['orig_id']
643        category = form['category']
644       
645        # preprocess 'visible' field (checkbox needs special checking)
646        if form.has_key('visible_%s' % id):
647            form['visible_%s' % id] = True
648        else:
649            form['visible_%s' % id] = False
650       
651        # collect all action fields
652        for attr in ACTION_ATTRS:
653            info[attr] = form['%s_%s' % (attr, id)]
654       
655        return (id, category, info)
656   
657    def parseAddForm(self, form):
658        """ Extract all needed fields from add form """
659        info = {}
660        id = form['id']
661        category = form['category']
662       
663        # preprocess 'visible' field (checkbox needs special checking)
664        if form.has_key('visible') and form['visible']:
665            form['visible'] = True
666        else:
667            form['visible'] = False
668       
669        # collect all action fields
670        for attr in ACTION_ATTRS:
671            info[attr] = form[attr]
672       
673        return (id, category, info)
674   
675    def getActionCategory(self, name):
676        portal_actions = getToolByName(self.context, 'portal_actions')
677        return portal_actions[name]
678   
679    def getOrCreateCategory(self, name):
680        """ Get or create (if necessary) category """
681        portal_actions = getToolByName(self.context, 'portal_actions')
682        if name not in map(lambda x: x.id, filter(lambda x: IActionCategory.providedBy(x), portal_actions.objectValues())):
683            portal_actions._setObject(name, ActionCategory(name))
684        return self.getActionCategory(name)
685   
686    def setSiteProperties(self, **kw):
687        """ Change site_properties """
688        site_properties = getToolByName(self.context, "portal_properties").site_properties
689        site_properties.manage_changeProperties(**kw)
690        return True
691   
692    #
693    # Utility methods for the kss actions management
694    #
695   
696    def kss_checkReorderControls(self, cat_name):
697        """ Add "noitems" class to Reorder controls to hide them if category is empty """
698        ksscore = self.getCommandSet('core')
699        selector = ksscore.getHtmlIdSelector("reorder")
700        category = self.getActionCategory(cat_name)
701        if filter(lambda x: IAction.providedBy(x), category.objectValues()):
702            ksscore.removeClass(selector, value="noitems")
703        else:
704            ksscore.addClass(selector, value="noitems")
705   
706    def kss_validateAction(self, id, cat_name):
707        """ Check whether action with given id exists in cat_name category """
708        try:
709            category = self.getActionCategory(cat_name)
710        except Exception:
711            raise KSSExplicitError, "'%s' action category does not exist" % cat_name
712       
713        # extract action id from given list item id on client
714        action_id = self.sufix and id[len(self.prefix):-len(self.sufix)] or id[len(self.prefix):]
715        try:
716            action = category[action_id]
717        except Exception:
718            raise KSSExplicitError, "No '%s' action in '%s' category." % (action_id, cat_name)
719       
720        return (action_id, category, action)
721   
722    def kss_issueErrors(self, errors, editform=False, fields=ACTION_ATTRS):
723        """ Display error messages on the client """
724        ksscore = self.getCommandSet('core')
725        for field in fields:
726            self.kss_issueFieldError(ksscore, field, errors.get(field, False), editform)
727   
728    def kss_issueFieldError(self, ksscore, name, error, editform):
729        """ Issue this error message for the field """
730        if editform:
731            id = '%s%s%s' % (self.prefix, editform, self.sufix)
732            field_selector = ksscore.getCssSelector('#%s .edit-field-%s' % (id, UI_ATTRS.get(name, name)))
733            field_error_selector = ksscore.getCssSelector('#%s .edit-field-%s .error-container' % (id, UI_ATTRS.get(name, name)))
734        else:
735            field_selector = ksscore.getCssSelector('#addaction .field-%s' % UI_ATTRS.get(name, name))
736            field_error_selector = ksscore.getCssSelector('#addaction .field-%s .error-container' % UI_ATTRS.get(name, name))
737
738        if error:
739            ksscore.replaceInnerHTML(field_error_selector, _(error))
740            ksscore.addClass(field_selector, 'error')
741        else:
742            ksscore.clearChildNodes(field_error_selector)
743            ksscore.removeClass(field_selector, 'error')
744   
745    def kss_toggleCollapsible(self, selector, collapsed=None, expanded=None, collapse=None):
746        """ KSS Server command to add plonetabs-toggleCollapsible client action to response """
747        command = self.commands.addCommand('plonetabs-toggleCollapsible', selector)
748        if collapsed is not None:
749            data = command.addParam('collapsed', collapsed)
750        if expanded is not None:
751            data = command.addParam('expanded', expanded)
752        if collapse is not None:
753            data = command.addParam('collapse', collapse)
754   
755    def kss_resetForm(self, selector):
756        """ KSS Server command to reset form on client """
757        command = self.commands.addCommand('plonetabs-resetForm', selector)
758   
759    def validateAction(self, id, category, prefix="tabslist_"):
760        """ If action with given id and category doesn't exist - raise kss exception """
761        portal_actions = getToolByName(self.context, "portal_actions")
762       
763        # remove prefix, added for making ids on configlet unique self.prefix
764        act_id = id[len(self.prefix):]
765       
766        if category not in portal_actions.objectIds():
767            raise KSSExplicitError, "Unexistent root portal actions category %s" % category
768       
769        cat_container = portal_actions[category]
770        if act_id not in map(lambda x: x.id, filter(lambda x: IAction.providedBy(x), cat_container.objectValues())):
771            raise KSSExplicitError, "%s action does not exist in %s category" % (act_id, category)
772       
773        return (cat_container, act_id)
774   
775    #
776    # Basic API to work with portal actions tool in a more pleasent way
777    #
778   
779    def addAction(self, cat_name, data):
780        """ Create and add new action to category with given name """
781        id = data.pop('id')
782        category = self.getOrCreateCategory(cat_name)
783        action = Action(id, **data)
784        category._setObject(id, action)
785        return action
786   
787    def updateAction(self, id, cat_name, data):
788        """ Update action with given id and category """
789        new_id = data.pop('id')
790        category = self.getActionCategory(cat_name)
791       
792        # rename action if necessary
793        if id != new_id:
794            category.manage_renameObject(id, new_id)
795       
796        # get action
797        action = category[new_id]
798       
799        # update action properties
800        for attr in data.keys():
801            if data.has_key(attr):
802                action._setPropValue(attr, data[attr])
803       
804        return action
805   
806    def deleteAction(self, id, cat_name):
807        """ Delete action with given id from given category """
808        category = self.getActionCategory(cat_name)
809        category.manage_delObjects(ids=[id,])
810        return True
811   
812    def moveAction(self, id, cat_name, steps=0):
813        """ Move action by a given steps """
814        if steps != 0:
815            category = self.getActionCategory(cat_name)
816            if steps > 0:
817                category.moveObjectsUp([id,], steps)
818            else:
819                category.moveObjectsDown([id,], abs(steps))
820            return True
821        return False
822   
823    #
824    # KSS Methods that are used to update different parts of the page
825    # accordingly to category
826    #
827   
828    def updatePage(self, category):
829        """ Seek for according method in class and calls it if found
830            Example of making up method's name:
831                portal_tabs => updatePortalTabs """
832        method_name = 'update%sPageSection' % ''.join(map(lambda x: x.capitalize(), category.split('_')))
833        if hasattr(self, method_name):
834            getattr(self, method_name)()
835   
836    def updatePortalTabsPageSection(self):
837        """ Method for updating global-sections on client """
838        ksscore = self.getCommandSet("core")
839        ksscore.replaceHTML(
840            ksscore.getHtmlIdSelector("portal-globalnav"),
841            self.sections_template(),
842            withKssSetup="False")
843   
844    def updateSiteActionsPageSection(self):
845        """ Method for updating site action on client """
846        ksszope = self.getCommandSet("zope")
847        ksszope.refreshViewlet(
848            self.getCommandSet("core").getHtmlIdSelector("portal-siteactions"),
849            "plone.portalheader",
850            "plone.site_actions")
851   
852    def updateUserPageSection(self):
853        """ Method for updating site action on client """
854        ksszope = self.getCommandSet("zope")
855        ksszope.refreshViewlet(
856            self.getCommandSet("core").getHtmlIdSelector("portal-personaltools-wrapper"),
857            "plone.portaltop",
858            "plone.personal_bar")
859
860
Note: See TracBrowser for help on using the repository browser.