From a86c8afcfa8146900d1dc6e10c49a36ad784db0a Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Mon, 5 Dec 2011 10:44:34 +0100 Subject: [PATCH 1/5] SEO: show informative URLs (with title) when global option 'useInformativeURLs' is set --- browser/common.py | 6 ++++++ browser/node.py | 34 ++++++++-------------------------- browser/node_macros.pt | 4 ++-- browser/util.py | 11 ++++++++--- layout/browser/base.py | 9 ++------- 5 files changed, 26 insertions(+), 38 deletions(-) diff --git a/browser/common.py b/browser/common.py index 8cc21ab..9ab6845 100644 --- a/browser/common.py +++ b/browser/common.py @@ -58,6 +58,7 @@ from cybertools.relation.interfaces import IRelationRegistry from cybertools.stateful.interfaces import IStateful from cybertools.text import mimetypes from cybertools.typology.interfaces import IType, ITypeManager +from loops.browser.util import normalizeForUrl from loops.common import adapted, baseObject from loops.config.base import DummyOptions from loops.i18n.browser import I18NView @@ -151,6 +152,11 @@ class BaseView(GenericView, I18NView): def name(self): return getName(self.context) + def makeTargetUrl(self, baseUrl, targetId, title=None): + if self.globalOptions('useInformativeURLs') and title: + return '%s/.%s-%s' % (baseUrl, targetId, normalizeForUrl(title)) + return '%s/.%s' % (baseUrl, targetId) + @Lazy def principalId(self): principal = self.request.principal diff --git a/browser/node.py b/browser/node.py index 5c95f8d..82a325b 100644 --- a/browser/node.py +++ b/browser/node.py @@ -292,8 +292,7 @@ class NodeView(BaseView): def targetUrl(self): t = self.targetObjectView if t is not None: - #return '%s/.target%s' % (self.url, t.uniqueId) - return '%s/.%s' % (self.url, t.uniqueId) + return self.makeTargetUrl(self.url, t.uniqueId, t.title) return '' def renderTarget(self): @@ -465,10 +464,10 @@ class NodeView(BaseView): @Lazy def virtualTargetUrl(self): - targetId = self.targetId - if targetId is not None: - #return '%s/.target%s' % (self.url, targetId) - return '%s/.%s' % (self.url, targetId) + target = self.virtualTargetObject + if target is not None: + tv = BaseView(target, self.request) + return self.makeTargetUrl(self.url, tv.uniqueId, tv.title) else: return self.url @@ -494,12 +493,10 @@ class NodeView(BaseView): """ Return URL of given target view given as .XXX URL. """ if isinstance(target, BaseView): - #return '%s/.target%s' % (self.url, target.uniqueId) - return '%s/.%s' % (self.url, target.uniqueId) + return self.makeTargetUrl(self.url, target.uniqueId, target.title) else: - #return ('%s/.target%s' % - return ('%s/.%s' % - (self.url, util.getUidForObject(target))) + return self.makeTargetUrl(self.url, util.getUidForObject(target), + target.title) def getActions(self, category='object', target=None): actions = [] @@ -900,21 +897,6 @@ class NodeTraverser(ItemTraverser): return self.context.getLoopsRoot() if name.startswith('.'): name = self.cleanUpTraversalStack(request, name)[1:] - #traversalStack = request._traversal_stack - #while traversalStack and traversalStack[0].startswith('.target'): - # # skip obsolete target references in the url - # name = traversalStack.pop(0) - #traversedNames = request._traversed_names - #if traversedNames: - # lastTraversed = traversedNames[-1] - # if lastTraversed.startswith('.target') and lastTraversed != name: - # # let tag show the current object - # traversedNames[-1] = name - #if len(name) > len('.target'): - # uid = int(name[len('.target'):]) - # target = util.getObjectForUid(uid) - #else: - # target = self.context.target target = self.getTarget(name) if target is not None: # remember self.context in request diff --git a/browser/node_macros.pt b/browser/node_macros.pt index 2f7af3c..5470efe 100644 --- a/browser/node_macros.pt +++ b/browser/node_macros.pt @@ -141,7 +141,7 @@ tal:condition="nocall:target">
Resource Title
@@ -164,7 +164,7 @@ tal:condition="nocall:target">
Concept Title
diff --git a/browser/util.py b/browser/util.py index 5e1d818..ca12040 100644 --- a/browser/util.py +++ b/browser/util.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2006 Helmut Merz helmutm@cy55.de +# Copyright (c) 2011 Helmut Merz helmutm@cy55.de # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,10 +18,9 @@ """ Utilities. - -$Id$ """ +import re from zope.app import zapi from zope.app.pagetemplate import ViewPageTemplateFile from zope.app.publisher.browser.menu import BrowserMenu @@ -65,3 +64,9 @@ def html_quote(text, character_entities=((u'&', u'&'), (u'<', u'<' ), for re, name in character_entities: text = text.replace(re, name) return text + + +pattern = re.compile(r'[ /\?\+%]') + +def normalizeForUrl(text): + return pattern.sub('-', text) diff --git a/layout/browser/base.py b/layout/browser/base.py index d34da23..a6494d2 100644 --- a/layout/browser/base.py +++ b/layout/browser/base.py @@ -22,8 +22,6 @@ Base classes for layout-based views. $Id$ """ -import re - from zope.app.security.interfaces import IUnauthenticatedPrincipal from zope import component from zope.cachedescriptors.property import Lazy @@ -34,6 +32,7 @@ from zope.traversing.browser import absoluteURL from cybertools.util import format from loops.common import adapted from loops.i18n.browser import LanguageInfo +from loops.browser.util import normalizeForUrl as normalize from loops import util @@ -167,11 +166,7 @@ class BaseView(object): def getKeywords(self): return self.context.title.split() - + def getMetaDescription(self): return self.context.title -pattern = re.compile(r'[ /\?\+%]') - -def normalize(text): - return pattern.sub('-', text) From 3dd1698b80ee4e1e04f073c9542c95ee39e513fe Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 6 Dec 2011 08:50:51 +0100 Subject: [PATCH 2/5] more on breadcrumbs: for resources; get view for breadcrumbs parent via new standard NodeView method --- browser/node.py | 8 +++++--- browser/resource.py | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/browser/node.py b/browser/node.py index 82a325b..6153ad7 100644 --- a/browser/node.py +++ b/browser/node.py @@ -444,9 +444,7 @@ class NodeView(BaseView): def targetRender(self): return u'
%s
' % self.targetView('download.html', 'show') - @Lazy - def virtualTarget(self): - obj = self.virtualTargetObject + def getViewForTarget(self, obj): if obj is not None: basicView = component.getMultiAdapter((obj, self.request)) if obj == self.targetObject: @@ -456,6 +454,10 @@ class NodeView(BaseView): if hasattr(basicView, 'view'): return basicView.view + @Lazy + def virtualTarget(self): + return self.getViewForTarget(self.virtualTargetObject) + @Lazy def targetId(self): target = self.virtualTargetObject diff --git a/browser/resource.py b/browser/resource.py index 251ca49..4c31da9 100644 --- a/browser/resource.py +++ b/browser/resource.py @@ -18,8 +18,6 @@ """ View class for resource objects. - -$Id$ """ import urllib @@ -134,9 +132,6 @@ class ResourceView(BaseView): else: return self.template.macros['download'] - #def __init__(self, context, request): - # super(ResourceView, self).__init__(context, request) - def setupController(self): cont = self.controller if cont is None: @@ -157,6 +152,20 @@ class ResourceView(BaseView): subMacro=version_macros.macros['portlet_versions'], priority=25, info=self) + def breadcrumbs(self): + data = [] + if self.breadcrumbsParent is not None: + data.extend(self.breadcrumbsParent.breadcrumbs()) + if self.context != self.nodeView.targetObject: + data.append(dict(label=self.title, + url=self.nodeView.getUrlForTarget(self.context))) + return data + + @Lazy + def breadcrumbsParent(self): + for c in self.context.getConcepts([self.defaultPredicate]): + return self.nodeView.getViewForTarget(c) + @Lazy def view(self): context = self.context From 41847e26e2ded2fc5d1e63db940de5e8dc13b120 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 6 Dec 2011 09:32:15 +0100 Subject: [PATCH 3/5] improve breadcrumbs layout --- browser/skin/lobo/body.pt | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/browser/skin/lobo/body.pt b/browser/skin/lobo/body.pt index 7d22bca..765984b 100644 --- a/browser/skin/lobo/body.pt +++ b/browser/skin/lobo/body.pt @@ -27,14 +27,21 @@
-
- You are here: - - - > -
+ + + + + +
You are here: + + + > +
+
From fd48e3f329483b10df9c34b06a58056facc7a457 Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Tue, 6 Dec 2011 10:43:19 +0100 Subject: [PATCH 4/5] improve breadcrumbs layout --- browser/skin/lobo/body.pt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/skin/lobo/body.pt b/browser/skin/lobo/body.pt index 765984b..cca73aa 100644 --- a/browser/skin/lobo/body.pt +++ b/browser/skin/lobo/body.pt @@ -30,7 +30,7 @@ -
You are here: Date: Fri, 9 Dec 2011 09:11:59 +0100 Subject: [PATCH 5/5] allow suppression of controller set-up when creating target view, e.g. when creating view for breadcrumbs item --- browser/node.py | 6 +++--- browser/resource.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/browser/node.py b/browser/node.py index 6153ad7..e49fdb6 100644 --- a/browser/node.py +++ b/browser/node.py @@ -444,13 +444,13 @@ class NodeView(BaseView): def targetRender(self): return u'
%s
' % self.targetView('download.html', 'show') - def getViewForTarget(self, obj): + def getViewForTarget(self, obj, setup=True): if obj is not None: basicView = component.getMultiAdapter((obj, self.request)) if obj == self.targetObject: basicView._viewName = self.context.viewName - #if self.context.nodeType != 'text': - basicView.setupController() + if setup: + basicView.setupController() if hasattr(basicView, 'view'): return basicView.view diff --git a/browser/resource.py b/browser/resource.py index 4c31da9..1a41178 100644 --- a/browser/resource.py +++ b/browser/resource.py @@ -164,7 +164,7 @@ class ResourceView(BaseView): @Lazy def breadcrumbsParent(self): for c in self.context.getConcepts([self.defaultPredicate]): - return self.nodeView.getViewForTarget(c) + return self.nodeView.getViewForTarget(c, setup=False) @Lazy def view(self):