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

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

Added ability to genarate sitemap even googlesitemap_properties sheet is absent

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