source: products/quintagroup.plonetabs/branches/nokss/quintagroup/plonetabs/browser/plonetabs.py

Last change on this file was 3682, checked in by mike, 11 years ago

add icon field

  • Property svn:eol-style set to native
File size: 35.2 KB
RevLine 
[159]1import urllib
[161]2import re
[3680]3import json
[159]4
[45]5from Acquisition import aq_inner
[872]6from DateTime import DateTime
[45]7
[43]8from zope.interface import implements
[507]9from zope.component import getMultiAdapter
[3563]10
11# BBB: compatibility with older plone versions
12try:
[3598]13    # Plone < 4.3
14    from zope.app.container import interfaces
15    INameChooser = interfaces.INameChooser
[3563]16except ImportError:
[3598]17    # Plone >= 4.3
18    from zope.container.interfaces import INameChooser
19
[3681]20from zope.viewlet.interfaces import IViewletManager, IViewlet
21
[507]22from plone.app.layout.navigation.root import getNavigationRoot
23
[43]24from Products.CMFCore.utils import getToolByName
[53]25from Products.CMFCore.interfaces import IAction, IActionCategory
26from Products.CMFCore.ActionInformation import Action, ActionCategory
[55]27from Products.CMFCore.Expression import Expression
[43]28from Products.CMFPlone import utils
29from Products.CMFPlone.browser.navigation import get_view_url
30from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
[872]31from Products.Five.browser import BrowserView
[53]32from Products.statusmessages.interfaces import IStatusMessage
[43]33
[3437]34from quintagroup.plonetabs.config import PROPERTY_SHEET, FIELD_NAME
[3563]35from quintagroup.plonetabs.utils import setupViewletByName
[873]36from quintagroup.plonetabs import messageFactory as _
[43]37from interfaces import IPloneTabsControlPanel
38
[3682]39ACTION_ATTRS = ["id", "title", "url_expr", "icon_expr", "available_expr", "visible"]
[151]40UI_ATTRS = {"id": "id",
41            "title": "name",
42            "url_expr": "action",
[3682]43            "icon_expr": "icon_expr",
[151]44            "available_expr": "condition",
45            "visible": "visible"}
[43]46
[159]47bad_id = re.compile(r'[^a-zA-Z0-9-_~,.$\(\)# @]').search
48
[872]49cookie_name = 'ploneTabsMode'
50
[3437]51
[3680]52class PloneTabsControlPanel():
[3244]53
[43]54    implements(IPloneTabsControlPanel)
[3244]55
[53]56    template = ViewPageTemplateFile("templates/plonetabs.pt")
[43]57    actionslist_template = ViewPageTemplateFile("templates/actionslist.pt")
58    autogenerated_template = ViewPageTemplateFile("templates/autogenerated.pt")
[162]59    autogenerated_list = ViewPageTemplateFile("templates/autogeneratedlist.pt")
[872]60    link_template = ViewPageTemplateFile("templates/changemodelink.pt")
[3244]61
[135]62    # custom templates used to update page sections
[46]63    sections_template = ViewPageTemplateFile("templates/sections.pt")
[3244]64
[149]65    # configuration variables
66    prefix = "tabslist_"
67    sufix = ""
[3244]68
[53]69    def __call__(self):
[747]70        """Perform the update and redirect if necessary, or render the page"""
[53]71        postback = True
[3680]72        ajaxback = False
[53]73        errors = {}
[3244]74
[53]75        form = self.request.form
76        submitted = form.get('form.submitted', False)
[3680]77        ajax_request = form.get('ajax_request', False)
[3244]78
[53]79        # action handler def handler(self, form)
[3680]80        if ajax_request:
81            ajaxback = self.ajax_postback(form, errors)
[135]82        if submitted:
[3672]83            postback = self.submitted_postback(form, errors)
[3244]84
[872]85        mode = self.request.get(cookie_name, False)
86        if mode in ('plain', 'rich'):
87            # set cookie to remember the choice
88            expires = (DateTime() + 365).toZone('GMT').rfc822()
89            self.request.response.setCookie(cookie_name, mode, path='/',
90                                            expires=expires)
[3244]91
[3680]92        if ajaxback:
93            return json.dumps(ajaxback)
94        elif postback:
[53]95            return self.template(errors=errors)
[3244]96
[3672]97    def submitted_postback(self, form, errors):
98        """submitted postback"""
99        if 'add.add' in form.keys():
100            postback = self.manage_addAction(form, errors)
101        elif "edit.save" in form.keys():
102            postback = self.manage_editAction(form, errors)
103        elif "edit.delete" in form.keys():
104            postback = self.manage_deleteAction(form, errors)
105        elif "edit.moveup" in form.keys():
106            postback = self.manage_moveUpAction(form, errors)
107        elif "edit.movedown" in form.keys():
108            postback = self.manage_moveDownAction(form, errors)
109        elif "autogenerated.save" in form.keys():
110            postback = self.manage_setAutogeneration(form, errors)
111        else:
112            postback = True
113        return postback
114
[3680]115
116    def ajax_postback(self, form, errors):
117        """ajax_postback ajaxback"""
118        conv_dict = {
119            "edit_moveact": "manage_ajax_moveAction",
120            "category_change": "manage_ajax_changeCategory",
121            "edit_delete": "manage_ajax_deleteAction",
122            "edit_save": "manage_ajax_saveAction",
123            "edit_cancel": "manage_ajax_cancelEditting",
124            "tabslist_visible": "manage_ajax_toggleActionsVisibility",
125            "roottabs_visible": "manage_ajax_toggleRootsVisibility",
126            "generated_tabs": "manage_ajax_toggleGeneratedTabs",
127            "add_add": "manage_ajax_addAction",
128        }
129       
130        for method in conv_dict.keys():
131            if method in form.keys():
132                method_val = conv_dict.get(method)
133                return  getattr(self, method_val)(form, errors)
134
135        return False
136
[3672]137    @property
138    def plone_portal_state(self):
139        """plone_portal_state"""
140        return getMultiAdapter((aq_inner(self.context), self.request),
141                               name='plone_portal_state')
142
143    @property
144    def portal_properties(self):
145        """portal_properties"""
146        return getToolByName(self.context, 'portal_properties', None)
147
148    @property
149    def portal_actions(self):
150        """portal_actions"""
151        return getToolByName(self.context, 'portal_actions')
152
[53]153    ########################################
[3680]154    # AJAX Methods
155    ########################################
156
157    def manage_ajax_addAction(self, form, errs):
158        # extract posted data
159        resp_dict = {}
160        cat_name = form['cat_name']
161        id, ie7bad_category, data = self.parseAddForm(self.request.form)
162
163        # validate posted data
164        errors = self.validateActionFields(cat_name, data)
165
166        if not errors:
167            action = self.addAction(cat_name, data)
168            # add one more action to actions list
169            content = self.getActionsList(category=cat_name, tabs=[action, ])
170            resp_dict['content'] = content
171            resp_dict['status_code'] = 200
172            resp_dict['status_message'] = "%s action successfully added." % action.id
173        else:
174            resp_dict['status_message'] = "Please correct the indicated errors."
175            resp_dict['status_code'] = 500
176            resp_dict['content'] = errors
177        return resp_dict
178
179    def manage_ajax_toggleGeneratedTabs(self, form, errs):
180        """Toggle autogenaration setting on configlet"""
181        resp_dict = {}
182        errors = []
183       
184        #TODO: parse, validate form
185        checked = form['generated_tabs']
186        field = form['field']
187       
188        if not errors:
189            if checked == 'true':
190                self.setSiteProperties(**{field: False})
191                message = _(u"Generated tabs switched on.")
192            else:
193                self.setSiteProperties(**{field: True})
194                message = _(u"Generated tabs switched off.")
195            content = self.getGeneratedTabs()
196            resp_dict['content'] = content
197            resp_dict['status_code'] = 200
198            resp_dict['status_message'] = message
199        else:
200            resp_dict['status_message'] = errors
201            resp_dict['status_code'] = 500
202        return resp_dict
203
204    def manage_ajax_toggleRootsVisibility(self, form, errs):
205        #Toggle visibility for portal actions
206        resp_dict = {}
207        errors = []
208       
209        #TODO: parse, validate form
210        id = form['orig_id']
211        checked = form['visibility']
212
213        portal = getMultiAdapter((aq_inner(self.context), self.request),
214                                 name='plone_portal_state').portal()
215
216        # remove prefix, added for making ids on configlet unique ("roottabs_")
217        obj_id = id[len("roottabs_"):]
218
219        if obj_id not in portal.objectIds():
220            errors.append("Object with %s id doesn't exist in portal root." % obj_id)
221
222        checked = True if checked == 'true' else False
223
224        if not errors:
225            portal[obj_id].update(excludeFromNav=not checked)
226
227            if checked:
228                message = "%s object was included into navigation." % obj_id
229            else:
230                message = "%s object was excluded from navigation." % obj_id
231            resp_dict['status_message'] = message
232            resp_dict['status_code'] = 200
233        else:
234            resp_dict['status_message'] = errors
235            resp_dict['status_code'] = 500
236        return resp_dict
237
238    def manage_ajax_toggleActionsVisibility(self, form, errs):
239        #Toggle visibility for portal actions
240        resp_dict = {}
241       
242        #TODO: parse, validate form
243        id = form['orig_id']
244        cat_name = form['category']
245        checked = form['visibility']
246
247        act_id, category, action, errors = self.manage_validateAction(id, cat_name)
248
249        if not errors:
250            self.updateAction(act_id, cat_name,
251                              {'id': act_id, 'visible': (checked == 'true') or False})
252            if checked == 'true':
253                message = "%s action is now visible." % act_id
254            else:
255                message = "%s action is now invisible." % act_id
256            resp_dict['status_message'] = message
257            resp_dict['status_code'] = 200
258        else:
259            resp_dict['status_message'] = errors
260            resp_dict['status_code'] = 500
261        return resp_dict
262   
263    def manage_ajax_deleteAction(self, form, errs):
264        """Delete portal action with given id & category"""
265        resp_dict = {}
266       
267        #TODO: parse, validate form
268        id = form['orig_id']
269        cat_name = form['category']
270
271        act_id, category, action, errors = self.manage_validateAction(id, cat_name)
272        if not errors:
273            self.deleteAction(act_id, cat_name)
274            resp_dict['status_message'] =  "%s action successfully removed." % act_id
275            resp_dict['status_code'] = 200
276        else:
277            resp_dict['status_message'] = errors
278            resp_dict['status_code'] = 500
279        return resp_dict
280
281    def manage_ajax_cancelEditting(self, form, errs):
282        """Hide edit form for given action"""
283        resp_dict = {}
284        #TODO: parse, validate form
285        id = form['orig_id']
286        cat_name = form['category']
287
288        act_id, category, action, errors = self.manage_validateAction(id, cat_name)
289
290        if not errors:
291            content = self.getActionsList(category=cat_name, tabs=[action, ])
292            resp_dict['content'] = content
293            resp_dict['status_message'] =  "Changes discarded."
294            resp_dict['status_code'] = 200
295        else:
296            resp_dict['status_message'] = errors
297            resp_dict['status_code'] = 500
298        return resp_dict
299
300    def manage_validateAction(self, action_id, cat_name):
301        """Check whether action with given id exists in cat_name category"""
302        errors = []
303        act_id = ""
304        category = ""
305        action = ""
306
307        act_id = action_id
308
309        try:
310            category = self.getActionCategory(cat_name)
311        except Exception:
312            errors.append("%s action category does not exist." % cat_name)
313
314        try:
315            action = category[act_id]
316        except Exception:
317            errors.append("No %s action in %s category." %
318                                   (act_id, cat_name))
319        return (act_id, category, action, errors)
320
321    def manage_ajax_saveAction(self, form, errs):
322        """Manage Method to update action"""
323        # extract posted data
324        resp_dict = {}
325        id, cat_name, data = self.parseEditForm(form)
326
327        # get category and action to edit
328        category = self.getActionCategory(cat_name)
329        action = category[id]
330
331        # validate posted data
332        errors = self.validateActionFields(
333            cat_name, data, allow_dup=(id == data['id']))
334
335        if not errors:
336            action = self.updateAction(id, cat_name, data)
337            content = self.getActionsList(category=cat_name, tabs=[action, ])
338            resp_dict['content'] = content
339            resp_dict['status_code'] = 200
340            resp_dict['status_message'] = "%s action successfully updated." % action.id
341        else:
342            resp_dict['status_code'] = 500
343            resp_dict['content'] = errors
344            resp_dict['status_message'] = "Please correct the indicated errors."
345        return resp_dict
346
347    def manage_ajax_moveAction(self, form, errs):
348        resp_dict = {}
349        cat_name = form['cat_name']
350        category = self.getActionCategory(cat_name)
351        components = urllib.unquote(form['actions']).split('&')
352        if self.sufix == '':
353            ids = [component[len(self.prefix):] for component in components]
354        else:
355            ids = [component[len(self.prefix):-len(self.sufix)]
356                   for component in components]
357        # do actual sorting
358        resp = category.moveObjectsByDelta(ids, -len(category.objectIds()))
359
360        if resp:
361            resp_dict['status_code'] = 200
362            resp_dict['status_message'] = "Actions successfully sorted."
363        else:
364            resp_dict['status_code'] = 500
365            resp_dict['status_message'] = "There was error while sorting, or list not changed"
366        return resp_dict
367
368    def manage_ajax_changeCategory(self, form, errs):
369        resp_dict = {}
370        errors = []
371
372        """Change action category to manage"""
373        cat_name = form['category']
374        # update actions list
375        resp_dict['actionslist'] = self.getActionsList(category=cat_name)
376        # update autogenerated sections
377        resp_dict['section'] = self.getAutoGenereatedSection(cat_name)
378        # and title
379        ts = getToolByName(self.context, 'translation_service')
380        resp_dict['title'] = ts.translate(self.getPageTitle(cat_name), context=self.context)
381   
382        if not errors:
383            resp_dict['status_code'] = 200
384            resp_dict['status_message'] = "Category changed successfully"
385        else:
386            resp_dict['status_code'] = 500
387            resp_dict['status_message'] = "There was error while changed category"
388
389        return resp_dict
390
391
392    ########################################
[53]393    # Methods for processing configlet posts
394    ########################################
[3244]395
[53]396    def manage_setAutogeneration(self, form, errors):
[747]397        """Process managing autogeneration settings"""
[3244]398
[53]399        # set excludeFromNav property for root objects
[3672]400        portal = self.plone_portal_state.portal()
[53]401        generated_tabs = form.get("generated_tabs", '0')
402        nonfolderish_tabs = form.get("nonfolderish_tabs", '0')
[3244]403
[53]404        for item in self.getRootTabs():
405            obj = getattr(portal, item['id'], None)
406            if obj is not None:
407                checked = form.get(item['id'], None)
408                if checked == '1':
409                    obj.update(excludeFromNav=False)
410                else:
411                    obj.update(excludeFromNav=True)
412
413        # set disable_folder_sections property
414        if int(generated_tabs) == 1:
[146]415            self.setSiteProperties(disable_folder_sections=False)
[53]416        else:
[146]417            self.setSiteProperties(disable_folder_sections=True)
[3244]418
[53]419        # set disable_nonfolderish_sections property
420        if int(nonfolderish_tabs) == 1:
[146]421            self.setSiteProperties(disable_nonfolderish_sections=False)
[53]422        else:
[146]423            self.setSiteProperties(disable_nonfolderish_sections=True)
[3244]424
[146]425        # after successfull form processing make redirect with good message
[747]426        IStatusMessage(self.request).addStatusMessage(_(u"Changes saved!"),
427                                                      type="info")
[53]428        self.redirect()
429        return False
[3244]430
[135]431    def manage_addAction(self, form, errs):
[747]432        """Manage method to add a new action to given category,
433        if category doesn't exist, create it
434        """
[139]435        # extract posted data
436        id, cat_name, data = self.parseAddForm(form)
[3244]437
[138]438        # validate posted data
[139]439        errors = self.validateActionFields(cat_name, data)
[3244]440
[53]441        # if not errors find (or create) category and set action to it
442        if not errors:
[151]443            action = self.addAction(cat_name, data)
[747]444            IStatusMessage(self.request).addStatusMessage(
[873]445                _(u"'${id}' action successfully added.",
[3437]446                  mapping={'id': action.id}), type="info")
[135]447            self.redirect(search="category=%s" % cat_name)
[53]448            return False
449        else:
[135]450            errs.update(errors)
[747]451            IStatusMessage(self.request).addStatusMessage(
452                _(u"Please correct the indicated errors."), type="error")
[53]453            return True
[3244]454
[135]455    def manage_editAction(self, form, errs):
[747]456        """Manage Method to update action"""
[138]457        # extract posted data
[139]458        id, cat_name, data = self.parseEditForm(form)
[3244]459
[138]460        # get category and action to edit
461        category = self.getActionCategory(cat_name)
462        action = category[id]
[3244]463
[138]464        # validate posted data
[3598]465        errors = self.validateActionFields(
466            cat_name, data, allow_dup=(id == data['id']))
[3244]467
[138]468        if not errors:
469            action = self.updateAction(id, cat_name, data)
[747]470            IStatusMessage(self.request).addStatusMessage(
[873]471                _(u"'${id}' action saved.", mapping={'id': action.id}),
472                type="info")
[138]473            self.redirect(search="category=%s" % cat_name)
474            return False
[55]475        else:
[3598]476            errs.update(self.processErrors(
477                errors, sufix='_%s' % id))  # add edit form sufix to error ids
478            IStatusMessage(self.request).addStatusMessage(_(
479                u"Please correct the indicated errors."), type="error")
[138]480            return True
[3244]481
[138]482    def manage_deleteAction(self, form, errs):
[747]483        """Manage Method to delete action"""
[138]484        # extract posted data
[139]485        id, cat_name, data = self.parseEditForm(form)
[3244]486
[138]487        # get category and action to delete
488        category = self.getActionCategory(cat_name)
489        if id in category.objectIds():
490            self.deleteAction(id, cat_name)
[747]491            IStatusMessage(self.request).addStatusMessage(
[873]492                _(u"'${id}' action deleted.", mapping={'id': id}), type="info")
[138]493            self.redirect(search="category=%s" % cat_name)
494            return False
[53]495        else:
[747]496            IStatusMessage(self.request).addStatusMessage(
[873]497                _(u"No '${id}' action in '${cat_name}' category.",
498                  mapping={'id': id, 'cat_name': cat_name}),
[747]499                type="error")
[138]500            return True
[3244]501
[141]502    def manage_moveUpAction(self, form, errs):
[747]503        """Manage Method for moving up given action by one position"""
[141]504        # extract posted data
505        id, cat_name, data = self.parseEditForm(form)
[3244]506
[141]507        # get category and action to move
508        category = self.getActionCategory(cat_name)
509        if id in category.objectIds():
510            self.moveAction(id, cat_name, steps=1)
[747]511            IStatusMessage(self.request).addStatusMessage(
[3608]512                _(u"'${id}' action moved up.", mapping={'id': id}),
513                type="info")
[141]514            self.redirect(search="category=%s" % cat_name)
515            return False
[54]516        else:
[747]517            IStatusMessage(self.request).addStatusMessage(
[873]518                _(u"No '${id}' action in '${cat_name}' category.",
519                  mapping={'id': id, 'cat_name': cat_name}), type="error")
[141]520            return True
[3244]521
[141]522    def manage_moveDownAction(self, form, errs):
[747]523        """Manage Method for moving down given action by one position"""
[141]524        # extract posted data
525        id, cat_name, data = self.parseEditForm(form)
[3244]526
[141]527        # get category and action to move
528        category = self.getActionCategory(cat_name)
529        if id in category.objectIds():
530            self.moveAction(id, cat_name, steps=-1)
[747]531            IStatusMessage(self.request).addStatusMessage(
[873]532                _(u"'${id}' action moved down.", mapping={'id': id}),
533                type="info")
[141]534            self.redirect(search="category=%s" % cat_name)
535            return False
[54]536        else:
[747]537            IStatusMessage(self.request).addStatusMessage(
[873]538                _(u"No '${id}' action in '${cat_name}' category.",
539                  mapping={'id': id, 'cat_name': cat_name}),
[747]540                type="error")
[141]541            return True
[3244]542
[147]543    def redirect(self, url="", search="", url_hash=""):
[747]544        """Redirect to @@plonetabs-controlpanel configlet"""
[798]545        if not url:
[3672]546            portal_url = self.plone_portal_state.portal_url()
[798]547            url = '%s/%s' % (portal_url, "@@plonetabs-controlpanel")
548        if search:
549            search = '?%s' % search
550        if url_hash:
551            url_hash = '#%s' % url_hash
[147]552        self.request.response.redirect("%s%s%s" % (url, search, url_hash))
[3244]553
[53]554    ###################################
[138]555    #
556    #  Methods - providers for templates
557    #
[53]558    ###################################
[3244]559
[873]560    def _charset(self):
[3672]561        pp = self.portal_properties
[873]562        if pp is not None:
563            site_properties = getattr(pp, 'site_properties', None)
564            if site_properties is not None:
565                return site_properties.getProperty('default_charset', 'utf-8')
566        return 'utf-8'
[3244]567
[43]568    def getPageTitle(self, category="portal_tabs"):
[747]569        """See interface"""
[3672]570        portal_props = self.portal_properties
[873]571        default_title = _(u"Plone '${cat_name}' Configuration",
572                          mapping={'cat_name': category})
[3244]573
[43]574        if not hasattr(portal_props, PROPERTY_SHEET):
575            return default_title
[3244]576
[43]577        sheet = getattr(portal_props, PROPERTY_SHEET)
578        if not hasattr(sheet, FIELD_NAME):
579            return default_title
[3244]580
[43]581        field = sheet.getProperty(FIELD_NAME)
582        dict = {}
583        for line in field:
584            cat, title = line.split("|", 2)
585            dict[cat] = title
[3244]586
[873]587        title = dict.get(category, None)
588        if title is None:
589            return default_title
[3244]590
[873]591        charset = self._charset()
592        if not isinstance(title, unicode):
593            title = title.decode(charset)
[3244]594
[873]595        return _(title)
[3244]596
[43]597    def hasActions(self, category="portal_tabs"):
[747]598        """See interface"""
[3672]599        tool = self.portal_actions
[3437]600        return len(tool.listActions(categories=[category, ])) > 0
[3244]601
[43]602    def getPortalActions(self, category="portal_tabs"):
[747]603        """See interface"""
[3672]604        portal_actions = self.portal_actions
[3244]605
[45]606        if category not in portal_actions.objectIds():
607            return []
[3244]608
[45]609        actions = []
610        for item in portal_actions[category].objectValues():
611            if IAction.providedBy(item):
612                actions.append(item)
[3244]613
[45]614        return actions
[3244]615
[43]616    def isGeneratedTabs(self):
[747]617        """See interface"""
[3672]618        site_properties = self.portal_properties.site_properties
[3437]619        return not site_properties.getProperty("disable_folder_sections",
620                                               False)
[3244]621
[43]622    def isNotFoldersGenerated(self):
[747]623        """See interface"""
[3672]624        site_properties = self.portal_properties.site_properties
[747]625        prop = site_properties.getProperty("disable_nonfolderish_sections",
626                                           False)
627        return not prop
[3244]628
[151]629    def getActionsList(self, category="portal_tabs", errors={}, tabs=[]):
[747]630        """See interface"""
[151]631        kw = {'category': category, 'errors': errors}
632        if tabs:
633            kw['tabs'] = tabs
634        return self.actionslist_template(**kw)
[3244]635
[162]636    def getAutoGenereatedSection(self, cat_name, errors={}):
[747]637        """See interface"""
[162]638        return self.autogenerated_template(category=cat_name, errors=errors)
[3244]639
[43]640    def getGeneratedTabs(self):
[747]641        """See interface"""
[162]642        return self.autogenerated_list()
[3244]643
[3673]644    def _add_sort_option(self, query):
645        """ add sort option """
646        navtree_props = getattr(self.portal_properties, 'navtree_properties')
647        sortAttribute = navtree_props.getProperty('sortAttribute', None)
648        if sortAttribute is not None:
649            query['sort_on'] = sortAttribute
650
651            sortOrder = navtree_props.getProperty('sortOrder', None)
652            if sortOrder is not None:
653                query['sort_order'] = sortOrder
654
[43]655    def getRootTabs(self):
[747]656        """See interface"""
[43]657        context = aq_inner(self.context)
[3244]658
[43]659        portal_catalog = getToolByName(context, 'portal_catalog')
[3672]660        portal_properties = self.portal_properties
[43]661        navtree_properties = getattr(portal_properties, 'navtree_properties')
[3244]662
[43]663        # Build result dict
664        result = []
[3244]665
[147]666        # check whether tabs autogeneration is turned on
[43]667        if not self.isGeneratedTabs():
668            return result
[3244]669
[43]670        query = {}
671        rootPath = getNavigationRoot(context)
[3437]672        query['path'] = {'query': rootPath, 'depth': 1}
[43]673        query['portal_type'] = utils.typesToList(context)
[3244]674
[3673]675        self._add_sort_option(query)
[3244]676
[43]677        if navtree_properties.getProperty('enable_wf_state_filtering', False):
[747]678            query['review_state'] = navtree_properties.getProperty(
679                'wf_states_to_show', [])
[3244]680
[43]681        query['is_default_page'] = False
682
683        if not self.isNotFoldersGenerated():
684            query['is_folderish'] = True
685
686        # Get ids not to list and make a dict to make the search fast
687        idsNotToList = navtree_properties.getProperty('idsNotToList', ())
688        excludedIds = {}
689        for id in idsNotToList:
[3437]690            excludedIds[id] = 1
[43]691
692        rawresult = portal_catalog.searchResults(**query)
693
694        # now add the content to results
695        for item in rawresult:
[3598]696            if item.getId not in excludedIds:
[3672]697                result.append(self.getItem(item))
[3244]698
[43]699        return result
[3244]700
[3672]701    def getItem(self, item):
702        """get item"""
703        context = aq_inner(self.context)
704        id_, item_url = get_view_url(item)
705        data = {
706            'name': utils.pretty_title_or_id(context, item),
707            'id': id_,
708            'url': item_url,
709            'description': item.Description,
710            'exclude_from_nav': item.exclude_from_nav,
711        }
712        return data
713
[43]714    def getCategories(self):
[747]715        """See interface"""
[3672]716        portal_actions = self.portal_actions
[43]717        return portal_actions.objectIds()
[3244]718
[138]719    #
720    # Methods to make this class looks like global sections viewlet
721    #
[3244]722
[43]723    def test(self, condition, ifTrue, ifFalse):
[747]724        """See interface"""
[43]725        if condition:
726            return ifTrue
727        else:
728            return ifFalse
[3244]729
[46]730    # methods for rendering global-sections viewlet via kss,
731    # due to bug in macroContent when global-section list is empty,
732    # ul have condition
733    def portal_tabs(self):
[747]734        """See global-sections viewlet"""
[3437]735        actions = getMultiAdapter((self.context, self.request),
[3598]736                                  name=u'plone_context_state').actions()
[3437]737        actions_tabs = []
738        try:
739            # Plone 4 and higher
740            import plone.app.upgrade
741            plone.app.upgrade  # pyflakes
742        except ImportError:
743            actions_tabs = actions
744        if not actions_tabs and 'portal_tabs' in actions:
745            actions_tabs = actions['portal_tabs']
746
[747]747        portal_tabs_view = getMultiAdapter((self.context, self.request),
[3598]748                                           name="portal_tabs_view")
[3244]749        return portal_tabs_view.topLevelTabs(actions=actions_tabs)
750
[46]751    def selected_portal_tab(self):
[747]752        """See global-sections viewlet"""
[3563]753        # BBB: compatibility with older plone versions.
[3598]754        # ``selectedTabs`` Python script was merged into the
[3563]755        # GlobalSectionsViewlet.
756        section_viewlet = setupViewletByName(self,
757                                             self.context,
758                                             self.request,
759                                             'plone.global_sections')
[3564]760        if hasattr(section_viewlet, 'selectedTabs'):
[3563]761            # Plone >= 4.3
762            selected_tabs = section_viewlet.selectedTabs(
[3598]763                default_tab='index_html',
[3563]764                portal_tabs=self.portal_tabs())
765        else:
766            # Plone < 4.3
767            selectedTabs = self.context.restrictedTraverse('selectedTabs')
768            selected_tabs = selectedTabs('index_html', self.context,
[3598]769                                         self.portal_tabs())
[3244]770
[46]771        return selected_tabs['portal']
[3244]772
[138]773    #
[48]774    # Utility Methods
[138]775    #
[3244]776
[507]777    def fixExpression(self, expr):
[798]778        """Fix expression appropriately for tal format"""
[507]779        if expr.find('/') == 0:
780            return 'string:${portal_url}%s' % expr
781        elif re.compile('^(ht|f)tps?\:', re.I).search(expr):
782            return 'string:%s' % expr
[3668]783        # elif re.compile('^(python:|string:|not:|exists:|nocall:|path:)',
784                        # re.I).search(expr):
785            # return expr
[507]786        elif expr.find(':') != -1:
787            return expr
788        else:
789            return 'string:${object_url}/%s' % expr
[3244]790
[48]791    def copyAction(self, action):
[747]792        """Copy action to dictionary"""
[3437]793        action_info = {'description': action.description}
[48]794        for attr in ACTION_ATTRS:
795            action_info[attr] = getattr(action, attr)
796        return action_info
[3244]797
[3673]798    def _validate_expression(self, name, data, errors):
799        """ validate expression """
800        if data[name]:
801            try:
802                Expression(data[name])
803            except Exception, e:
804                mapping = {'expr': data[name]}
805                idx = data[name].find(':')
806                if idx != -1:
807                    mapping['expr_type'] = data[name][:idx]
808                errors[name] = self._formatError(e, **mapping)
809
[138]810    def validateActionFields(self, cat_name, data, allow_dup=False):
[747]811        """Check action fields on validity"""
[135]812        errors = {}
[3244]813
[138]814        if allow_dup:
[747]815            # create dummy category to avoid id
816            # duplication during action update
817            category = ActionCategory(cat_name)
[138]818        else:
[747]819            # get or create (if necessary) actions category
820            category = self.getOrCreateCategory(cat_name)
[3244]821
[135]822        # validate action id
823        chooser = INameChooser(category)
824        try:
825            chooser.checkName(data['id'], self.context)
826        except Exception, e:
[3437]827            errors['id'] = self._formatError(e, **{'id': data['id']})
[3244]828
[135]829        # validate action name
830        if not data['title'].strip():
[873]831            errors['title'] = _(u"Empty or invalid title specified")
[3244]832
[135]833        # validate condition expression
[3673]834        self._validate_expression('available_expr', data, errors)
[3244]835
[135]836        # validate action expression
[3673]837        self._validate_expression('url_expr', data, errors)
[3244]838
[135]839        return errors
[3244]840
[873]841    def _formatError(self, message, **kw):
842        """Make error message a little bit prettier to ease translation"""
843        charset = self._charset()
844        message = str(message)
845        message = message.replace('"', "'")
846        mapping = {}
847        for key, value in kw.items():
848            message = message.replace("'%s'" % value, "'${%s}'" % key)
849            # trying to work around zope.i18n issue
850            mapping[key] = unicode(value, charset)
851        message = message.strip()
852        return _(unicode(message, charset), mapping=mapping)
[3244]853
[138]854    def processErrors(self, errors, prefix='', sufix=''):
[3244]855        """Add prefixes, sufixes to error ids
[747]856        This is necessary during edit form validation,
857        because every edit form on the page has it's own sufix (id)
858        """
[138]859        if not (prefix or sufix):
860            return errors
[3244]861
[138]862        result = {}
863        for key, value in errors.items():
864            result['%s%s%s' % (prefix, key, sufix)] = value
[3244]865
[138]866        return result
[3244]867
[139]868    def parseEditForm(self, form):
[747]869        """Extract all needed fields from edit form"""
[138]870        # get original id and category
871        info = {}
872        id = form['orig_id']
873        category = form['category']
[3244]874
[138]875        # preprocess 'visible' field (checkbox needs special checking)
[3598]876        if 'visible_%s' % id in form:
[138]877            form['visible_%s' % id] = True
878        else:
879            form['visible_%s' % id] = False
[3244]880
[138]881        # collect all action fields
882        for attr in ACTION_ATTRS:
883            info[attr] = form['%s_%s' % (attr, id)]
[3244]884
[138]885        return (id, category, info)
[3244]886
[139]887    def parseAddForm(self, form):
[747]888        """Extract all needed fields from add form"""
[139]889        info = {}
890        id = form['id']
891        category = form['category']
[3244]892
[139]893        # preprocess 'visible' field (checkbox needs special checking)
[3598]894        if 'visible' in form and form['visible']:
[139]895            form['visible'] = True
896        else:
897            form['visible'] = False
[3244]898
[507]899        # fix expression fields
900        form['url_expr'] = self.fixExpression(form['url_expr'])
[3244]901
[139]902        # collect all action fields
903        for attr in ACTION_ATTRS:
904            info[attr] = form[attr]
[3244]905
[139]906        return (id, category, info)
[3244]907
[138]908    def getActionCategory(self, name):
[3672]909        portal_actions = self.portal_actions
[138]910        return portal_actions[name]
[3244]911
[135]912    def getOrCreateCategory(self, name):
[747]913        """Get or create (if necessary) category"""
[3672]914        portal_actions = self.portal_actions
[747]915        if name not in map(lambda x: x.id,
916                           filter(lambda x: IActionCategory.providedBy(x),
917                                  portal_actions.objectValues())):
[135]918            portal_actions._setObject(name, ActionCategory(name))
[138]919        return self.getActionCategory(name)
[3244]920
[147]921    def setSiteProperties(self, **kw):
[747]922        """Change site_properties"""
[3672]923        site_properties = self.portal_properties.site_properties
[147]924        site_properties.manage_changeProperties(**kw)
925        return True
[3244]926
[3681]927    def renderViewlet(self, manager, name):
928        if isinstance(manager, basestring):
929            manager = getMultiAdapter((self.context, self.request, self,),
930                                      IViewletManager, name=manager)
931        renderer = getMultiAdapter((self.context, self.request, self, manager),
932                                   IViewlet, name=name)
933        renderer = renderer.__of__(self.context)
934        renderer.update()
935        return renderer.render()
[147]936    #
937    # Basic API to work with portal actions tool in a more pleasent way
938    #
[3244]939
[135]940    def addAction(self, cat_name, data):
[747]941        """Create and add new action to category with given name"""
[135]942        id = data.pop('id')
943        category = self.getOrCreateCategory(cat_name)
944        action = Action(id, **data)
945        category._setObject(id, action)
946        return action
[3244]947
[138]948    def updateAction(self, id, cat_name, data):
[747]949        """Update action with given id and category"""
[138]950        new_id = data.pop('id')
951        category = self.getActionCategory(cat_name)
[3244]952
[138]953        # rename action if necessary
954        if id != new_id:
955            category.manage_renameObject(id, new_id)
[3244]956
[138]957        # get action
958        action = category[new_id]
[3244]959
[138]960        # update action properties
[150]961        for attr in data.keys():
[3598]962            if attr in data:
[138]963                action._setPropValue(attr, data[attr])
[3244]964
[138]965        return action
[3244]966
[138]967    def deleteAction(self, id, cat_name):
[747]968        """Delete action with given id from given category"""
[138]969        category = self.getActionCategory(cat_name)
[3437]970        category.manage_delObjects(ids=[id, ])
[138]971        return True
[3244]972
[141]973    def moveAction(self, id, cat_name, steps=0):
[747]974        """Move action by a given steps"""
[141]975        if steps != 0:
976            category = self.getActionCategory(cat_name)
977            if steps > 0:
[3437]978                category.moveObjectsUp([id, ], steps)
[141]979            else:
[3437]980                category.moveObjectsDown([id, ], abs(steps))
[141]981            return True
982        return False
[3244]983
984
[872]985class PloneTabsMode(BrowserView):
[3244]986
[872]987    def __call__(self):
988        mode = self.request.get(cookie_name, False)
989        if mode in ('plain', 'rich'):
990            return mode
991        mode = self.request.cookies.get(cookie_name, False)
992        if mode in ('plain', 'rich'):
993            return mode
994        return 'rich'
Note: See TracBrowser for help on using the repository browser.