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

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

reback renderViewlet method; remove test_kssRegistry test

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