import copy from Acquisition import aq_inner from zope.interface import implements from zope.component import getUtility, getMultiAdapter from zope.i18n import translate from zope.schema.interfaces import IVocabularyFactory from Acquisition import aq_inner from Products.CMFCore.utils import getToolByName from Products.CMFCore.interfaces import IAction from Products.CMFEditions.setuphandlers import DEFAULT_POLICIES from Products.CMFPlone import PloneMessageFactory as _ from Products.CMFPlone import PloneMessageFactory as pmf from Products.CMFPlone import utils from Products.CMFPlone.browser.navigation import get_view_url from Products.Five.browser import BrowserView from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from plone.app.layout.navigation.root import getNavigationRoot from plone.app.kss.plonekssview import PloneKSSView from plone.app.workflow.remap import remap_workflow from plone.memoize.instance import memoize from kss.core import kssaction, KSSExplicitError from quintagroup.plonetabs.config import * from interfaces import IPloneTabsControlPanel def format_description(text, request=None): # We expect the workflow to be a text of "- " divided bullet points. text = translate(text.strip(), domain="plone", context=request) return [s.strip() for s in text.split("- ") if s] ACTION_ATTRS = ["id", "visible", "description", "url_expr", "title", "available_expr"] class PloneTabsControlPanel(PloneKSSView): implements(IPloneTabsControlPanel) actionslist_template = ViewPageTemplateFile("templates/actionslist.pt") autogenerated_template = ViewPageTemplateFile("templates/autogenerated.pt") sections_template = ViewPageTemplateFile("templates/sections.pt") def getPageTitle(self, category="portal_tabs"): """ See interface """ portal_props = getToolByName(self.context, "portal_properties") default_title = "Plone '%s' Configuration" % category if not hasattr(portal_props, PROPERTY_SHEET): return default_title sheet = getattr(portal_props, PROPERTY_SHEET) if not hasattr(sheet, FIELD_NAME): return default_title field = sheet.getProperty(FIELD_NAME) dict = {} for line in field: cat, title = line.split("|", 2) dict[cat] = title return dict.get(category, None) or default_title def hasActions(self, category="portal_tabs"): """ See interface """ return len(getToolByName(self.context, "portal_actions").listActions(categories=[category,])) > 0 def getPortalActions(self, category="portal_tabs"): """ See interface """ portal_actions = getToolByName(self.context, "portal_actions") if category not in portal_actions.objectIds(): return [] actions = [] for item in portal_actions[category].objectValues(): if IAction.providedBy(item): actions.append(item) return actions def isGeneratedTabs(self): """ See interface """ site_properties = getToolByName(self.context, "portal_properties").site_properties return not site_properties.getProperty("disable_folder_sections", False) def isNotFoldersGenerated(self): """ See interface """ site_properties = getToolByName(self.context, "portal_properties").site_properties return not site_properties.getProperty("disable_nonfolderish_sections", False) def getActionsList(self, category="portal_tabs"): """ See interface """ return self.actionslist_template(category=category) def getGeneratedTabs(self): """ See interface """ return self.autogenerated_template() def getRootTabs(self): """ See interface """ context = aq_inner(self.context) portal_catalog = getToolByName(context, 'portal_catalog') portal_properties = getToolByName(context, 'portal_properties') navtree_properties = getattr(portal_properties, 'navtree_properties') # Build result dict result = [] # check whether we only want actions if not self.isGeneratedTabs(): return result query = {} rootPath = getNavigationRoot(context) query['path'] = {'query' : rootPath, 'depth' : 1} query['portal_type'] = utils.typesToList(context) sortAttribute = navtree_properties.getProperty('sortAttribute', None) if sortAttribute is not None: query['sort_on'] = sortAttribute sortOrder = navtree_properties.getProperty('sortOrder', None) if sortOrder is not None: query['sort_order'] = sortOrder if navtree_properties.getProperty('enable_wf_state_filtering', False): query['review_state'] = navtree_properties.getProperty('wf_states_to_show', []) query['is_default_page'] = False if not self.isNotFoldersGenerated(): query['is_folderish'] = True # Get ids not to list and make a dict to make the search fast idsNotToList = navtree_properties.getProperty('idsNotToList', ()) excludedIds = {} for id in idsNotToList: excludedIds[id]=1 rawresult = portal_catalog.searchResults(**query) # now add the content to results for item in rawresult: if not excludedIds.has_key(item.getId): id, item_url = get_view_url(item) data = {'name' : utils.pretty_title_or_id(context, item), 'id' : id, 'url' : item_url, 'description': item.Description, 'exclude_from_nav' : item.exclude_from_nav} result.append(data) return result def getCategories(self): """ See interface """ portal_actions = getToolByName(self.context, "portal_actions") return portal_actions.objectIds() def test(self, condition, ifTrue, ifFalse): """ See interface """ if condition: return ifTrue else: return ifFalse # methods for rendering global-sections viewlet via kss, # due to bug in macroContent when global-section list is empty, # ul have condition def portal_tabs(self): """ See global-sections viewlet """ actions = context_state = getMultiAdapter((self.context, self.request), name=u"plone_context_state").actions() portal_tabs_view = getMultiAdapter((self.context, self.request), name="portal_tabs_view") return portal_tabs_view.topLevelTabs(actions=actions) def selected_portal_tab(self): """ See global-sections viewlet """ selectedTabs = self.context.restrictedTraverse('selectedTabs') selected_tabs = selectedTabs('index_html', self.context, self.portal_tabs()) return selected_tabs['portal'] ########################## # kss server actions ########################## def updateGlobalSections(self, ksscore): """ Method for updating global-sections on client """ ksscore.replaceHTML( ksscore.getHtmlIdSelector("portal-globalnav"), self.sections_template(), withKssSetup="False") # XXX TODO #def updateSection(self, ksscore, section): #""" Method for updating global-sections on client """ #replace_id = section #macro = SECTION_MAPPING.get(section, None) #if macro is not None: #ksscore.replaceHTML( #ksscore.getHtmlIdSelector(replace_id), #self.macroContent(macro), ##self.sections_template(), #withKssSetup="False") def validateAction(self, id, category, prefix="tabslist_"): """ If action with given id and category doesn't exist - raise kss exception """ portal_actions = getToolByName(self.context, "portal_actions") # remove prefix, added for making ids on configlet unique ("tabslist_") act_id = id[len("tabslist_"):] if category not in portal_actions.objectIds(): raise KSSExplicitError, "Unexistent root portal actions category %s" % category cat_container = portal_actions[category] if act_id not in map(lambda x: x.id, filter(lambda x: IAction.providedBy(x), cat_container.objectValues())): raise KSSExplicitError, "%s action does not exist in %s category" % (act_id, category) return (cat_container, act_id) @kssaction def toggleGeneratedTabs(self, field, checked='0'): """ Toggle autogenaration setting on configlet """ changeProperties = getToolByName(self.context, "portal_properties").site_properties.manage_changeProperties if checked == '1': changeProperties(**{field : False}) else: changeProperties(**{field : True}) ksscore = self.getCommandSet("core") replace_id = "roottabs" content = self.getGeneratedTabs() ksscore.replaceInnerHTML(ksscore.getHtmlIdSelector(replace_id), content, withKssSetup="True") # update global-sections viewlet self.updateGlobalSections(ksscore) @kssaction def toggleActionsVisibility(self, id, checked='0', category=None): """ Toggle visibility for portal actions """ portal_actions = getToolByName(self.context, "portal_actions") cat_container, act_id = self.validateAction(id, category) if checked == '1': checked = True else: checked = False cat_container[act_id].visible = checked ksscore = self.getCommandSet("core") if checked: ksscore.removeClass(ksscore.getHtmlIdSelector(id), value="invisible") else: ksscore.addClass(ksscore.getHtmlIdSelector(id), value="invisible") # update global-sections viewlet if category == "portal_tabs": self.updateGlobalSections(ksscore) @kssaction def toggleRootsVisibility(self, id, checked='0'): """ Toggle visibility for portal root objects (exclude_from_nav) """ portal = getMultiAdapter((aq_inner(self.context), self.request), name='plone_portal_state').portal() # remove prefix, added for making ids on configlet unique ("roottabs_") obj_id = id[len("roottabs_"):] if obj_id not in portal.objectIds(): raise KSSExplicitError, "Object with %s id doesn't exist in portal root" % obj_id if checked == '1': checked = True else: checked = False portal[obj_id].update(excludeFromNav=not checked) ksscore = self.getCommandSet("core") if checked: ksscore.removeClass(ksscore.getHtmlIdSelector(id), value="invisible") else: ksscore.addClass(ksscore.getHtmlIdSelector(id), value="invisible") # update global-sections viewlet self.updateGlobalSections(ksscore) @kssaction def deleteAction(self, id, category): """ Delete portal action with given id & category """ portal_actions = getToolByName(self.context, "portal_actions") cat_container, act_id = self.validateAction(id, category) cat_container.manage_delObjects(ids=[act_id,]) # update action list on client ksscore = self.getCommandSet("core") ksscore.deleteNode(ksscore.getHtmlIdSelector(id)) # add "noitems" class to Reorder controls to hide it if not filter(lambda x: IAction.providedBy(x), cat_container.objectValues()): ksscore.addClass(ksscore.getHtmlIdSelector("reorder"), value="noitems") # XXX TODO: fade effect during removing, for this kukit js action/command plugin needed # update global-sections viewlet if category == "portal_tabs": self.updateGlobalSections(ksscore) @kssaction def editAction(self, id, category): """ Show edit form for given action """ cat_container, act_id = self.validateAction(id, category) # collect data action_info = self.copyAction(cat_container[act_id]) action_info["editing"] = True ksscore = self.getCommandSet("core") content = self.actionslist_template(tabs=[action_info,]) replace_id = id ksscore.replaceHTML(ksscore.getHtmlIdSelector(replace_id), content) # focus name field ksscore.focus(ksscore.getCssSelector("#%s input[name=name_%s]" % (id, act_id))) @kssaction def editCancel(self, id, category): """ Hide edit form for given action """ cat_container, act_id = self.validateAction(id, category) ksscore = self.getCommandSet("core") content = self.actionslist_template(tabs=[cat_container[act_id],]) replace_id = id ksscore.replaceHTML(ksscore.getHtmlIdSelector(replace_id), content) # Methods for processing configlet actions without javascript/ajax def manage_setAutogeneration(self, generated_tabs="0", nonfolderish_tabs="0"): """ Process managing autogeneration settings """ # set excludeFromNav property for root objects portal = getMultiAdapter((aq_inner(self.context), self.request), name='plone_portal_state').portal() form = self.request.form for item in self.getRootTabs(): obj = getattr(portal, item['id'], None) if obj is not None: checked = form.get(item['id'], None) if checked == '1': obj.update(excludeFromNav=False) else: obj.update(excludeFromNav=True) # set disable_folder_sections property changeProperties = getToolByName(self.context, "portal_properties").site_properties.manage_changeProperties if int(generated_tabs) == 1: changeProperties(disable_folder_sections=False) else: changeProperties(disable_folder_sections=True) # set disable_nonfolderish_sections property if int(nonfolderish_tabs) == 1: changeProperties(disable_nonfolderish_sections=False) else: changeProperties(disable_nonfolderish_sections=True) self.redirect() def redirect(self, url="", search="", hash=""): """ Redirect to @@plonetabs-controlpanel configlet """ if url == "": portal_url = getMultiAdapter((self.context, self.request), name=u"plone_portal_state").portal_url() url = "%s/%s" % (portal_url, "@@plonetabs-controlpanel") if search != "": search = "?%s" % search if hash != "": hash = "#%s" % hash self.request.response.redirect("%s%s%s" % (url, search, hash)) # Utility Methods def copyAction(self, action): """ Copyt action to dictionary """ action_info = {} for attr in ACTION_ATTRS: action_info[attr] = getattr(action, attr) return action_info