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

Last change on this file since 55 was 55, checked in by crchemist, 18 years ago

Remove superfluous code

  • Property svn:eol-style set to native
File size: 26.1 KB
Line 
1import copy, sys
2
3from Acquisition import aq_inner
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
10
11from OFS.CopySupport import CopyError
12
13from Products.CMFCore.utils import getToolByName
14from Products.CMFCore.interfaces import IAction, IActionCategory
15from Products.CMFCore.ActionInformation import Action, ActionCategory
16from Products.CMFCore.Expression import Expression
17from Products.CMFEditions.setuphandlers import DEFAULT_POLICIES
18from Products.CMFPlone import PloneMessageFactory as _
19from Products.CMFPlone import PloneMessageFactory as pmf
20from Products.CMFPlone import utils
21from Products.CMFPlone.browser.navigation import get_view_url
22from Products.Five.browser import BrowserView
23from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
24from Products.statusmessages.interfaces import IStatusMessage
25
26from plone.app.layout.navigation.root import getNavigationRoot
27from plone.app.kss.plonekssview import PloneKSSView
28from plone.app.workflow.remap import remap_workflow
29from plone.memoize.instance import memoize
30from kss.core import kssaction, KSSExplicitError
31
32from quintagroup.plonetabs.config import *
33from interfaces import IPloneTabsControlPanel
34
35ACTION_ATTRS = ["id", "visible", "url_expr", "title", "available_expr"]
36
37class PloneTabsControlPanel(PloneKSSView):
38   
39    implements(IPloneTabsControlPanel)
40   
41    template = ViewPageTemplateFile("templates/plonetabs.pt")
42    actionslist_template = ViewPageTemplateFile("templates/actionslist.pt")
43    autogenerated_template = ViewPageTemplateFile("templates/autogenerated.pt")
44    sections_template = ViewPageTemplateFile("templates/sections.pt")
45   
46    def __call__(self):
47        """ Perform the update and redirect if necessary, or render the page """
48        postback = True
49        errors = {}
50        context = aq_inner(self.context)
51       
52        form = self.request.form
53        action = form.get("action", "")
54        submitted = form.get('form.submitted', False)
55       
56        # action handler def handler(self, form)
57        if action == "add_action":
58            if submitted:
59                postback = self.manage_addAction(form, errors)
60        elif action == "edit_action":
61            if submitted:
62                postback = self.manage_editAction(form, errors)
63        elif action == "delete_action":
64            postback = self.manage_deleteAction(self.request, errors)
65        #elif action == "visible_action":
66                #pass
67        elif action == "moveup_action":
68            postback = self.manage_moveUpAction(self.request, errors)
69        elif action == "movedown_action":
70            postback = self.manage_moveDownAction(self.request, errors)
71        elif action == "set_autogeneration":
72            if submitted:
73                postback = self.manage_setAutogeneration(form, errors)
74        else:
75            postback = True
76       
77        if postback:
78            return self.template(errors=errors)
79   
80    ########################################
81    # Methods for processing configlet posts
82    ########################################
83   
84    def manage_setAutogeneration(self, form, errors):
85        """ Process managing autogeneration settings """
86       
87        # set excludeFromNav property for root objects
88        portal = getMultiAdapter((aq_inner(self.context), self.request), name='plone_portal_state').portal()
89        generated_tabs = form.get("generated_tabs", '0')
90        nonfolderish_tabs = form.get("nonfolderish_tabs", '0')
91       
92        for item in self.getRootTabs():
93            obj = getattr(portal, item['id'], None)
94            if obj is not None:
95                checked = form.get(item['id'], None)
96                if checked == '1':
97                    obj.update(excludeFromNav=False)
98                else:
99                    obj.update(excludeFromNav=True)
100
101        # set disable_folder_sections property
102        changeProperties = getToolByName(self.context, "portal_properties").site_properties.manage_changeProperties
103       
104        if int(generated_tabs) == 1:
105            changeProperties(disable_folder_sections=False)
106        else:
107            changeProperties(disable_folder_sections=True)
108       
109        # set disable_nonfolderish_sections property
110        if int(nonfolderish_tabs) == 1:
111            changeProperties(disable_nonfolderish_sections=False)
112        else:
113            changeProperties(disable_nonfolderish_sections=True)
114       
115        IStatusMessage(self.request).addStatusMessage(_(u"Changes saved!"), type="info")
116       
117        self.redirect()
118       
119        return False
120   
121    def manage_addAction(self, form, errors):
122        """ Add a new action to given category, if category doesn't exist, create it """
123       
124        # extract data from request's form
125        id = form.get("id")
126        category = form.get("category")
127       
128        # before checking for existence category we will validate all input data
129        # and issue errors if needed
130       
131        # create and validate action
132        if not id:
133            errors["id"] = _(u"Id field is required")
134        if not form.get("title"):
135            errors["title"] = _(u"Title field is required")
136
137        action = Action(id)
138        for prop in ACTION_ATTRS:
139            if prop != "id":
140                try:
141                    if prop == "visible":
142                        value = int(form.get(prop, '0'))
143                    else:
144                        value = form.get(prop)
145                   
146                    action._setPropValue(prop, value)
147                except Exception, e:
148                    errors[prop] = "%s" % str(e)
149       
150        # if not errors find (or create) category and set action to it
151        if not errors:
152            portal_actions = getToolByName(self.context, "portal_actions")
153            cat_container = None
154           
155            if category not in map(lambda x: x.id, filter(lambda x: IActionCategory.providedBy(x), portal_actions.objectValues())):
156                try:
157                    portal_actions._setObject(category, ActionCategory(category))
158                except Exception, e:
159                    errors["select_category"] = "%s" % str(e)
160                else:
161                    cat_container = portal_actions[category]
162            else:
163                cat_container = portal_actions[category]
164           
165            if cat_container is not None:
166                try:
167                    cat_container._setObject(id, action)
168                except Exception, e:
169                    errors["id"] = "%s" % str(e)
170       
171        if not errors:
172            IStatusMessage(self.request).addStatusMessage(_(u"'%s' action successfully added." % id), type="info")
173            self.redirect(search="category=%s" % category)
174            return False
175        else:
176            IStatusMessage(self.request).addStatusMessage(_(u"Please correct the indicated errors."), type="error")
177            return True
178   
179    def manage_editAction(self, form, errors):
180        """ Add a new action to given category, if category doesn't exist, create it """
181        portal_actions = getToolByName(self.context, "portal_actions")
182       
183        # extract data from request's form
184        orig_id = form.get("orig_id")
185        new_id = form.get("id_%s" % orig_id)
186        category = form.get("category")
187       
188        # before updating action we validate action id/category
189        if category not in portal_actions.objectIds():
190            IStatusMessage(self.request).addStatusMessage(_(u"Unexistent root portal actions category '%s'" % category), type="error")
191        else:
192            cat_container = portal_actions[category]
193            if orig_id not in map(lambda x: x.id, filter(lambda x: IAction.providedBy(x), cat_container.objectValues())):
194                IStatusMessage(self.request).addStatusMessage(_(u"'%s' action does not exist in '%s' category" % (orig_id, category)), type="error")
195            else:
196                # validate input
197                # id is required
198                if not new_id:
199                    errors["id_%s" % orig_id] = _(u"Id field is required")
200               
201                # title is required
202                if not form.get("title_%s" % orig_id):
203                    errors["title_%s" % orig_id] = _(u"Title field is required")
204               
205                # check action for valid tal expression
206                try:
207                    Expression(form.get("url_expr_%s" % orig_id))
208                except Exception, e:
209                    errors["url_expr_%s" % orig_id] = "%s" % str(e)
210               
211                # check condition for valid tal expression
212                try:
213                    Expression(form.get("available_expr_%s" % orig_id))
214                except Exception, e:
215                    errors["available_expr_%s" % orig_id] = "%s" % str(e)
216               
217                # check visible for integer
218                try:
219                    int(form.get("visible_%s" % orig_id, '0'))
220                except Exception, e:
221                    errors["visible_%s" % orig_id] = "%s" % str(e)
222               
223                # rename action if needed
224                if not errors:
225                    if new_id != orig_id:
226                        try:
227                            cat_container.manage_renameObject(orig_id, new_id)
228                        except CopyError, e:
229                            errors["id_%s" % orig_id] = _(u"Invalid id")
230                        except:
231                            exctype, v = sys.exc_info()[:2]
232                            errors["id_%s" % orig_id] = "%s" % str(v)
233               
234                if not errors:
235                    action = cat_container[new_id]
236                   
237                    for prop in ACTION_ATTRS:
238                        if prop != "id":
239                            if prop == "visible":
240                                value = int(form.get("%s_%s" % (prop, orig_id), '0'))
241                            else:
242                                value = form.get("%s_%s" % (prop, orig_id))
243                            action._setPropValue(prop, value)
244                   
245                    IStatusMessage(self.request).addStatusMessage(_(u"'%s' action saved." % new_id), type="info")
246                    self.redirect(search="category=%s" % category)
247                   
248                    return False
249       
250        IStatusMessage(self.request).addStatusMessage(_(u"Please correct the indicated errors."), type="error")
251        return True
252   
253    def manage_deleteAction(self, request, errors):
254        """ Delete action with given id/category """
255        portal_actions = getToolByName(self.context, "portal_actions")
256
257        id = request.get("id", "")
258        category = request.get("category", "")
259       
260        if category not in portal_actions.objectIds():
261            IStatusMessage(self.request).addStatusMessage(_(u"Unexistent root portal actions category '%s'" % category), type="error")
262        else:
263            cat_container = portal_actions[category]
264            if id not in map(lambda x: x.id, filter(lambda x: IAction.providedBy(x), cat_container.objectValues())):
265                IStatusMessage(self.request).addStatusMessage(_(u"'%s' action does not exist in '%s' category" % (id, category)), type="error")
266            else:
267                cat_container.manage_delObjects(ids=[id,])
268                IStatusMessage(self.request).addStatusMessage(_(u"'%s' action in '%s' category successfully deleted" % (id, category)), type="info")
269       
270        self.redirect(search="category=%s" % category)
271        return False
272   
273    def manage_moveUpAction(self, request, errors):
274        """ Move up given action by one position """
275        portal_actions = getToolByName(self.context, "portal_actions")
276
277        id = request.get("id", "")
278        category = request.get("category", "")
279       
280        if category not in portal_actions.objectIds():
281            IStatusMessage(self.request).addStatusMessage(_(u"Unexistent root portal actions category '%s'" % category), type="error")
282        else:
283            cat_container = portal_actions[category]
284            if id not in map(lambda x: x.id, filter(lambda x: IAction.providedBy(x), cat_container.objectValues())):
285                IStatusMessage(self.request).addStatusMessage(_(u"'%s' action does not exist in '%s' category" % (id, category)), type="error")
286            else:
287                cat_container.moveObjectsUp([id,], 1)
288                IStatusMessage(self.request).addStatusMessage(_(u"'%s' action in '%s' category moved up" % (id, category)), type="info")
289       
290        self.redirect(search="category=%s" % category)
291        return False
292   
293    def manage_moveDownAction(self, request, errors):
294        """ Move up given action by one position """
295        portal_actions = getToolByName(self.context, "portal_actions")
296
297        id = request.get("id", "")
298        category = request.get("category", "")
299       
300        if category not in portal_actions.objectIds():
301            IStatusMessage(self.request).addStatusMessage(_(u"Unexistent root portal actions category '%s'" % category), type="error")
302        else:
303            cat_container = portal_actions[category]
304            if id not in map(lambda x: x.id, filter(lambda x: IAction.providedBy(x), cat_container.objectValues())):
305                IStatusMessage(self.request).addStatusMessage(_(u"'%s' action does not exist in '%s' category" % (id, category)), type="error")
306            else:
307                cat_container.moveObjectsDown([id,], 1)
308                IStatusMessage(self.request).addStatusMessage(_(u"'%s' action in '%s' category moved down" % (id, category)), type="info")
309       
310        self.redirect(search="category=%s" % category)
311        return False
312   
313    def redirect(self, url="", search="", hash=""):
314        """ Redirect to @@plonetabs-controlpanel configlet """
315       
316        if url == "":
317            portal_url =  getMultiAdapter((self.context, self.request), name=u"plone_portal_state").portal_url()
318            url = "%s/%s" % (portal_url, "@@plonetabs-controlpanel")
319       
320        if search != "":
321            search = "?%s" % search
322       
323        if hash != "":
324            hash = "#%s" % hash
325       
326        self.request.response.redirect("%s%s%s" % (url, search, hash))
327   
328   
329    ###################################
330    ##  Methods - providers for templates
331    ###################################
332   
333    def getPageTitle(self, category="portal_tabs"):
334        """ See interface """
335        portal_props = getToolByName(self.context, "portal_properties")
336        default_title = "Plone '%s' Configuration" % category
337       
338        if not hasattr(portal_props, PROPERTY_SHEET):
339            return default_title
340       
341        sheet = getattr(portal_props, PROPERTY_SHEET)
342        if not hasattr(sheet, FIELD_NAME):
343            return default_title
344       
345        field = sheet.getProperty(FIELD_NAME)
346        dict = {}
347        for line in field:
348            cat, title = line.split("|", 2)
349            dict[cat] = title
350       
351        return dict.get(category, None) or default_title
352   
353    def hasActions(self, category="portal_tabs"):
354        """ See interface """
355        return len(getToolByName(self.context, "portal_actions").listActions(categories=[category,])) > 0
356   
357    def getPortalActions(self, category="portal_tabs"):
358        """ See interface """
359        portal_actions = getToolByName(self.context, "portal_actions")
360       
361        if category not in portal_actions.objectIds():
362            return []
363       
364        actions = []
365        for item in portal_actions[category].objectValues():
366            if IAction.providedBy(item):
367                actions.append(item)
368       
369        return actions
370   
371    def isGeneratedTabs(self):
372        """ See interface """
373        site_properties = getToolByName(self.context, "portal_properties").site_properties
374        return not site_properties.getProperty("disable_folder_sections", False)
375   
376    def isNotFoldersGenerated(self):
377        """ See interface """
378        site_properties = getToolByName(self.context, "portal_properties").site_properties
379        return not site_properties.getProperty("disable_nonfolderish_sections", False)
380   
381    def getActionsList(self, category="portal_tabs", errors={}):
382        """ See interface """
383        return self.actionslist_template(category=category, errors=errors)
384   
385    def getGeneratedTabs(self):
386        """ See interface """
387        return self.autogenerated_template()
388   
389    def getRootTabs(self):
390        """ See interface """
391        context = aq_inner(self.context)
392       
393        portal_catalog = getToolByName(context, 'portal_catalog')
394        portal_properties = getToolByName(context, 'portal_properties')
395        navtree_properties = getattr(portal_properties, 'navtree_properties')
396       
397        # Build result dict
398        result = []
399       
400        # check whether we only want actions
401        if not self.isGeneratedTabs():
402            return result
403       
404        query = {}
405       
406        rootPath = getNavigationRoot(context)
407        query['path'] = {'query' : rootPath, 'depth' : 1}
408        query['portal_type'] = utils.typesToList(context)
409       
410        sortAttribute = navtree_properties.getProperty('sortAttribute', None)
411        if sortAttribute is not None:
412            query['sort_on'] = sortAttribute
413           
414            sortOrder = navtree_properties.getProperty('sortOrder', None)
415            if sortOrder is not None:
416                query['sort_order'] = sortOrder
417       
418        if navtree_properties.getProperty('enable_wf_state_filtering', False):
419            query['review_state'] = navtree_properties.getProperty('wf_states_to_show', [])
420       
421        query['is_default_page'] = False
422
423        if not self.isNotFoldersGenerated():
424            query['is_folderish'] = True
425
426        # Get ids not to list and make a dict to make the search fast
427        idsNotToList = navtree_properties.getProperty('idsNotToList', ())
428        excludedIds = {}
429        for id in idsNotToList:
430            excludedIds[id]=1
431
432        rawresult = portal_catalog.searchResults(**query)
433
434        # now add the content to results
435        for item in rawresult:
436            if not excludedIds.has_key(item.getId):
437                id, item_url = get_view_url(item)
438                data = {'name'       : utils.pretty_title_or_id(context, item),
439                        'id'         : id,
440                        'url'        : item_url,
441                        'description': item.Description,
442                        'exclude_from_nav' : item.exclude_from_nav}
443                result.append(data)
444       
445        return result
446   
447    def getCategories(self):
448        """ See interface """
449        portal_actions = getToolByName(self.context, "portal_actions")
450        return portal_actions.objectIds()
451   
452    def test(self, condition, ifTrue, ifFalse):
453        """ See interface """
454        if condition:
455            return ifTrue
456        else:
457            return ifFalse
458   
459    # methods for rendering global-sections viewlet via kss,
460    # due to bug in macroContent when global-section list is empty,
461    # ul have condition
462    def portal_tabs(self):
463        """ See global-sections viewlet """
464        actions = context_state = getMultiAdapter((self.context, self.request), name=u"plone_context_state").actions()
465        portal_tabs_view = getMultiAdapter((self.context, self.request), name="portal_tabs_view")
466       
467        return portal_tabs_view.topLevelTabs(actions=actions)
468   
469    def selected_portal_tab(self):
470        """ See global-sections viewlet """
471        selectedTabs = self.context.restrictedTraverse('selectedTabs')
472        selected_tabs = selectedTabs('index_html', self.context, self.portal_tabs())
473       
474        return selected_tabs['portal']
475   
476    ##########################
477    # kss server actions
478    ##########################
479   
480    def updateGlobalSections(self, ksscore):
481        """ Method for updating global-sections on client """
482        ksscore.replaceHTML(
483            ksscore.getHtmlIdSelector("portal-globalnav"),
484            self.sections_template(),
485            withKssSetup="False")
486   
487   
488    # XXX TODO
489    #def updateSection(self, ksscore, section):
490        #""" Method for updating global-sections on client """
491       
492        #replace_id = section
493        #macro = SECTION_MAPPING.get(section, None)
494       
495        #if macro is not None:
496            #ksscore.replaceHTML(
497                #ksscore.getHtmlIdSelector(replace_id),
498                #self.macroContent(macro),
499                ##self.sections_template(),
500                #withKssSetup="False")
501   
502    def validateAction(self, id, category, prefix="tabslist_"):
503        """ If action with given id and category doesn't exist - raise kss exception """
504        portal_actions = getToolByName(self.context, "portal_actions")
505       
506        # remove prefix, added for making ids on configlet unique ("tabslist_")
507        act_id = id[len("tabslist_"):]
508       
509        if category not in portal_actions.objectIds():
510            raise KSSExplicitError, "Unexistent root portal actions category %s" % category
511       
512        cat_container = portal_actions[category]
513        if act_id not in map(lambda x: x.id, filter(lambda x: IAction.providedBy(x), cat_container.objectValues())):
514            raise KSSExplicitError, "%s action does not exist in %s category" % (act_id, category)
515       
516        return (cat_container, act_id)
517   
518    @kssaction
519    def toggleGeneratedTabs(self, field, checked='0'):
520        """ Toggle autogenaration setting on configlet """
521       
522        changeProperties = getToolByName(self.context, "portal_properties").site_properties.manage_changeProperties
523        if checked == '1':
524            changeProperties(**{field : False})
525        else:
526            changeProperties(**{field : True})
527       
528        ksscore = self.getCommandSet("core")
529        replace_id = "roottabs"
530        content = self.getGeneratedTabs()
531       
532        ksscore.replaceInnerHTML(ksscore.getHtmlIdSelector(replace_id), content, withKssSetup="True")
533       
534        # update global-sections viewlet
535        self.updateGlobalSections(ksscore)
536   
537    @kssaction
538    def toggleActionsVisibility(self, id, checked='0', category=None):
539        """ Toggle visibility for portal actions """
540        portal_actions = getToolByName(self.context, "portal_actions")
541        cat_container, act_id = self.validateAction(id, category)
542       
543        if checked == '1':
544            checked = True
545        else:
546            checked = False
547       
548        cat_container[act_id].visible = checked
549       
550        ksscore = self.getCommandSet("core")
551        if checked:
552            ksscore.removeClass(ksscore.getHtmlIdSelector(id), value="invisible")
553        else:
554            ksscore.addClass(ksscore.getHtmlIdSelector(id), value="invisible")
555       
556        # update global-sections viewlet
557        if category == "portal_tabs":
558            self.updateGlobalSections(ksscore)
559   
560    @kssaction
561    def toggleRootsVisibility(self, id, checked='0'):
562        """ Toggle visibility for portal root objects (exclude_from_nav) """
563        portal = getMultiAdapter((aq_inner(self.context), self.request), name='plone_portal_state').portal()
564       
565        # remove prefix, added for making ids on configlet unique ("roottabs_")
566        obj_id = id[len("roottabs_"):]
567       
568        if obj_id not in portal.objectIds():
569            raise KSSExplicitError, "Object with %s id doesn't exist in portal root" % obj_id
570       
571        if checked == '1':
572            checked = True
573        else:
574            checked = False
575       
576        portal[obj_id].update(excludeFromNav=not checked)
577       
578        ksscore = self.getCommandSet("core")
579        if checked:
580            ksscore.removeClass(ksscore.getHtmlIdSelector(id), value="invisible")
581        else:
582            ksscore.addClass(ksscore.getHtmlIdSelector(id), value="invisible")
583       
584        # update global-sections viewlet
585        self.updateGlobalSections(ksscore)
586   
587    @kssaction
588    def deleteAction(self, id, category):
589        """ Delete portal action with given id & category """
590        portal_actions = getToolByName(self.context, "portal_actions")
591        cat_container, act_id = self.validateAction(id, category)
592       
593        cat_container.manage_delObjects(ids=[act_id,])
594       
595        # update action list on client
596        ksscore = self.getCommandSet("core")
597       
598        ksscore.deleteNode(ksscore.getHtmlIdSelector(id))
599       
600        # add "noitems" class to Reorder controls to hide it
601        if not filter(lambda x: IAction.providedBy(x), cat_container.objectValues()):
602            ksscore.addClass(ksscore.getHtmlIdSelector("reorder"), value="noitems")
603       
604        # XXX TODO: fade effect during removing, for this kukit js action/command plugin needed
605       
606        # update global-sections viewlet
607        if category == "portal_tabs":
608            self.updateGlobalSections(ksscore)
609   
610    @kssaction
611    def editAction(self, id, category):
612        """ Show edit form for given action """
613        cat_container, act_id = self.validateAction(id, category)
614       
615        # collect data
616        action_info = self.copyAction(cat_container[act_id])
617        action_info["editing"] = True
618       
619        ksscore = self.getCommandSet("core")
620        content = self.actionslist_template(tabs=[action_info,])
621        replace_id = id
622       
623        ksscore.replaceHTML(ksscore.getHtmlIdSelector(replace_id), content)
624       
625        # focus name field
626        ksscore.focus(ksscore.getCssSelector("#%s input[name=name_%s]" % (id, act_id)))
627   
628    @kssaction
629    def editCancel(self, id, category):
630        """ Hide edit form for given action """
631        cat_container, act_id = self.validateAction(id, category)
632       
633        ksscore = self.getCommandSet("core")
634        content = self.actionslist_template(tabs=[cat_container[act_id],])
635        replace_id = id
636       
637        ksscore.replaceHTML(ksscore.getHtmlIdSelector(replace_id), content)
638   
639    # Utility Methods
640   
641    def copyAction(self, action):
642        """ Copyt action to dictionary """
643        action_info = {}
644        for attr in ACTION_ATTRS:
645            action_info[attr] = getattr(action, attr)
646        return action_info
647   
648
649
650
651
652
653
654
655
656
657
Note: See TracBrowser for help on using the repository browser.