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

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

rewrite kss actions via jquery ajax; remove all related to kss resources

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