def getTopics(self): """Returns list of major topics and subtopics; used in helpcenter_topicview""" # major topics are those defined in the HelpCenter object. # returns list in the form: # [{title, url, subtopics}, ...] # subtopics are [{title, url}, ...] context = Acquisition.aq_inner(self.context) here_url = context.absolute_url() # get a set of the major topics try: majorTopics = context.getSectionsVocab() except KeyError: return [] liveSections = set(self.catalog.uniqueValuesFor('getSections')) sections = [] currTitle = '' for live in majorTopics: if live in liveSections: # break into main topic, sub topic cs = [s.strip() for s in live.split(':')] main = cs[0] sub = ': '.join(cs[1:]) # sub = cs[-1] if main != currTitle: # append a new topic dict currTitle = main currSubSections = [] sections.append({ 'title': currTitle, 'subtopics': currSubSections, 'url': here_url + '/topic/' + url_quote_plus(currTitle), }) if sub: # add to the subtopics list id = sub.lower().replace(' ', '-') # make HTML anchor ID currSubSections.append({ 'title': sub, 'url': "%s/topic/%s#%s" % (here_url, url_quote_plus(currTitle), id) }) #sections.sort(indexCmp) return sections
def __call__(self, subject, body_text, text_format="plain", username=None, password=None): """This method is lifted almost directly from CMFPlone's discussion_reply.cpy skin script. Modifications start at the point where we try to adapt to IWeblogEntry. """ req = self.request if username or password: # The user username/password inputs on on the comment form were used, # which might happen when anonymous commenting is enabled. If they typed # something in to either of the inputs, we send them to 'logged_in'. # 'logged_in' will redirect them back to this script if authentication # succeeds with a query string which will post the message appropriately # and show them the result. if 'logged_in' fails, the user will be # presented with the stock login failure page. This all depends # heavily on cookiecrumbler, but I believe that is a Plone requirement. came_from = "%s?subject=%s&body_text=%s" % (req["URL"], subject, body_text) came_from = url_quote_plus(came_from) portal_url = self.context.portal_url() return req.RESPONSE.redirect( "%s/logged_in?__ac_name=%s" "&__ac_password=%s" "&came_from=%s" % (portal_url, url_quote_plus(username), url_quote_plus(password), came_from) ) # if (the user is already logged in) or (if anonymous commenting is enabled and # they posted without typing a username or password into the form), we do # the following mtool = getToolByName(self.context, "portal_membership") creator = mtool.getAuthenticatedMember().getId() dtool = getToolByName(self.context, "portal_discussion") tb = dtool.getDiscussionFor(self.context) id = tb.createReply(title=subject, text=body_text, Creator=creator) reply = tb.getReply(id) # TODO THIS NEEDS TO GO AWAY! if hasattr(dtool.aq_explicit, "cookReply"): dtool.cookReply(reply, text_format="plain") parent = tb.aq_parent # return to the discussable object. obj = self.context.plone_utils.getDiscussionThread(tb)[0] try: entry = IWeblogEntry(obj).__of__(self.context.aq_inner.aq_parent) # Check for the existence of a parent weblog to see if `obj' should # be treated as having an archive url. if IWeblog.providedBy(entry.getWeblog()): weview = getMultiAdapter((obj, self.request), name=u"weblogentry_view") base = weview.getArchiveURLFor(entry) except TypeError: base = obj.getTypeInfo().getActionInfo("object/view", obj)["url"] anchor = reply.getId() from Products.CMFPlone.utils import transaction_note transaction_note("Added comment to %s at %s" % (parent.title_or_id(), reply.absolute_url())) self.context.plone_utils.addPortalMessage(_(u"Comment added.")) target = "%s#%s" % (base, anchor) return req.RESPONSE.redirect(target)
def __call__(self): context = aq_inner(self.context) q = self.request.form.get('term', None) self.limit = int(self.request.form.get('limit', 10)) path = self.request.form.get('path', None) plone_utils = getToolByName(context, 'plone_utils') self.pretty_title_or_id = plone_utils.pretty_title_or_id self.normalizeString = plone_utils.normalizeString plone_view = getMultiAdapter((context, self.request), name='plone') self.get_icon = plone_view.getIcon pprops = getToolByName(context, 'portal_properties') sprops = getattr(pprops, 'site_properties', None) self.useViewAction = [] if sprops is not None: self.useViewAction = sprops.getProperty('typesUseViewActionInListings', []) registry = getUtility(IRegistry) self.settings = registry.forInterface(ILiveSearchSettings) q = prepare_SearchableText(q) r = q + '*' self.searchterms = url_quote_plus(r) site_encoding = plone_utils.getSiteEncoding() if path is None: path = getNavigationRoot(context) catalog = getToolByName(context, 'portal_catalog') friendly_types = plone_utils.getUserFriendlyTypes() self.facet_params = context.restrictedTraverse( '@@search-facets/facet_parameters')() self.searchterm_query = '?searchterm=%s' % url_quote_plus(q) if self.settings.grouping: results = catalog(SearchableText=r, portal_type=friendly_types, request_handler='livesearch', path=path, sort_limit=self.settings.group_search_limit)[:self.settings.group_search_limit] else: results = catalog(SearchableText=r, portal_type=friendly_types, request_handler='livesearch', path=path, sort_limit=self.limit)[:self.limit] self.request.response.setHeader( 'Content-Type', 'application/json;charset=%s' % site_encoding) return self.get_results_as_json(results)
def getTopics(self): """Returns list of major topics and subtopics; used in helpcenter_topicview""" # major topics are those defined in the HelpCenter object. # returns list in the form: # [{title, url, subtopics}, ...] # subtopics are [{title, url}, ...] context = Acquisition.aq_inner(self.context) here_url = context.absolute_url() # get a set of the major topics try: majorTopics = context.getSectionsVocab() except KeyError: return [] liveSections = set( self.catalog.uniqueValuesFor('getSections') ) sections = [] currTitle = '' for live in majorTopics: if live in liveSections: # break into main topic, sub topic cs = [s.strip() for s in live.split(':')] main = cs[0] sub = ': '.join(cs[1:]) # sub = cs[-1] if main != currTitle: # append a new topic dict currTitle = main currSubSections = [] sections.append( {'title':currTitle, 'subtopics':currSubSections, 'url': here_url + '/topic/' + url_quote_plus(currTitle), } ) if sub: # add to the subtopics list id = sub.lower().replace(' ','-') # make HTML anchor ID currSubSections.append( {'title':sub, 'url': "%s/topic/%s#%s" % (here_url, url_quote_plus(currTitle), id) } ) #sections.sort(indexCmp) return sections
def render_results(self, resp): ts = getToolByName(self.context, 'translation_service') portal_url = getToolByName(self.context, 'portal_url')() portalProperties = getToolByName(self.context, 'portal_properties') siteProperties = getattr(portalProperties, 'site_properties', None) useViewAction = [] if siteProperties is not None: useViewAction = siteProperties.getProperty( 'typesUseViewActionInListings', []) results = OGSolrContentListing(resp) self.result_items = [] self.show_more = {} self.legend = ts.translate(legend_livesearch, context=self.request) self.nothing_found = ts.translate(label_no_results_found, context=self.request) self.advanced_search_url = portal_url + '/advanced_search?SearchableText=%s' % url_quote_plus( self.search_term) self.advanced_search_label = ts.translate(label_advanced_search, context=self.request) for result in results: item_url = result.getURL() if result.portal_type in useViewAction: item_url += '/view' item_url = item_url + u'?SearchableText=%s' % url_quote_plus( self.search_term) title = safe_unicode(result.Title()) css_klass = get_mimetype_icon_klass(result.doc) description = safe_unicode(result.Description()) or u'' self.result_items.append({ 'url': item_url, 'title': title, 'css_klass': css_klass, 'description': description }) if results.actual_result_count > self.limit: # add a more... row searchquery = u'@@search?SearchableText=%s&path=%s' % ( url_quote_plus(self.search_term), self.path) title = ts.translate(label_show_all, context=self.request) self.show_more = {'url': searchquery, 'title': title} return self.template()
def ShowBlogItemCategories(zope): ''' <span tal:repeat="category python:here.getCategories('dict')" tal:omit-tag=""> <a tal:condition="python:category['id'] in here.getItemCategories()" tal:define="url_quote_plus python:modules['Products'].PythonScripts.standard.url_quote_plus; oc python:url_quote_plus(category.get('id'))" tal:attributes="href string:${here/getRootURL}/oc-$oc"><img tal:replace="structure python:category['image'].tag(title='%s: Click to view blogs items of this category only'%category.get('id'), border='0', style='float:right')" /></a> </span> ''' html = [] m = "Click to view items of this category only" thesecategories = zope.getItemCategories() root = zope.getRoot() tmpl = '<a href="%(href)s" title="%(title)s"><img border="0" '\ 'src="%(src)s" style="float:right" alt="%(alt)s"/></a>' for category in thesecategories: oc = url_quote_plus(category) u = zope.getRootURL() + '/oc-%s' % oc category_filename = category.lower().replace('.','').replace(' ','') + '.gif' img_src = root.getMiscAlias('/misc_/Peterbecom/categoryimages/%s' % category_filename) link = tmpl % dict(src=img_src, alt=category, href=u, title="%s (%s)" % (m, category)) html.append(link) #print category return ''.join(html)
def ShowBlogItemCategories(zope): ''' <span tal:repeat="category python:here.getCategories('dict')" tal:omit-tag=""> <a tal:condition="python:category['id'] in here.getItemCategories()" tal:define="url_quote_plus python:modules['Products'].PythonScripts.standard.url_quote_plus; oc python:url_quote_plus(category.get('id'))" tal:attributes="href string:${here/getRootURL}/oc-$oc"><img tal:replace="structure python:category['image'].tag(title='%s: Click to view blogs items of this category only'%category.get('id'), border='0', style='float:right')" /></a> </span> ''' html = [] m = "Click to view items of this category only" thesecategories = zope.getItemCategories() root = zope.getRoot() tmpl = '<a href="%(href)s" title="%(title)s"><img border="0" '\ 'src="%(src)s" style="float:right" alt="%(alt)s"/></a>' for category in thesecategories: oc = url_quote_plus(category) u = zope.getRootURL() + '/oc-%s' % oc category_filename = category.lower().replace('.', '').replace( ' ', '') + '.gif' img_src = root.getMiscAlias('/misc_/Peterbecom/categoryimages/%s' % category_filename) link = tmpl % dict( src=img_src, alt=category, href=u, title="%s (%s)" % (m, category)) html.append(link) #print category return ''.join(html)
def getStartHeres(self, startHereLimit=10): """ returns a list of topic dicts [{title:topicTitle, startheres:listOfStartHeres, url:urlOfTopic, count:itemsInTopic}, ...] startheres are dicts {title:titleOfStartHere,url:urlOfStartHere} This is used in helpcenter_ploneorg3.pt. """ context = Acquisition.aq_inner(self.context) here_url = context.absolute_url() phc = context.getPHCObject() topics = phc.getSectionsVocab() sections = [] for topic in topics: if ':' not in topic: items = self.catalog(portal_type=['HelpCenterReferenceManual', 'HelpCenterTutorial', 'HelpCenterHowTo'], review_state='published', getSections=[topic]) startHeres = [] for item in items: if item.getStartHere: startHeres.append({'title': item.Title, 'url': item.getURL()}) sections.append({ 'title': topic, 'startheres': startHeres[:startHereLimit], 'url': here_url + '/topic/' + url_quote_plus(topic), 'count': len(items), }) return sections
def __call__(self): parent = self.context.aq_parent try: IRenameProtected(parent) raise Unauthorized() except TypeError: if HAS_AT: # This will be true if AT and pw-protected enabled if IBaseFolder.providedBy(parent) and\ IATRenameProtected.providedBy(parent): raise Unauthorized() title = safe_unicode(self.context.title_or_id()) mtool = getToolByName(self.context, 'portal_membership') if not mtool.checkPermission('Copy or Move', self.context): raise Unauthorized, _(u'Permission denied to rename ${title}.', mapping={u'title': title}) pathName = url_quote_plus('paths:list') safePath = '/'.join(self.context.getPhysicalPath()) orig_template = self.request['HTTP_REFERER'].split('?')[0] url = '%s/folder_rename_form?orig_template=%s&%s=%s' % (self.context.absolute_url(), orig_template, pathName, safePath) self.request.response.redirect(url)
def searchterms(self, quote=True): q = self.request.get('q', '') for char in ('?', '-', '+', '*'): q = q.replace(char, ' ') q = quote_chars(q) if quote: return url_quote_plus(q) return q
def get_show_more_link(self): params = self.facet_params params += '&SearchableText=' + self.searchterms path = self.request.form.get('path', None) if path: params += '&path=' + url_quote_plus(path) return '<a href="@@search?%s" style="font-weight:normal">%s</a>' % ( params, translate(label_show_all, context=self.request), )
def render_results(self, resp): ts = getToolByName(self.context, 'translation_service') portal_url = getToolByName(self.context, 'portal_url')() portalProperties = getToolByName(self.context, 'portal_properties') siteProperties = getattr(portalProperties, 'site_properties', None) useViewAction = [] if siteProperties is not None: useViewAction = siteProperties.getProperty('typesUseViewActionInListings', []) results = OGSolrContentListing(resp) self.result_items = [] self.show_more = {} self.legend = ts.translate(legend_livesearch, context=self.request) self.nothing_found = ts.translate(label_no_results_found, context=self.request) self.advanced_search_url = portal_url + '/advanced_search?SearchableText=%s' % url_quote_plus(self.search_term) self.advanced_search_label = ts.translate(label_advanced_search, context=self.request) for result in results: item_url = result.getURL() if result.portal_type in useViewAction: item_url += '/view' item_url = item_url + u'?SearchableText=%s' % url_quote_plus(self.search_term) title = safe_unicode(result.Title()) css_klass = get_mimetype_icon_klass(result.doc) description = safe_unicode(result.Description()) or u'' self.result_items.append({'url': item_url, 'title': title, 'css_klass': css_klass, 'description': description}) if results.actual_result_count > self.limit: # add a more... row searchquery = u'@@search?SearchableText=%s&path=%s' % (url_quote_plus(self.search_term), self.path) title = ts.translate(label_show_all, context=self.request) self.show_more = {'url': searchquery, 'title': title} return self.template()
def _signature_data(self, fieldname='file'): context = aq_inner(self.context) request = self.request properties_tool = getToolByName(context, 'portal_properties') hmac_properties = getattr(properties_tool, 'red5_protectedvod_properties', None) red5_server_url = hmac_properties.getProperty('red5_server_url') red5_server_url = red5_server_url.rstrip('/') secret_phrase = hmac_properties.getProperty('secret') check_ip = hmac_properties.getProperty('check_ip', False) try: ttl = int(hmac_properties.getProperty('ttl')) except ValueError: ttl = 60 clientip = "" if check_ip: clientip = request.get('HTTP_X_FORWARDED_FOR', None) if not clientip: clientip = request.get('REMOTE_ADDR', None) expires = "%08x" % (DateTime().timeTime() + ttl) (path, filename) = self._storage_info(fieldname) sign_path = "/%s/" % (path, ) sign_data = [sign_path, filename, clientip, expires] signature = hmac_hexdigest(secret_phrase, sign_data) data = { "server_url": red5_server_url, "sign_path": sign_path, "path": path, "filename": filename, "expires": expires, "clientip": clientip, "signature": url_quote_plus(signature) } logger.debug(data) return data
def get_show_more_item(self): label_show_all = _('label_show_all', default='Show all items') params = self.facet_params params += '&SearchableText=' + self.searchterms path = self.request.form.get('path', None) if path: params += '&path=' + url_quote_plus(path) url = '@@search?{0}'.format(params) return { 'url': url, 'title': translate(label_show_all, context=self.request), 'first_of_group': False, 'cssclass': 'no-result' }
def render_javascript(self, field, key, value, REQUEST, render_prefix=None): here = REQUEST['here'] page_template = getattr(here,field.get_value("javascript_ZPT")) return "<!-- Generated by render_javascript -->\n%s" % page_template( getResourceByReference_ZPT = field.get_value('getResourceByReference_ZPT'), createOrder_script = field.get_value('createOrder_script'), portal_types = "portal_type:list=" + "&portal_type:list=".join([url_quote_plus(x[0]) for x in field.get_value('portal_types')]), barcodeStartString = field.get_value('barcodeStartString'), barcodeStopString = field.get_value('barcodeStopString'), fastResourceEntry_display = field.get_value("display_fastResourceEntry"), portal_type_fastResourceEntry = field.get_value('portal_type_fastResourceEntry'), resource_category_fastResourceEntry = field.get_value('resource_category_fastResourceEntry') )
def getStartHeres(self, startHereLimit=10): """ returns a list of topic dicts [{title:topicTitle, startheres:listOfStartHeres, url:urlOfTopic, count:itemsInTopic}, ...] startheres are dicts {title:titleOfStartHere,url:urlOfStartHere} This is used in helpcenter_ploneorg3.pt. """ context = Acquisition.aq_inner(self.context) here_url = context.absolute_url() phc = context.getPHCObject() topics = phc.getSectionsVocab() sections = [] for topic in topics: if ':' not in topic: items = self.catalog(portal_type=[ 'HelpCenterReferenceManual', 'HelpCenterTutorial', 'HelpCenterHowTo' ], review_state=('published', 'internally_published'), getSections=[topic]) startHeres = [] for item in items: if getattr(item, 'getStartHere', False): startHeres.append({ 'title': item.Title, 'url': item.getURL() }) sections.append({ 'title': topic, 'startheres': startHeres[:startHereLimit], 'url': here_url + '/topic/' + url_quote_plus(topic), 'count': len(items), }) return sections
def getData(self): """ Return sorted KeywordList for businessArea """ extractor = getUtility(IKeywordExtractor) path = self.getCurrentPath() areas = extractor.subjects(self.context, path, query_index='BusinessArea', type='Institution', sort_on='sortable_title') for item in areas: item['link'] = url_quote_plus(item['BusinessArea']) current = [] for brain in item['brains']: current.append(brain) if not current: del areas[areas.index(item)] else: current.sort(lambda x, y : cmp(x['Title'].strip().lower(),y['Title'].strip().lower())) item['brains'] = current return areas
def getStartHeres(self, startHereLimit=10): """ returns a list of topic dicts [{title:topicTitle, startheres:listOfStartHeres, url:urlOfTopic, count:itemsInTopic}, ...] startheres are dicts {title:titleOfStartHere,url:urlOfStartHere} This is used in helpcenter_ploneorg3.pt. """ context = Acquisition.aq_inner(self.context) here_url = context.absolute_url() phc = context.getPHCObject() topics = phc.getSectionsVocab() sections = [] for topic in topics: if ":" not in topic: items = self.catalog( portal_type=["HelpCenterReferenceManual", "HelpCenterTutorial", "HelpCenterHowTo"], review_state=("published", "internally_published"), getSections=[topic], ) startHeres = [] for item in items: if getattr(item, "getStartHere", False): startHeres.append({"title": item.Title, "url": item.getURL()}) sections.append( { "title": topic, "startheres": startHeres[:startHereLimit], "url": here_url + "/topic/" + url_quote_plus(topic), "count": len(items), } ) return sections
def render(self): # We set the parameters sent in livesearch using the old way. q = self.request['q'] limit = 10 path = None ploneUtils = getToolByName(self.context, 'plone_utils') portal_url = getToolByName(self.context, 'portal_url')() pretty_title_or_id = ploneUtils.pretty_title_or_id portalProperties = getToolByName(self.context, 'portal_properties') siteProperties = getattr(portalProperties, 'site_properties', None) useViewAction = [] if siteProperties is not None: useViewAction = siteProperties.getProperty('typesUseViewActionInListings', []) # SIMPLE CONFIGURATION MAX_TITLE = 40 MAX_DESCRIPTION = 80 # generate a result set for the query catalog = self.context.portal_catalog friendly_types = ploneUtils.getUserFriendlyTypes() def quotestring(s): return '"%s"' % s def quote_bad_chars(s): bad_chars = ["(", ")"] for char in bad_chars: s = s.replace(char, quotestring(char)) return s multispace = u'\u3000'.encode('utf-8') for char in ('?', '-', '+', '*', multispace): q = q.replace(char, ' ') r = q.split() r = " AND ".join(r) r = quote_bad_chars(r) + '*' searchterms = url_quote_plus(r) params = {'SearchableText': r, 'portal_type': friendly_types, 'sort_limit': limit + 1} if path is None: # useful for subsides params['path'] = getNavigationRoot(self.context) else: params['path'] = path # search limit+1 results to know if limit is exceeded results = catalog(**params) searchterm_query = '?searchterm=%s' % url_quote_plus(q) REQUEST = self.context.REQUEST RESPONSE = REQUEST.RESPONSE RESPONSE.setHeader('Content-Type', 'application/json') label_no_results_found = _('label_no_results_found', default='No matching results found.') label_advanced_search = _('label_advanced_search', default='Advanced Search…') label_show_all = _('label_show_all', default='Show all items') ts = getToolByName(self.context, 'translation_service') queryElements = [] if results: # TODO: We have to build a JSON with the desired parameters. for result in results[:limit]: # Calculate icon replacing '.' per '-' as '.' in portal_types break CSS icon = result.portal_type.lower().replace(".", "-") itemUrl = result.getURL() if result.portal_type in useViewAction: itemUrl += '/view' itemUrl = itemUrl + searchterm_query full_title = safe_unicode(pretty_title_or_id(result)) if len(full_title) > MAX_TITLE: display_title = ''.join((full_title[:MAX_TITLE], '...')) else: display_title = full_title full_title = full_title.replace('"', '"') display_description = safe_unicode(result.Description) if len(display_description) > MAX_DESCRIPTION: display_description = ''.join( (display_description[:MAX_DESCRIPTION], '...')) # We build the dictionary element with the desired parameters and we add it to the queryElements array. queryElement = {'title': display_title, 'description': display_description, 'itemUrl': itemUrl, 'icon': icon} queryElements.append(queryElement) if len(results) > limit: #We have to add here an element to the JSON in case there is too many elements. searchquery = '/@@search?SearchableText=%s&path=%s' \ % (searchterms, params['path']) too_many_results = {'title': ts.translate(label_show_all, context=REQUEST), 'description': '', 'itemUrl': portal_url + searchquery, 'icon': ''} queryElements.append(too_many_results) else: # No results no_results = {'title': ts.translate(label_no_results_found, context=REQUEST), 'description': '', 'itemUrl': portal_url + '/@@search', 'icon': ''} queryElements.append(no_results) advancedSearch = {'title': ts.translate(label_advanced_search, context=REQUEST), 'description': '', 'itemUrl': portal_url + '/@@search', 'icon': ''} queryElements.append(advancedSearch) return json.dumps(queryElements)
##bind namespace= ##bind script=script ##bind subpath=traverse_subpath ##parameters= ##title=Show the rename form for an object ## from Products.CMFPlone.utils import safe_unicode from Products.CMFCore.utils import getToolByName from Products.CMFPlone import PloneMessageFactory as _ from AccessControl import Unauthorized from Products.PythonScripts.standard import url_quote_plus REQUEST = context.REQUEST title = safe_unicode(context.title_or_id()) mtool = getToolByName(context, 'portal_membership') if not mtool.checkPermission('Copy or Move', context) or \ not mtool.checkPermission('Delete portal content', context): raise Unauthorized, _(u'Permission denied to rename ${title}.', mapping={u'title': title}) pathName = url_quote_plus('paths:list') safePath = '/'.join(context.getPhysicalPath()) orig_template = REQUEST['HTTP_REFERER'].split('?')[0] url = '%s/folder_rename_form?orig_template=%s&%s=%s' % ( context.absolute_url(), orig_template, pathName, safePath) REQUEST.RESPONSE.redirect(url)
##bind subpath=traverse_subpath ##parameters=qs=None, **kw ##title=Creates a query string based on existing string plus keyword arguments from zExceptions import Forbidden if container.REQUEST.get('PUBLISHED') is script: raise Forbidden('Script may not be published.') from Products.PythonScripts.standard import url_quote_plus L = [] if qs: # break an existing query string into key value pairs entityparts = qs.split('&') for entitypart in entityparts: ampparts = entitypart.split('&') for amppart in ampparts: tmp = amppart.split('=', 1) if len(tmp) > 1: k, v = tmp else: k, v = tmp[0], '' L.append((k, v)) else: for k, v in kw.items(): L.append((k, url_quote_plus(v))) # separate k/v pairs with & (dont blame me, see the RFC) new = '&'.join(['%s=%s' % (k, v) for k, v in L]) return new
def render(self): # We set the parameters sent in livesearch using the old way. q = self.request['q'] limit = 10 path = None ploneUtils = getToolByName(self.context, 'plone_utils') portal_url = getToolByName(self.context, 'portal_url')() pretty_title_or_id = ploneUtils.pretty_title_or_id portalProperties = getToolByName(self.context, 'portal_properties') siteProperties = getattr(portalProperties, 'site_properties', None) useViewAction = [] if siteProperties is not None: useViewAction = siteProperties.getProperty('typesUseViewActionInListings', []) # SIMPLE CONFIGURATION MAX_TITLE = 40 MAX_DESCRIPTION = 80 # generate a result set for the query catalog = self.context.portal_catalog friendly_types = ploneUtils.getUserFriendlyTypes() def quotestring(s): return '"%s"' % s def quote_bad_chars(s): bad_chars = ["(", ")"] for char in bad_chars: s = s.replace(char, quotestring(char)) return s multispace = u'\u3000'.encode('utf-8') for char in ('?', '-', '+', '*', multispace): q = q.replace(char, ' ') r = q.split() r = " AND ".join(r) r = quote_bad_chars(r) + '*' searchterms = url_quote_plus(r) params = {'SearchableText': r, 'portal_type': friendly_types, 'sort_limit': limit + 1} if path is None: # useful for subsides params['path'] = getNavigationRoot(self.context) else: params['path'] = path params["Language"] = pref_lang() # search limit+1 results to know if limit is exceeded results = catalog(**params) REQUEST = self.context.REQUEST RESPONSE = REQUEST.RESPONSE RESPONSE.setHeader('Content-Type', 'application/json') label_show_all = _('label_show_all', default='Show all items') ts = getToolByName(self.context, 'translation_service') queryElements = [] if results: # TODO: We have to build a JSON with the desired parameters. for result in results[:limit]: # Calculate icon replacing '.' per '-' as '.' in portal_types break CSS icon = result.portal_type.lower().replace(".", "-") itemUrl = result.getURL() if result.portal_type in useViewAction: itemUrl += '/view' full_title = safe_unicode(pretty_title_or_id(result)) if len(full_title) > MAX_TITLE: display_title = ''.join((full_title[:MAX_TITLE], '...')) else: display_title = full_title full_title = full_title.replace('"', '"') display_description = safe_unicode(result.Description) if len(display_description) > MAX_DESCRIPTION: display_description = ''.join( (display_description[:MAX_DESCRIPTION], '...')) # We build the dictionary element with the desired parameters and we add it to the queryElements array. queryElement = { 'class': '', 'title': display_title, 'description': display_description, 'itemUrl': itemUrl, 'icon': icon} queryElements.append(queryElement) return json.dumps(queryElements)
topic=REQGET('topic'), classification=REQGET('classification'), importance=REQGET('importance'), version_info=REQGET('version_info'), stealthy=REQGET('stealthy'), comment=REQGET('comment'), text=REQGET('text')) if context.security_related != was_security_related: # We're toggling security_related - we have to do the corresponding # restrict/unrestrict if available in the current state: if context.security_related: seeking_pretty = 'Restrict' else: seeking_pretty = 'Unrestrict' for action, pretty in context.valid_actions_pairs(): if pretty == seeking_pretty: context.do_action(action, ' Triggered by security_related toggle.') changed = changed + ", " + pretty.lower() + 'ed' break whence = context.absolute_url() if changed: msg = url_quote_plus("Changed: " + changed) context.REQUEST.RESPONSE.redirect("%s?portal_status_message=%s" % (whence, msg)) else: context.REQUEST.RESPONSE.redirect(whence)
def __call__(self): context = aq_inner(self.context) q = self.request.form.get('q', None) self.limit = int(self.request.form.get('limit', 10)) path = self.request.form.get('path', None) plone_utils = getToolByName(context, 'plone_utils') self.pretty_title_or_id = plone_utils.pretty_title_or_id self.normalizeString = plone_utils.normalizeString plone_view = getMultiAdapter((context, self.request), name='plone') self.getIcon = plone_view.getIcon pprops = getToolByName(context, 'portal_properties') sprops = getattr(pprops, 'site_properties', None) self.useViewAction = [] if sprops is not None: self.useViewAction = sprops.getProperty('typesUseViewActionInListings', []) registry = getUtility(IRegistry) self.settings = registry.forInterface(ILiveSearchSettings) # XXX really if it contains + * ? or - # it will not be right since the catalog ignores all non-word # characters equally like # so we don't even attept to make that right. # But we strip these and these so that the catalog does # not interpret them as metachars # See http://dev.plone.org/plone/ticket/9422 for an explanation of '\u3000' multispace = u'\u3000'.encode('utf-8') for char in ('?', '-', '+', '*', multispace): q = q.replace(char, ' ') r = quote_bad_chars(q)+'*' self.searchterms = url_quote_plus(r) site_encoding = plone_utils.getSiteEncoding() if path is None: path = getNavigationRoot(context) catalog = getToolByName(context, 'portal_catalog') friendly_types = plone_utils.getUserFriendlyTypes() self.facet_params = context.restrictedTraverse('@@search-facets/facet_parameters')() if self.settings.grouping: results = catalog(SearchableText=r, portal_type=friendly_types, qt='livesearch', path=path, sort_limit=self.settings.group_search_limit) group_by_types = self.settings.group_by + ['other'] grouped_results = {} for type_ in group_by_types: grouped_results[type_] = [] for result in results[:self.settings.group_search_limit]: if result.portal_type in grouped_results: grouped_results[result.portal_type].append(result) else: grouped_results['other'].append(result) else: results = catalog(SearchableText=r, portal_type=friendly_types, qt='livesearch', path=path, sort_limit=self.limit) self.searchterm_query = '?searchterm=%s'%url_quote_plus(q) if not results: self.write('''<fieldset class="livesearchContainer">''') self.write('''<legend id="livesearchLegend">%s</legend>''' % ( translate(legend_livesearch, context=self.request))) self.write('''<div class="LSIEFix">''') self.write('''<div id="LSNothingFound">%s</div>''' % ( translate(label_no_results_found, context=self.request))) self.write('''</div>''') self.write('''</fieldset>''') else: self.write('''<fieldset class="livesearchContainer">''') self.write('''<legend id="livesearchLegend">%s</legend>''' % ( translate(legend_livesearch, context=self.request))) self.write('''<div class="LSIEFix">''') if self.settings.grouping: self.write_grouped_results(grouped_results, group_by_types) else: self.write_results(results) self.write('''</div>''') self.write('''</fieldset>''') self.request.response.setHeader('Content-Type', 'text/xml;charset=%s' % site_encoding) return '\n'.join(self.output).encode(site_encoding)
# result set. # convert queries to zctextindex # XXX really if it contains + * ? or - # it will not be right since the catalog ignores all non-word # characters equally like # so we don't even attept to make that right. # But we strip these and these so that the catalog does # not interpret them as metachars ##q = re.compile(r'[\*\?\-\+]+').sub(' ', q) for char in '?-+*': q = q.replace(char, ' ') r=q.split() r = " AND ".join(r) r = quote_bad_chars(r)+'*' searchterms = url_quote_plus(r) site_encoding = context.plone_utils.getSiteEncoding() root_path = getNavigationRoot(context) root_ob = context.restrictedTraverse(path) path = root_ob.absolute_url() results = catalog(SearchableText=r, portal_type=friendly_types) RESPONSE = context.REQUEST.RESPONSE RESPONSE.setHeader('Content-Type', 'text/xml;charset=%s' % site_encoding) # replace named entities with their numbered counterparts, in the xml the named ones are not correct # ↓ --> ↓
topic=REQGET('topic'), classification=REQGET('classification'), importance=REQGET('importance'), version_info=REQGET('version_info'), comment=REQGET('comment'), text=REQGET('text')) if context.security_related != was_security_related: # We're toggling security_related - we have to do the corresponding # restrict/unrestrict if available in the current state: if context.security_related: seeking_pretty = 'Restrict' else: seeking_pretty = 'Unrestrict' for action, pretty in context.valid_actions_pairs(): if pretty == seeking_pretty: context.do_action(action, ' Triggered by security_related toggle.') changed = changed + ", " + pretty.lower() + 'ed' break whence = context.absolute_url() if changed: msg = url_quote_plus("Changed: " + changed) context.REQUEST.RESPONSE.redirect("%s?portal_status_message=%s" % (whence, msg)) else: context.REQUEST.RESPONSE.redirect(whence)
changes = context.edit(title=title, description=description, abbrev=abbrev, email=email, managers=managers, supporters=supporters, dispatching=dispatching, topics=topics, classifications=classifications, importances=importances, version_info_spiel=version_info_spiel) if not changes: changes = "No configuration changes" else: changes = "Changed: " + changes recatalog = context.REQUEST.get('recatalog', None) if recatalog: if recatalog == 1: context.reinstate_catalog(internal_only=1) changes += ", reinstated catalog, reindexed internally" else: context.reinstate_catalog(internal_only=0) changes += ", reinstated catalog, reindexed internally and site wide" msg = '?portal_status_message=%s.' % url_quote_plus(changes) context.REQUEST.RESPONSE.redirect( "%s/%s%s" % (context.absolute_url(), "collector_edit_form", msg))
# convert queries to zctextindex # XXX really if it contains + * ? or - # it will not be right since the catalog ignores all non-word # characters equally like # so we don't even attept to make that right. # But we strip these and these so that the catalog does # not interpret them as metachars # See http://dev.plone.org/plone/ticket/9422 for an explanation of '\u3000' multispace = u'\u3000'.encode('utf-8') for char in ('?', '-', '+', '*', multispace): q = q.replace(char, ' ') r = q.split() r = " AND ".join(r) r = quote_bad_chars(r) + '*' searchterms = url_quote_plus(r) REQUEST = context.REQUEST params = {'SearchableText': r, 'sort_limit': limit + 1} if 'portal_type' not in REQUEST: params['portal_type'] = friendly_types if path is None: pass # params['path'] = getNavigationRoot(context) else: params['path'] = path # search limit+1 results to know if limit is exceeded #context.plone_log(str(params))
def render(self): # We set the parameters sent in livesearch using the old way. q = self.request["q"] limit = 10 path = None ploneUtils = getToolByName(self.context, "plone_utils") portal_url = getToolByName(self.context, "portal_url")() pretty_title_or_id = ploneUtils.pretty_title_or_id portalProperties = getToolByName(self.context, "portal_properties") siteProperties = getattr(portalProperties, "site_properties", None) useViewAction = [] if siteProperties is not None: useViewAction = siteProperties.getProperty("typesUseViewActionInListings", []) # SIMPLE CONFIGURATION MAX_TITLE = 40 MAX_DESCRIPTION = 80 # generate a result set for the query catalog = self.context.portal_catalog friendly_types = ploneUtils.getUserFriendlyTypes() def quotestring(s): return '"%s"' % s def quote_bad_chars(s): bad_chars = ["(", ")"] for char in bad_chars: s = s.replace(char, quotestring(char)) return s multispace = u"\u3000".encode("utf-8") for char in ("?", "-", "+", "*", multispace): q = q.replace(char, " ") r = q.split() r = " AND ".join(r) r = quote_bad_chars(r) + "*" searchterms = url_quote_plus(r) params = {"SearchableText": r, "portal_type": friendly_types, "sort_limit": limit + 1} if path is None: # useful for subsides params["path"] = get_referer_path(self.context, self.request) else: params["path"] = path params["Language"] = pref_lang() # search limit+1 results to know if limit is exceeded results = catalog(**params) REQUEST = self.context.REQUEST RESPONSE = REQUEST.RESPONSE RESPONSE.setHeader("Content-Type", "application/json") label_show_all = _("label_show_all", default="Show all items") ts = getToolByName(self.context, "translation_service") queryElements = [] if results: # TODO: We have to build a JSON with the desired parameters. for result in results[:limit]: # Calculate icon replacing '.' per '-' as '.' in portal_types break CSS icon = result.portal_type.lower().replace(".", "-") itemUrl = result.getURL() if result.portal_type in useViewAction: itemUrl += "/view" full_title = safe_unicode(pretty_title_or_id(result)) if len(full_title) > MAX_TITLE: display_title = "".join((full_title[:MAX_TITLE], "...")) else: display_title = full_title full_title = full_title.replace('"', """) display_description = safe_unicode(result.Description) if len(display_description) > MAX_DESCRIPTION: display_description = "".join((display_description[:MAX_DESCRIPTION], "...")) # We build the dictionary element with the desired parameters and we add it to the queryElements array. queryElement = { "class": "", "title": display_title, "description": display_description, "itemUrl": itemUrl, "icon": icon, } queryElements.append(queryElement) if len(results) > limit: # We have to add here an element to the JSON in case there is too many elements. searchquery = "/@@search?SearchableText=%s&path=%s" % (searchterms, params["path"]) too_many_results = { "class": "with-separator", "title": ts.translate(label_show_all, context=REQUEST), "description": "", "itemUrl": portal_url + searchquery, "icon": "", } queryElements.append(too_many_results) return json.dumps(queryElements)
def result(self): context = self.context request = self.request response = self.request.response q = request.get('q') limit = int(request.get('limit', 10)) path = request.get('path', None) ploneUtils = getToolByName(context, 'plone_utils') portal_url = getToolByName(context, 'portal_url')() pretty_title_or_id = ploneUtils.pretty_title_or_id portalProperties = getToolByName(context, 'portal_properties') siteProperties = getattr(portalProperties, 'site_properties', None) useViewAction = [] if siteProperties is not None: useViewAction = siteProperties.getProperty('typesUseViewActionInListings', []) # SIMPLE CONFIGURATION MAX_TITLE = 29 MAX_DESCRIPTION = 93 mtool = getToolByName(context, 'portal_membership') friendly_types = ploneUtils.getUserFriendlyTypes() def quotestring(s): return '"%s"' % s def quote_bad_chars(s): bad_chars = ["(", ")"] for char in bad_chars: s = s.replace(char, quotestring(char)) return s def pretty_date(when): result = ('%s %s, %s') % (DateTime(when).strftime('%B'), DateTime(when).strftime('%d'), DateTime(when).strftime('%Y')) return result multispace = u'\u3000'.encode('utf-8') for char in ('?', '-', '+', '*', multispace): q = q.replace(char, ' ') r = q.split() r = " AND ".join(r) r = quote_bad_chars(r) + '*' searchterms = url_quote_plus(r) site_encoding = context.plone_utils.getSiteEncoding() params = {'SearchableText': r, 'portal_type': friendly_types, 'sort_limit': limit + 1} if path is None: # useful for subsides params['path'] = getNavigationRoot(context) else: params['path'] = path # search limit+1 results to know if limit is exceeded #params = get_query(searchable_text, params) #results = catalog(**params) results = query(params) searchterm_query = '?searchterm=%s' % url_quote_plus(q) #request = context.request #response = request.response response.setHeader('Content-Type', 'text/xml;charset=%s' % site_encoding) # replace named entities with their numbered counterparts, in the xml the named ones are not correct # ↓ --> ↓ # … --> … legend_livesearch = _('legend_livesearch', default='LiveSearch ↓') label_no_results_found = _('label_no_results_found', default='No matching results found.') label_advanced_search = _('label_advanced_search', default='Advanced Search…') label_show_all = _('label_show_all', default='Show all items') ts = getToolByName(context, 'translation_service') output = [] def write(s): output.append(safe_unicode(s)) if not results: write('''<fieldset class="livesearchContainer">''') write('''<legend id="livesearchLegend">%s</legend>''' % ts.translate(legend_livesearch, context=request)) write('''<div class="LSIEFix">''') write('''<div id="LSNothingFound">%s</div>''' % ts.translate(label_no_results_found, context=request)) write('''<ul class="ls-foot">''') write('''<li class="LSRow lsrow-adv-search">''') write('<b></b><a href="%s" style="font-weight:normal">%s</a>' % (portal_url + '/@@search', ts.translate(label_advanced_search, context=request))) write('''</li>''') write('''</ul>''') write('''</div>''') write('''</fieldset>''') else: write('''<fieldset class="livesearchContainer">''') write('''<legend id="livesearchLegend">%s</legend>''' % ts.translate(legend_livesearch, context=request)) write('''<div class="LSIEFix">''') write('''<ul class="LSTable">''') for result in results[:limit]: # breadcrumbs obj = result.getObject() breadcrumbs_view = getMultiAdapter((obj, request), name='breadcrumbs_view') breadcrumbs = breadcrumbs_view.breadcrumbs() ls_breadcrumb = '' breadcrumbs_size = len(breadcrumbs) - 1 if breadcrumbs_size > 0: for ls_key in breadcrumbs[:breadcrumbs_size]: ls_breadcrumb += ('''<a href="%s">%s</a> > ''' % (ls_key['absolute_url'], ls_key['Title'])) is_folderish = result.is_folderish if is_folderish: length_size = len(obj) else: length_size = result.getObjSize #icon = plone_view.getIcon(result) img_class = '%s-icon' % ploneUtils.normalizeString(result.portal_type) member = mtool.getMemberById(result.Creator) if member is not None: fullname = member.getProperty('fullname') else: fullname = '' itemUrl = result.getURL() if result.portal_type in useViewAction: itemUrl += '/view' itemUrl = itemUrl + searchterm_query write('''<li class="LSRow">''') #write(icon.html_tag() or '') write('''<div class="%s ls-content-icon"></div>''' % (img_class)) write('''<div class="ls-details">''') full_title = safe_unicode(pretty_title_or_id(result)) if len(full_title) > MAX_TITLE: display_title = ''.join((full_title[:MAX_TITLE], '...')) else: display_title = full_title full_title = full_title.replace('"', '"') #klass = 'contenttype-%s' % ploneUtils.normalizeString(result.portal_type) klass = 'ls-content-title' write('''<a href="%s" title="%s" class="%s">%s</a>''' % (itemUrl, full_title, klass, display_title)) display_description = safe_unicode(result.Description) if len(display_description) > MAX_DESCRIPTION: display_description = ''.join((display_description[:MAX_DESCRIPTION], '...')) # need to quote it, to avoid injection of html containing javascript and other evil stuff display_description = html_quote(display_description) write('''<div class="LSDescr">%s</div>''' % (display_description)) if breadcrumbs_size > 0: write('''<div class="LSBreadcrumb">in %s</div>''' % (ls_breadcrumb[:-3])) else: write('''<div class="LSBreadcrumb">in Home</div>''') write('''<div class="LSMeta">''') display_type = html_quote(safe_unicode(result.Type)) write('''<span class="LSType">%s</span>''' % (display_type)) if result.Type == 'File' or result.Type == 'Image': write('''<span class="LSType"> • %s</span>''' % (length_size)) elif result.Type == 'Folder': write('''<span class="LSType"> • %s item(s)</span>''' % (length_size)) display_creator = html_quote(safe_unicode(fullname)) if len(display_creator) > 0: write(''' • Create by <a href="%s/author/%s" class="LSCreator">%s</a>''' % (portal_url, member.getProperty('id'), display_creator)) display_modified = html_quote(safe_unicode((pretty_date(result.modified)))) write('''<span class="LSModified">on %s</span>''' % (display_modified)) write('''</div>''') write('''</div>''') write('''</li>''') full_title, display_title, display_description, display_type = None, None, None, None write('''</ul><ul class="ls-foot">''') if len(results) > limit: # add a more... row write('''<li class="LSRow lsrow-show-all">''') searchquery = '@@search?SearchableText=%s&path=%s' % (searchterms, params['path']) write('<b></b><a href="%s" style="font-weight:normal">%s</a>' % ( searchquery, ts.translate(label_show_all, context=request))) write('''</li>''') write('''<li class="LSRow lsrow-adv-search">''') write('<b></b><a href="%s" style="font-weight:normal">%s</a>' % (portal_url + '/@@search', ts.translate(label_advanced_search, context=request))) write('''</li>''') write('''</ul>''') write('''</div>''') write('''</fieldset>''') return '\n'.join(output).encode(site_encoding)
def __call__(self): context = aq_inner(self.context) q = self.request.form.get('q', None) self.limit = int(self.request.form.get('limit', 10)) path = self.request.form.get('path', None) plone_utils = getToolByName(context, 'plone_utils') self.pretty_title_or_id = plone_utils.pretty_title_or_id self.normalizeString = plone_utils.normalizeString plone_view = getMultiAdapter((context, self.request), name='plone') self.getIcon = plone_view.getIcon pprops = getToolByName(context, 'portal_properties') sprops = getattr(pprops, 'site_properties', None) self.useViewAction = [] if sprops is not None: self.useViewAction = sprops.getProperty( 'typesUseViewActionInListings', []) registry = getUtility(IRegistry) self.settings = registry.forInterface(ILiveSearchSettings) # XXX really if it contains + * ? or - # it will not be right since the catalog ignores all non-word # characters equally like # so we don't even attept to make that right. # But we strip these and these so that the catalog does # not interpret them as metachars # See http://dev.plone.org/plone/ticket/9422 for an explanation of '\u3000' multispace = u'\u3000'.encode('utf-8') for char in ('?', '-', '+', '*', multispace): q = q.replace(char, ' ') r = quote_bad_chars(q) + '*' self.searchterms = url_quote_plus(r) site_encoding = plone_utils.getSiteEncoding() if path is None: path = getNavigationRoot(context) catalog = getToolByName(context, 'portal_catalog') friendly_types = plone_utils.getUserFriendlyTypes() self.facet_params = context.restrictedTraverse( '@@search-facets/facet_parameters')() if self.settings.grouping: results = catalog(SearchableText=r, portal_type=friendly_types, qt='livesearch', path=path, sort_limit=self.settings.group_search_limit) group_by_types = self.settings.group_by + ['other'] grouped_results = {} for type_ in group_by_types: grouped_results[type_] = [] for result in results[:self.settings.group_search_limit]: if result.portal_type in grouped_results: grouped_results[result.portal_type].append(result) else: grouped_results['other'].append(result) else: results = catalog(SearchableText=r, portal_type=friendly_types, qt='livesearch', path=path, sort_limit=self.limit) self.searchterm_query = '?searchterm=%s' % url_quote_plus(q) if not results: self.write('''<fieldset class="livesearchContainer">''') self.write('''<legend id="livesearchLegend">%s</legend>''' % (translate(legend_livesearch, context=self.request))) self.write('''<div class="LSIEFix">''') self.write( '''<div id="LSNothingFound">%s</div>''' % (translate(label_no_results_found, context=self.request))) self.write('''</div>''') self.write('''</fieldset>''') else: self.write('''<fieldset class="livesearchContainer">''') self.write('''<legend id="livesearchLegend">%s</legend>''' % (translate(legend_livesearch, context=self.request))) self.write('''<div class="LSIEFix">''') if self.settings.grouping: self.write_grouped_results(grouped_results, group_by_types) else: self.write_results(results) self.write('''</div>''') self.write('''</fieldset>''') self.request.response.setHeader('Content-Type', 'text/xml;charset=%s' % site_encoding) return '\n'.join(self.output).encode(site_encoding)
# convert queries to zctextindex # XXX really if it contains + * ? or - # it will not be right since the catalog ignores all non-word # characters equally like # so we don't even attept to make that right. # But we strip these and these so that the catalog does # not interpret them as metachars # See http://dev.plone.org/plone/ticket/9422 for an explanation of '\u3000' multispace = u'\u3000'.encode('utf-8') for char in ('?', '-', '+', '*', multispace): q = q.replace(char, ' ') r = q.split() r = " AND ".join(r) r = quote_bad_chars(r) + '*' searchterms = url_quote_plus(r) params = {'SearchableText': r, 'portal_type': friendly_types, 'sort_limit': limit + 1} if path is None: # useful for subsides params['path'] = getNavigationRoot(context) else: params['path'] = path # search limit+1 results to know if limit is exceeded results = catalog(**params) quoted_term = url_quote_plus(q)
##bind container=container ##bind context=context ##bind namespace= ##bind script=script ##bind subpath=traverse_subpath ##parameters= ##title=Show the rename form for an object from Products.CMFPlone.utils import safe_unicode from Products.CMFCore.utils import getToolByName from AccessControl import Unauthorized from Products.PythonScripts.standard import url_quote_plus REQUEST = context.REQUEST title = safe_unicode(context.title_or_id()) mtool = getToolByName(context, 'portal_membership') if not mtool.checkPermission('Copy or Move', context): raise Unauthorized(_(u'Permission denied to rename ${title}.', mapping={u'title': title})) pathName = url_quote_plus('paths:list') safePath = '/'.join(context.getPhysicalPath()) orig_template = REQUEST['HTTP_REFERER'].split('?')[0] url = '%s/folder_rename_form?orig_template=%s&%s=%s' % (context.absolute_url(), orig_template, pathName, safePath) REQUEST.RESPONSE.redirect(url)
def render_results(self, resp): output = [] def write(s): output.append(safe_unicode(s)) ts = getToolByName(self.context, 'translation_service') portal_url = getToolByName(self.context, 'portal_url')() portalProperties = getToolByName(self.context, 'portal_properties') siteProperties = getattr(portalProperties, 'site_properties', None) useViewAction = [] if siteProperties is not None: useViewAction = siteProperties.getProperty('typesUseViewActionInListings', []) results = OGSolrContentListing(resp) if not results: write('''<fieldset class="livesearchContainer">''') write('''<legend id="livesearchLegend">%s</legend>''' % ts.translate(legend_livesearch, context=self.request)) write('''<div class="LSIEFix">''') write('''<div id="LSNothingFound">%s</div>''' % ts.translate(label_no_results_found, context=self.request)) write('''<div class="LSRow">''') write('<a href="%s" style="font-weight:normal">%s</a>' % (portal_url + '/advanced_search?SearchableText=%s' % url_quote_plus(self.search_term), ts.translate(label_advanced_search, context=self.request))) write('''</div>''') write('''</div>''') write('''</fieldset>''') else: write('''<fieldset class="livesearchContainer">''') write('''<legend id="livesearchLegend">%s</legend>''' % ts.translate(legend_livesearch, context=self.request)) write('''<div class="LSIEFix">''') write('''<ul class="LSTable">''') for result in results: itemUrl = result.getURL() if result.portal_type in useViewAction: itemUrl += '/view' itemUrl = itemUrl + u'?SearchableText=%s' % url_quote_plus(self.search_term) write('''<li class="LSRow">''') full_title = safe_unicode(result.Title()) if len(full_title) > MAX_TITLE: display_title = ''.join((full_title[:MAX_TITLE], '...')) else: display_title = full_title full_title = full_title.replace('"', '"') css_klass = get_mimetype_icon_klass(result.doc) write('''<a href="%s" title="%s" class="%s">%s</a>''' % (itemUrl, full_title, css_klass, display_title)) display_description = safe_unicode(result.Description()) or u'' if len(display_description) > MAX_DESCRIPTION: display_description = ''.join((display_description[:MAX_DESCRIPTION], '...')) # need to quote it, to avoid injection of html containing javascript and other evil stuff display_description = html_quote(display_description) write('''<div class="LSDescr">%s</div>''' % (display_description)) write('''</li>''') full_title, display_title, display_description = None, None, None write('''<li class="LSRow">''') write('<a href="%s" style="font-weight:normal">%s</a>' % (portal_url + '/advanced_search?SearchableText=%s' % url_quote_plus(self.search_term), ts.translate(label_advanced_search, context=self.request))) write('''</li>''') if resp.num_found > self.limit: # add a more... row write('''<li class="LSRow">''') searchquery = u'@@search?SearchableText=%s&path=%s' % (url_quote_plus(self.search_term), self.path) write(u'<a href="%s" style="font-weight:normal">%s</a>' % ( searchquery, ts.translate(label_show_all, context=self.request))) write('''</li>''') write('''</ul>''') write('''</div>''') write('''</fieldset>''') return '\n'.join(output).encode('utf-8')
# result set. # convert queries to zctextindex # XXX really if it contains + * ? or - # it will not be right since the catalog ignores all non-word # characters equally like # so we don't even attept to make that right. # But we strip these and these so that the catalog does # not interpret them as metachars # See http://dev.plone.org/plone/ticket/9422 for an explanation of '\u3000' multispace = u'\u3000'.encode('utf-8') for char in ('?', '-', '+', '*', multispace): q = q.replace(char, ' ') r = q.split() r = " AND ".join(r) searchurlparameter = url_quote_plus(quote_bad_chars(r)) r = quote_bad_chars(r) + '*' searchterms = url_quote_plus(r) site_encoding = context.plone_utils.getSiteEncoding() params = {'SearchableText': r, 'portal_type': friendly_types, 'sort_limit': limit + 1} if path is None: # useful for subsides params['path'] = getNavigationRoot(context) else: params['path'] = path
def getSectionMap(self, portal_types=TOPIC_VIEW_TYPES): """ returns a complex list of section dicts [{title:sectiontitle, subtopics:listOfSubTopics, url:urlOfSection, count:itemsInSection}, ...] subtopics are each lists [{title:titleOfSubSection,url:urlOfSubSection}] This is used in helpcenter_ploneorg.pt. """ context = Acquisition.aq_inner(self.context) here_url = context.absolute_url() phc = context.getPHCObject() topics = phc.getSectionsVocab() # dict to save counts and start-here items topicsDict = defaultdict(lambda: 0) featuredDict = defaultdict(list) items = self.catalog(portal_type=portal_types, review_state='published') for item in items: for section in item.getSections: if not section: continue topicsDict[section] = topicsDict[section] + 1 if item.getStartHere: featuredDict[section].append({ 'title': item.Title, 'description': item.Description, 'url': item.getURL(), }) sections = [] currTitle = '' for topic in topics: count = topicsDict.get(topic) if count: # break into main topic, sub topic cs = [s.strip() for s in topic.split(':')] main = cs[0] sub = ': '.join(cs[1:]) if main != currTitle: # append a new topic dict currTitle = main currSubSections = [] featured = featuredDict.get(topic) sections.append({ 'title': currTitle, 'subtopics': currSubSections, 'url': here_url + '/topic/' + url_quote_plus(currTitle), 'count': count, 'featured': featured, }) if sub: # add to the subtopics list id = sub.lower().replace(' ', '-') # make HTML anchor ID #sections[-1]['count'] += count #no need to globalize on the main level section because it's already done: now a subsection item has the main section automatically selected currSubSections.append({ 'title': sub, 'url': "%s/topic/%s#%s" % (here_url, url_quote_plus(currTitle), id) }) return sections
def quoted_searchable_text(self): return url_quote_plus(self.request.form.get('SearchableText', ''))
## Script (Python) "collector_add_issue.py" ##parameters=title, security_related, submitter_email, topic, importance, classification, description, version_info ##title=Submit a Request from Products.PythonScripts.standard import url_quote_plus REQGET = context.REQUEST.get id, err = context.add_issue(title=title, security_related=security_related, submitter_name=REQGET('submitter_name'), submitter_email=submitter_email, description=description, topic=topic, classification=classification, importance=importance, version_info=version_info, assignees=REQGET('assignees', []), file=REQGET('file'), fileid=REQGET('fileid', ''), filetype=REQGET('filetype', 'file')) dest = "%s/%s" % (context.absolute_url(), id) if err: dest += '?portal_status_message=' + url_quote_plus(err) context.REQUEST.RESPONSE.redirect(dest)
##parameters=qs=None, **kw ##title=Creates a query string based on existing string plus keyword arguments from zExceptions import Forbidden if container.REQUEST.get('PUBLISHED') is script: raise Forbidden('Script may not be published.') from Products.PythonScripts.standard import url_quote_plus L = [] if qs: # break an existing query string into key value pairs entityparts = qs.split('&') for entitypart in entityparts: ampparts = entitypart.split('&') for amppart in ampparts: tmp = amppart.split('=', 1) if len(tmp) > 1: k, v = tmp else: k, v = tmp[0], '' L.append((k, v)) else: for k, v in kw.items(): L.append((k, url_quote_plus(v))) # separate k/v pairs with & (dont blame me, see the RFC) new = '&'.join(['%s=%s' % (k, v) for k, v in L]) return new
managers=managers, supporters=supporters, dispatching=dispatching, state_email=state_email, topics=topics, classifications=classifications, importances=importances, version_info_spiel=version_info_spiel) if not changes: changes = "No configuration changes" else: changes = "Changed: " + changes recatalog = context.REQUEST.get('recatalog', None) if recatalog: if recatalog == 1: context.reinstate_catalog(internal_only=1) changes += ", reinstated catalog, reindexed internally" else: context.reinstate_catalog(internal_only=0) changes += ", reinstated catalog, reindexed internally and site wide" msg = '?portal_status_message=%s.' % url_quote_plus(changes) context.REQUEST.RESPONSE.redirect("%s/%s%s" % (context.absolute_url(), "collector_edit_form", msg))
from Products.PythonScripts.standard import url_quote_plus from Products.qPloneComments.utils import send_email req = context.REQUEST if username or password: # The user username/password inputs on on the comment form were used, # which might happen when anonymous commenting is enabled. If they typed # something in to either of the inputs, we send them to 'logged_in'. # 'logged_in' will redirect them back to this script if authentication # succeeds with a query string which will post the message appropriately # and show them the result. if 'logged_in' fails, the user will be # presented with the stock login failure page. This all depends # heavily on cookiecrumbler, but I believe that is a Plone requirement. came_from = '%s?subject=%s&body_text=%s' % (req['URL'], subject, body_text) came_from = url_quote_plus(came_from) portal_url = context.portal_url() return req.RESPONSE.redirect( '%s/logged_in?__ac_name=%s' '&__ac_password=%s' '&came_from=%s' % (portal_url, url_quote_plus(username), url_quote_plus(password), came_from, ) ) # if (the user is already logged in) or (if anonymous commenting is enabled and # they posted without typing a username or password into the form), we do # the following
# convert queries to zctextindex # XXX really if it contains + * ? or - # it will not be right since the catalog ignores all non-word # characters equally like # so we don't even attept to make that right. # But we strip these and these so that the catalog does # not interpret them as metachars # See http://dev.plone.org/plone/ticket/9422 for an explanation of '\u3000' multispace = u'\u3000'.encode('utf-8') for char in ('?', '-', '+', '*', multispace): q = q.replace(char, ' ') r = q.split() r = " AND ".join(r) r = quote_bad_chars(r) + '*' searchterms = url_quote_plus(r) site_encoding = context.plone_utils.getSiteEncoding() if path is None: path = getNavigationRoot(context) # '*' no final do path indica que a busca esta sendo realizada para selecionar # um paciente para uma consulta. selecting_patient_for_visit = False if path[-1] == '*': path = path[0:-1] selecting_patient_for_visit = True limit = 20 # aumenta o limite de resultados na adição de visita # no Cmed esta busca eh usada apenas para pacientes, e os inativos nao sao mostrados pbrains = catalog(SearchableText=r,
def render_livesearch_box(self, q, path, limit, results): ts = getToolByName(self.context, 'translation_service') portal_url = getToolByName(self.context, 'portal_url')() plone_view = self.context.restrictedTraverse('@@plone') #portal_state = self.context.restrictedTraverse('@@plone_portal_state') portalProperties = getToolByName(self.context, 'portal_properties') siteProperties = getattr(portalProperties, 'site_properties', None) ploneUtils = getToolByName(self.context, 'plone_utils') useViewAction = [] if siteProperties is not None: useViewAction = siteProperties.getProperty( 'typesUseViewActionInListings', []) #searchterm_query = '?searchterm=%s' % url_quote_plus(q) pretty_title_or_id = ploneUtils.pretty_title_or_id searchterms = url_quote_plus(q) # SIMPLE CONFIGURATION #USE_ICON = True MAX_TITLE = 50 MAX_DESCRIPTION = 150 REQUEST = self.request RESPONSE = REQUEST.RESPONSE RESPONSE.setHeader('Content-Type', 'text/xml;charset=utf-8') # replace named entities with their numbered counterparts, in the # xml the named ones are not correct # ↓ --> ↓ # … --> … legend_livesearch = _( 'legend_livesearch', default='LiveSearch ↓') label_no_results_found = _('label_no_results_found', default='No matching results found.') label_advanced_search = _('label_advanced_search', default='Advanced Search…') label_show_all = _('label_show_all', default='Show all items') if not results: self.write('''<fieldset class="livesearchContainer">''') self.write('''<legend id="livesearchLegend">%s</legend>''' % ts.translate(legend_livesearch, context=REQUEST)) self.write('''<div class="LSIEFix">''') self.write('''<div id="LSNothingFound">%s</div>''' % ts.translate(label_no_results_found, context=REQUEST)) self.write('''<div class="LSRow">''') self.write('<a href="%s" class="advancedsearchlink">%s</a>' % (portal_url + '/@@search', ts.translate(label_advanced_search, context=REQUEST))) self.write('''</div>''') self.write('''</div>''') self.write('''</fieldset>''') else: self.write('''<fieldset class="livesearchContainer">''') self.write('''<legend id="livesearchLegend">%s</legend>''' % ts.translate(legend_livesearch, context=REQUEST)) self.write('''<div class="LSIEFix">''') self.write('''<ul class="LSTable">''') for result in results[:limit]: icon = plone_view.getIcon(result) # XXX: default item URL # itemUrl = result.getURL() itemUrl = "{0}/articles/{1}".format( self.context.absolute_url(), str(result.id)) if result.portal_type in useViewAction: itemUrl += '/view' #itemUrl = itemUrl + searchterm_query self.write('''<li class="LSRow">''') self.write(icon.html_tag() or '') full_title = safe_unicode(pretty_title_or_id(result)) if len(full_title) > MAX_TITLE: display_title = ''.join((full_title[:MAX_TITLE], '...')) else: display_title = full_title full_title = full_title.replace('"', '"') klass = 'contenttype-%s' \ % ploneUtils.normalizeString(result.portal_type) self.write('''<a href="%s" title="%s" class="%s">%s</a>''' % (itemUrl, full_title, klass, display_title)) display_description = safe_unicode(result.Description) if len(display_description) > MAX_DESCRIPTION: display_description = ''.join( (display_description[:MAX_DESCRIPTION], '...')) # need to quote it, to avoid injection of html containing # javascript and other evil stuff display_description = html_quote(display_description) self.write('''<div class="LSDescr">%s</div>''' % ( display_description)) self.write('''</li>''') full_title = None display_title = None display_description = None self.write('''<li class="LSRow">''') self.write('<a href="%s" class="advancedsearchlink">%s</a>' % (portal_url + '/@@search', ts.translate(label_advanced_search, context=REQUEST))) self.write('''</li>''') if len(results) > limit: # add a more... row self.write('''<li class="LSRow">''') searchquery = '@@search?SearchableText=%s&path=%s' \ % (searchterms, path) self.write('<a href="%s" class="advancedsearchlink">%s</a>' % ( searchquery, ts.translate(label_show_all, context=REQUEST))) self.write('''</li>''') self.write('''</ul>''') self.write('''</div>''') self.write('''</fieldset>''') return '\n'.join(self.output).encode('utf-8')
def getSectionMap(self, portal_types=TOPIC_VIEW_TYPES): """ returns a complex list of section dicts [{title:sectiontitle, subtopics:listOfSubTopics, url:urlOfSection, count:itemsInSection}, ...] subtopics are each lists [{title:titleOfSubSection,url:urlOfSubSection}] This is used in helpcenter_ploneorg.pt. """ context = Acquisition.aq_inner(self.context) here_url = context.absolute_url() phc = context.getPHCObject() topics = phc.getSectionsVocab() # dict to save counts and start-here items topicsDict = defaultdict(lambda: 0) featuredDict = defaultdict(list) items = self.catalog(portal_type=portal_types, review_state='published') for item in items: for section in item.getSections: if not section: continue topicsDict[section] = topicsDict[section] + 1 if getattr(item, 'getStartHere', False): featuredDict[section].append({ 'title': item.Title, 'description': item.Description, 'url': item.getURL(), }) sections = [] currTitle = '' for topic in topics: count = topicsDict.get(topic) if count: # break into main topic, sub topic cs = [s.strip() for s in topic.split(':')] main = cs[0] sub = ': '.join(cs[1:]) if main != currTitle: # append a new topic dict currTitle = main currSubSections = [] featured = featuredDict.get(topic) sections.append({ 'title': currTitle, 'subtopics': currSubSections, 'url': here_url + '/topic/' + url_quote_plus(currTitle), 'count': count, 'featured': featured, }) if sub: # add to the subtopics list id = sub.lower().replace(' ', '-') # make HTML anchor ID #sections[-1]['count'] += count #no need to globalize on the main level section because it's already done: now a subsection item has the main section automatically selected currSubSections.append({ 'title': sub, 'url': "%s/topic/%s#%s" % (here_url, url_quote_plus(currTitle), id) }) return sections
# convert queries to zctextindex # XXX really if it contains + * ? or - # it will not be right since the catalog ignores all non-word # characters equally like # so we don't even attept to make that right. # But we strip these and these so that the catalog does # not interpret them as metachars # See http://dev.plone.org/plone/ticket/9422 for an explanation of '\u3000' multispace = u"\u3000".encode("utf-8") for char in ("?", "-", "+", "*", multispace): q = q.replace(char, " ") r = q.split() r = " AND ".join(r) r = quote_bad_chars(r) + "*" searchterms = url_quote_plus(r) site_encoding = context.plone_utils.getSiteEncoding() params = {"SearchableText": r, "portal_type": friendly_types, "sort_limit": limit + 1} if path is None: # useful for subsides params["path"] = getNavigationRoot(context) else: params["path"] = path # search limit+1 results to know if limit is exceeded results = catalog(**params) searchterm_query = "?searchterm=%s" % url_quote_plus(q)
def render_results(self, resp): output = [] def write(s): output.append(safe_unicode(s)) ts = getToolByName(self.context, 'translation_service') portal_url = getToolByName(self.context, 'portal_url')() portalProperties = getToolByName(self.context, 'portal_properties') siteProperties = getattr(portalProperties, 'site_properties', None) useViewAction = [] if siteProperties is not None: useViewAction = siteProperties.getProperty( 'typesUseViewActionInListings', []) results = OGSolrContentListing(resp) if not results: write('''<fieldset class="livesearchContainer">''') write('''<legend id="livesearchLegend">%s</legend>''' % ts.translate(legend_livesearch, context=self.request)) write('''<div class="LSIEFix">''') write('''<div id="LSNothingFound">%s</div>''' % ts.translate(label_no_results_found, context=self.request)) write('''<div class="LSRow">''') write('<a href="%s" style="font-weight:normal">%s</a>' % (portal_url + '/advanced_search?SearchableText=%s' % url_quote_plus(self.search_term), ts.translate(label_advanced_search, context=self.request))) write('''</div>''') write('''</div>''') write('''</fieldset>''') else: write('''<fieldset class="livesearchContainer">''') write('''<legend id="livesearchLegend">%s</legend>''' % ts.translate(legend_livesearch, context=self.request)) write('''<div class="LSIEFix">''') write('''<ul class="LSTable">''') for result in results: itemUrl = result.getURL() if result.portal_type in useViewAction: itemUrl += '/view' itemUrl = itemUrl + u'?SearchableText=%s' % url_quote_plus( self.search_term) write('''<li class="LSRow">''') full_title = safe_unicode(result.Title()) if len(full_title) > MAX_TITLE: display_title = ''.join((full_title[:MAX_TITLE], '...')) else: display_title = full_title full_title = full_title.replace('"', '"') css_klass = get_mimetype_icon_klass(result.doc) write('''<a href="%s" title="%s" class="%s">%s</a>''' % (itemUrl, full_title, css_klass, display_title)) display_description = safe_unicode(result.Description()) or u'' if len(display_description) > MAX_DESCRIPTION: display_description = ''.join( (display_description[:MAX_DESCRIPTION], '...')) # need to quote it, to avoid injection of html containing javascript and other evil stuff display_description = html_quote(display_description) write('''<div class="LSDescr">%s</div>''' % (display_description)) write('''</li>''') full_title, display_title, display_description = None, None, None write('''<li class="LSRow">''') write('<a href="%s" style="font-weight:normal">%s</a>' % (portal_url + '/advanced_search?SearchableText=%s' % url_quote_plus(self.search_term), ts.translate(label_advanced_search, context=self.request))) write('''</li>''') if resp.num_found > self.limit: # add a more... row write('''<li class="LSRow">''') searchquery = u'@@search?SearchableText=%s&path=%s' % ( url_quote_plus(self.search_term), self.path) write(u'<a href="%s" style="font-weight:normal">%s</a>' % (searchquery, ts.translate(label_show_all, context=self.request))) write('''</li>''') write('''</ul>''') write('''</div>''') write('''</fieldset>''') return '\n'.join(output).encode('utf-8')
# result set. # convert queries to zctextindex # XXX really if it contains + * ? or - # it will not be right since the catalog ignores all non-word # characters equally like # so we don't even attept to make that right. # But we strip these and these so that the catalog does # not interpret them as metachars # See http://dev.plone.org/plone/ticket/9422 for an explanation of '\u3000' multispace = u'\u3000'.encode('utf-8') for char in ('?', '-', '+', '*', multispace): q = q.replace(char, ' ') r = q.split() r = " AND ".join(r) searchurlparameter = url_quote_plus(quote_bad_chars(r)) r = quote_bad_chars(r) + '*' searchterms = url_quote_plus(r) site_encoding = context.plone_utils.getSiteEncoding() params = { 'SearchableText': r, 'portal_type': friendly_types, 'sort_limit': limit + 1 } if path is None: # useful for subsides params['path'] = getNavigationRoot(context) else:
## Script (Python) "collector_issue_followup.py" ##parameters=comment, action="comment" ##title=Submit a new comment. from Products.PythonScripts.standard import url_quote_plus REQUEST = context.REQUEST got = context.do_action(action, comment, assignees=REQUEST.get('assignees', []), file=REQUEST.get('file'), fileid=REQUEST.get('fileid', ''), filetype=(REQUEST.get('filetype', 'file'))) if context.status() in ['Resolved', 'Rejected', 'Deferred']: destination = context.aq_parent.absolute_url() else: destination = context.absolute_url() if got: destination += '?portal_status_message=' + url_quote_plus(got) context.REQUEST.RESPONSE.redirect(destination)
# result set. # convert queries to zctextindex # XXX really if it contains + * ? or - # it will not be right since the catalog ignores all non-word # characters equally like # so we don't even attept to make that right. # But we strip these and these so that the catalog does # not interpret them as metachars ##q = re.compile(r'[\*\?\-\+]+').sub(' ', q) for char in '?-+*': q = q.replace(char, ' ') r=q.split() r = " AND ".join(r) r = quote_bad_chars(r)+'*' searchterms = url_quote_plus(r) site_encoding = context.plone_utils.getSiteEncoding() # TODO: Until we have a SearchableText field on the content catalog, use the Title # results = catalog(SearchableText=r, portal_type=friendly_types) results = catalog(Title=r, portal_type=friendly_types) searchterm_query = '?searchterm=%s'%url_quote_plus(q) RESPONSE = context.REQUEST.RESPONSE RESPONSE.setHeader('Content-Type', 'text/xml;charset=%s' % site_encoding) # replace named entities with their numbered counterparts, in the xml the named ones are not correct # ↓ --> ↓ # … --> …
def __call__(self, subject, body_text, text_format='plain', username=None, password=None): """This method is lifted almost directly from CMFPlone's discussion_reply.cpy skin script. Modifications start at the point where we try to adapt to IWeblogEntry. """ req = self.request if username or password: # The user username/password inputs on on the comment form were used, # which might happen when anonymous commenting is enabled. If they typed # something in to either of the inputs, we send them to 'logged_in'. # 'logged_in' will redirect them back to this script if authentication # succeeds with a query string which will post the message appropriately # and show them the result. if 'logged_in' fails, the user will be # presented with the stock login failure page. This all depends # heavily on cookiecrumbler, but I believe that is a Plone requirement. came_from = '%s?subject=%s&body_text=%s' % (req['URL'], subject, body_text) came_from = url_quote_plus(came_from) portal_url = self.context.portal_url() return req.RESPONSE.redirect( '%s/logged_in?__ac_name=%s' '&__ac_password=%s' '&came_from=%s' % (portal_url, url_quote_plus(username), url_quote_plus(password), came_from, ) ) # if (the user is already logged in) or (if anonymous commenting is enabled and # they posted without typing a username or password into the form), we do # the following mtool = getToolByName(self.context, 'portal_membership') creator = mtool.getAuthenticatedMember().getId() # qPloneComments related code pp = getToolByName(self.context,'portal_properties') qPC = getattr(pp,'qPloneComments', None) requireEmail = False if qPC: requireEmail = qPC.getProperty('require_email', False) if requireEmail: if mtool.isAnonymousUser(): email = self.request.get('email', '') else: email = mtool.getAuthenticatedMember().getProperty('email') isForAnonymous = pp['qPloneComments'].getProperty('enable_anonymous_commenting', False) comment_creator = req.get('Creator', None) if isForAnonymous and comment_creator: # Get entered anonymous name creator = comment_creator dtool = getToolByName(self.context, 'portal_discussion') tb = dtool.getDiscussionFor(self.context) if requireEmail: id = tb.createReply(title=subject, text=body_text, Creator=creator, email=email) else: id = tb.createReply(title=subject, text=body_text, Creator=creator) reply = tb.getReply(id) # Add website property to reply website = req.get('website', '').strip() if website: if not website.startswith('http://'): website = 'http://' + website reply.manage_addProperty(id='website', value=website, type='string') # TODO THIS NEEDS TO GO AWAY! if hasattr(dtool.aq_explicit, 'cookReply'): dtool.cookReply(reply, text_format='plain') parent = tb.aq_parent # return to the discussable object. obj = self.context.plone_utils.getDiscussionThread(tb)[0] try: entry = IWeblogEntry(obj).__of__(self.context.aq_inner.aq_parent) # Check for the existence of a parent weblog to see if `obj' should # be treated as having an archive url. if IWeblog.providedBy(entry.getWeblog()): weview = getMultiAdapter((obj, self.request), name=u'weblogentry_view') base = weview.getArchiveURLFor(entry) except TypeError: base = obj.getTypeInfo().getActionInfo('object/view', obj)['url'] anchor = reply.getId() from Products.CMFPlone.utils import transaction_note transaction_note('Added comment to %s at %s' % (parent.title_or_id(), reply.absolute_url())) self.context.plone_utils.addPortalMessage(_(u'Comment added.')) target = '%s#%s' % (base, anchor) return req.RESPONSE.redirect(target)