from AccessControl import getSecurityManager from Acquisition import Explicit from Acquisition.interfaces import IAcquirer from zope.browserpage.viewpagetemplatefile import ViewPageTemplateFile from zope.interface import implementsOnly, implementer from zope.component import getMultiAdapter from zope.i18n import translate import z3c.form.interfaces import z3c.form.widget import z3c.form.util from zope.component.hooks import getSite from plone.app.layout.navigation.interfaces import INavtreeStrategy from plone.app.layout.navigation.navtree import buildFolderTree from plone.formwidget.autocomplete.widget import \ AutocompleteSelectionWidget, AutocompleteMultiSelectionWidget from Products.CMFCore.utils import getToolByName from Products.Five.browser import BrowserView from plone.formwidget.contenttree.interfaces import IContentTreeWidget from plone.formwidget.contenttree import MessageFactory as _ class Fetch(BrowserView): fragment_template = ViewPageTemplateFile('fragment.pt') recurse_template = ViewPageTemplateFile('input_recurse.pt') def getTermByBrain(self, brain): # Ask the widget return self.context.getTermByBrain(brain) def validate_access(self): content = self.context.form.context # If the object is not wrapped in an acquisition chain # we cannot check any permission. if not IAcquirer.providedBy(content): return url = self.request.getURL() view_name = url[len(content.absolute_url()):].split('/')[1] # May raise Unauthorized # If the view is 'edit', then traversal prefers the view and # restrictedTraverse prefers the edit() method present on most CMF # content. Sigh... if not view_name.startswith('@@') and not view_name.startswith('++'): view_name = '@@' + view_name view_instance = content.restrictedTraverse(view_name) getSecurityManager().validate(content, content, view_name, view_instance) def __call__(self): # We want to check that the user was indeed allowed to access the # form for this widget. We can only this now, since security isn't # applied yet during traversal. self.validate_access() widget = self.context context = widget.context # Update the widget before accessing the source. # The source was only bound without security applied # during traversal before. widget.update() source = widget.bound_source # Convert token from request to the path to the object token = self.request.form.get('href', None) directory = self.context.bound_source.tokenToPath(token) level = self.request.form.get('rel', 0) navtree_query = source.navigation_tree_query.copy() navtree_query['path'] = {'depth': 1, 'query': directory} if 'is_default_page' not in navtree_query: navtree_query['is_default_page'] = False content = context if not IAcquirer.providedBy(content): content = getSite() strategy = getMultiAdapter((content, widget), INavtreeStrategy) catalog = getToolByName(content, 'portal_catalog') children = [] for brain in catalog(navtree_query): newNode = {'item': brain, 'depth': -1, # not needed here 'currentItem': False, 'currentParent': False, 'children': []} if strategy.nodeFilter(newNode): newNode = strategy.decoratorFactory(newNode) children.append(newNode) return self.fragment_template(children=children, level=int(level)) class ContentTreeBase(Explicit): implementsOnly(IContentTreeWidget) # XXX: Due to the way the rendering of the QuerySourceRadioWidget works, # if we call this 'template' or use a directive, # we'll get infinite recursion when trying to render the radio buttons. input_template = ViewPageTemplateFile('input.pt') hidden_template = ViewPageTemplateFile('hidden.pt') display_template = None # set by subclass recurse_template = ViewPageTemplateFile('input_recurse.pt') # Parameters passed to the JavaScript function folderEvent = 'click' selectEvent = 'click' expandSpeed = 200 collapseSpeed = 200 multiFolder = True multi_select = False # Overrides for autocomplete widget formatItem = ('function(row, idx, count, value) {' ' return row[1] + " (" + row[0] + ")"; }') # By default, only show 'interesting' nodes, that is: nodes that # are selectable or that are folders. show_all_nodes = False def getTermByBrain(self, brain): return self.bound_source.getTermByBrain(brain) def render_tree(self): content = self.context if not IAcquirer.providedBy(content): content = getSite() source = self.bound_source strategy = getMultiAdapter((content, self), INavtreeStrategy) data = buildFolderTree(content, obj=content, query=source.navigation_tree_query, strategy=strategy) return self.recurse_template(children=data.get('children', []), level=1) def render(self): if self.mode == z3c.form.interfaces.DISPLAY_MODE: return self.display_template(self) elif self.mode == z3c.form.interfaces.HIDDEN_MODE: return self.hidden_template(self) else: return self.input_template(self) def js_extra(self): # Get bound source to extract path source = self.bound_source form_url = self.request.getURL() url = "%s/++widget++%s/@@contenttree-fetch" % (form_url, self.name) return """\ $('#%(id)s-widgets-query').each(function() { if($(this).siblings('input.searchButton').length > 0) { return; } $(document.createElement('input')) .attr({ 'type': 'button', 'value': '%(button_val)s' }) .addClass('searchButton') .click( function () { var parent = $(this).parents("*[id$='-autocomplete']") var window = parent.siblings("*[id$='-contenttree-window']") window.showDialog('%(url)s', %(expandSpeed)d); $('#' + parent.attr('id').replace('autocomplete', 'contenttree')).contentTree( { script: '%(url)s', folderEvent: '%(folderEvent)s', selectEvent: '%(selectEvent)s', expandSpeed: %(expandSpeed)d, collapseSpeed: %(collapseSpeed)s, multiFolder: %(multiFolder)s, multiSelect: %(multiSelect)s, rootUrl: '%(rootUrl)s' }, function(event, selected, data, title) { // alert(event + ', ' + selected + ', ' + data + ', ' + title); } ); }).insertAfter($(this)); }); $('#%(id)s-contenttree-window').find('.contentTreeAdd').unbind('click').click(function () { $(this).contentTreeAdd(); }); $('#%(id)s-contenttree-window').find('.contentTreeCancel').unbind('click').click(function () { $(this).contentTreeCancel(); }); $('#%(id)s-widgets-query').after(" "); """ % dict(url=url, id=self.name.replace('.', '-'), folderEvent=self.folderEvent, selectEvent=self.selectEvent, expandSpeed=self.expandSpeed, collapseSpeed=self.collapseSpeed, multiFolder=str(self.multiFolder).lower(), multiSelect=str(self.multi_select).lower(), rootUrl=source.navigation_tree_query['path']['query'], name=self.name, klass=self.klass, title=self.title, button_val=translate( _(u'label_contenttree_browse', default=u'browse...'), context=self.request)) class ContentTreeWidget(ContentTreeBase, AutocompleteSelectionWidget): """ContentTree widget that allows single selection. """ klass = u"contenttree-widget" display_template = ViewPageTemplateFile('display_single.pt') class MultiContentTreeWidget(ContentTreeBase, AutocompleteMultiSelectionWidget): """ContentTree widget that allows multiple selection """ klass = u"contenttree-widget" multi_select = True display_template = ViewPageTemplateFile('display_multiple.pt') @implementer(z3c.form.interfaces.IFieldWidget) def ContentTreeFieldWidget(field, request): return z3c.form.widget.FieldWidget(field, ContentTreeWidget(request)) @implementer(z3c.form.interfaces.IFieldWidget) def MultiContentTreeFieldWidget(field, request): return z3c.form.widget.FieldWidget(field, MultiContentTreeWidget(request))