Revision: 611
Author: batiste.bieler
Date: Tue Jul 21 01:02:49 2009
Log: Apply the patch for URL aliasing
http://code.google.com/p/django-page-cms/source/detail?r=611
Modified:
/trunk/pages/admin/__init__.py
/trunk/pages/managers.py
/trunk/pages/models.py
/trunk/pages/templatetags/pages_tags.py
/trunk/pages/urls.py
/trunk/pages/utils.py
/trunk/pages/views.py
=======================================
--- /trunk/pages/admin/__init__.py Sat Jul 11 06:22:03 2009
+++ /trunk/pages/admin/__init__.py Tue Jul 21 01:02:49 2009
@@ -14,7 +14,7 @@
from django.contrib.admin.sites import AlreadyRegistered
from pages import settings
-from pages.models import Page, Content
+from pages.models import Page, Content, PageAlias
from pages.utils import has_page_add_permission, get_placeholders
from pages.http import get_language_from_request, get_template_from_request
@@ -415,3 +415,12 @@
except AlreadyRegistered:
pass
+class AliasAdmin(admin.ModelAdmin):
+ list_display = ('page', 'url', 'is_canonical',)
+ list_editable = ('url', 'is_canonical',)
+
+try:
+ admin.site.register(PageAlias, AliasAdmin)
+except AlreadyRegistered:
+ pass
+
=======================================
--- /trunk/pages/managers.py Sat Jul 11 07:24:12 2009
+++ /trunk/pages/managers.py Tue Jul 21 01:02:49 2009
@@ -36,7 +36,8 @@
return self.filter(parent__isnull=True)
def valid_targets(self, page_id, perms="All", page=None):
- """QuerySet of valid targets for moving a page into the tree"""
+ """return a ``QuerySet`` of valid targets for moving a page into
the
+ tree"""
if page is None:
page = self.get(pk=page_id)
exclude_list = []
@@ -145,7 +146,7 @@
content = self.create(page=page, language=language, body=body,
type=cnttype)
def get_content(self, page, language, ctype, language_fallback=False):
- """Gets the latest content for a particular page and language.
+ """Gets the latest ``Content`` for a particular page and language.
Falls back to another language if wanted."""
PAGE_CONTENT_DICT_KEY = "page_content_dict_%d_%s"
if not language:
@@ -174,7 +175,7 @@
return ''
def get_content_slug_by_slug(self, slug):
- """Returns the latest Content slug object that match the given
+ """Returns the latest ``Content`` slug object that match the given
slug for the current site domain."""
content = self.filter(type='slug', body=slug)
if settings.PAGE_USE_SITE_ID:
@@ -187,7 +188,7 @@
return content
def get_page_ids_by_slug(self, slug):
- """Return all the page id according to a slug."""
+ """Return all page id matching the given slug."""
sql = '''SELECT pages_content.page_id,
MAX(pages_content.creation_date)
FROM pages_content WHERE (pages_content.type = %s
@@ -202,8 +203,8 @@
"""Hierachic page permission"""
def get_page_id_list(self, user):
- """Give a list of page where the user has rights or the
string "All"
- if the user has all rights."""
+ """Give a list of ``Page`` ids where the user has rights or the
string
+ "All" if the user has all rights."""
if user.is_superuser:
return 'All'
id_list = []
@@ -217,3 +218,43 @@
if page.id not in id_list:
id_list.append(page.id)
return id_list
+
+class PageAliasManager(models.Manager):
+ """PageAlias manager"""
+ def get_for_url(self, request, path=None, lang=None):
+ """
+ resolve a request to an alias. returns a PageAlias object or None
if the url
+ matches no page at all. The aliasing system supports plain aliases
(/foo/bar)
+ as well as aliases containing GET parameters
(like "index.php?page=foo").
+ """
+ from pages.utils import normalize_url
+ from pages.models import Page,PageAlias
+
+ url = normalize_url(path)
+ # §1: try with complete query string
+ if request.META["QUERY_STRING"]!="":
+ url = url + '?' + request.META["QUERY_STRING"]
+ try:
+ alias = PageAlias.objects.get(url=url)
+ return alias
+ except PageAlias.DoesNotExist:
+ pass
+ # §2: try with path only
+ url = normalize_url(path)
+ try:
+ alias = PageAlias.objects.get(url=url)
+ return alias
+ except PageAlias.DoesNotExist:
+ pass
+ # §3: the requested path is not an alias, but it may be the slug
of a page
+ page = Page.objects.from_path(path, lang)
+ if page:
+ alias = PageAlias(page=page, url=path)
+ # we have a page, let's see if there is a canonical alias for
it
+ if PageAlias.objects.filter(page=page,
is_canonical=True).count()>0:
+ alias.is_canonical = False
+ else:
+ alias.is_canonical = True
+ return alias
+ else:
+ return None
=======================================
--- /trunk/pages/models.py Wed Jul 15 01:56:17 2009
+++ /trunk/pages/models.py Tue Jul 21 01:02:49 2009
@@ -25,7 +25,7 @@
import mptt
from pages import settings
from pages.utils import get_placeholders
-from pages.managers import PageManager, ContentManager,
PagePermissionManager
+from pages.managers import PageManager, ContentManager,
PagePermissionManager, PageAliasManager
class Page(models.Model):
@@ -157,10 +157,16 @@
return languages
def get_absolute_url(self, language=None):
- url = reverse('pages-root')
- if settings.PAGE_USE_LANGUAGE_PREFIX:
- url += str(language) + '/'
- return url + self.get_url(language)
+ try:
+ alias = PageAlias.objects.get(page=self, is_canonical=True)
+ #if settings.PAGE_USE_LANGUAGE_PREFIX:
+ # url = str(language) + '/' + self.url
+ return alias.url
+ except:
+ url = reverse('pages-root')
+ if settings.PAGE_USE_LANGUAGE_PREFIX:
+ url += str(language) + '/'
+ return url + self.get_url(language)
def get_url(self, language=None):
"""
@@ -313,3 +319,23 @@
def __unicode__(self):
return "%s :: %s" % (self.page.slug(), self.body[0:15])
+
+class PageAlias(models.Model):
+ """URL alias for a page"""
+ page = models.ForeignKey(Page, null=True, blank=True,
verbose_name=_('page'))
+ url = models.CharField(max_length=1024, unique=True)
+ is_canonical = models.NullBooleanField(null=True, blank=True)
+ objects = PageAliasManager()
+ class Meta:
+ verbose_name_plural = _('Aliases')
+
+ def save(self, *args, **kwargs):
+ # normalize url
+ self.url = normalize_url(self.url)
+
+ # override to handle "is_canonical" properly
+ if self.is_canonical:
+ for alias in PageAlias.objects.filter(page=self.page):
+ alias.is_canonical = False
+ alias.save()
+ super(PageAlias, self).save(*args, **kwargs)
=======================================
--- /trunk/pages/templatetags/pages_tags.py Sat Jul 11 06:22:03 2009
+++ /trunk/pages/templatetags/pages_tags.py Tue Jul 21 01:02:49 2009
@@ -346,3 +346,30 @@
return PlaceholderNode.handle_token(parser, token)
register.tag('placeholder', do_placeholder)
+
+def pages_dynamic_tree_menu(context, page, url='/'):
+ """
+ render a "dynamic" tree menu, with all nodes expanded which are either
+ ancestors or the current page itself.
+ """
+ request = context['request']
+ site_id = None
+ children = None
+ if 'current_page' in context:
+ current_page = context['current_page']
+ # if this node is expanded, we also have to render its children
+ # a node is expanded if it is the current node or one of its
ancestors
+ if page.lft <= current_page.lft and page.rght >= current_page.rght:
+ children = page.get_children_for_frontend()
+ return locals()
+
+pages_dynamic_tree_menu =
register.inclusion_tag('pages/dynamic_tree_menu.html',
+
takes_context=True)(pages_dynamic_tree_menu)
+
+def pages_breadcrumb(context, page, url='/'):
+ request = context['request']
+ site_id = None
+ pages = page.get_ancestors()
+ return locals()
+pages_breadcrumb = register.inclusion_tag('pages/breadcrumb.html',
+
takes_context=True)(pages_breadcrumb)
=======================================
--- /trunk/pages/urls.py Wed Jul 15 01:56:17 2009
+++ /trunk/pages/urls.py Tue Jul 21 01:02:49 2009
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
from django.conf.urls.defaults import *
-from pages.views import details
+from pages.views import details, alias_wrapper
from pages import settings
urlpatterns = patterns('',
@@ -10,11 +10,11 @@
if settings.PAGE_USE_LANGUAGE_PREFIX:
urlpatterns += patterns('',
- url(r'^(?P<lang>[-\w]+)/(?P<path>.*)$', details,
+ url(r'^(?P<lang>[-\w]+)/(?P<path>.*)$', alias_wrapper,
name='pages-details-by-slug'),
)
else:
urlpatterns += patterns('',
- url(r'^(?P<path>.*)$', details,
+ url(r'^(?P<path>.*)$', alias_wrapper,
name='pages-details-by-slug'),
)
=======================================
--- /trunk/pages/utils.py Mon Jul 20 23:57:02 2009
+++ /trunk/pages/utils.py Tue Jul 21 01:02:49 2009
@@ -24,10 +24,10 @@
context = {}
temp.render(RequestContext(request, context))
plist, blist = [], []
- _placeholders_recursif(temp.nodelist, plist, blist)
+ placeholders_recursif(temp.nodelist, plist, blist)
return plist
-def _placeholders_recursif(nodelist, plist, blist):
+def placeholders_recursif(nodelist, plist, blist):
"""Recursively search into a template node list for PlaceholderNode
node."""
# I needed to import make this lazy import to make the doc compile
@@ -84,3 +84,23 @@
if permission == "All":
return True
return False
+
+def normalize_url(url):
+ """ return a normalized url with trailing and without leading slash
+
+ >>> normalize_url(None)
+ '/'
+ >>> normalize_url('/foo/bar')
+ '/foo/bar'
+ >>> normalize_url('foo/bar')
+ '/foo/bar'
+ >>> normalize_url('/foo/bar/')
+ '/foo/bar'
+ """
+ if not url or len(url)==0:
+ return '/'
+ if not url.startswith('/'):
+ url = '/' + url
+ if url.endswith('/'):
+ url = url[0:len(url)-1]
+ return url
=======================================
--- /trunk/pages/views.py Wed Jul 8 15:12:40 2009
+++ /trunk/pages/views.py Tue Jul 21 01:02:49 2009
@@ -3,7 +3,7 @@
from django.shortcuts import get_object_or_404
from django.contrib.sites.models import SITE_CACHE
from pages import settings
-from pages.models import Page, Content
+from pages.models import Page, Content, PageAlias
from pages.http import auto_render, get_language_from_request
from pages.http import get_slug_and_relative_path
@@ -67,3 +67,15 @@
}
details = auto_render(details)
+
+def alias_wrapper(request, path=None, lang=None, *args, **kwargs):
+ """
+ a wrapper for details() which resolves aliases and canonicalizes URLs
+ """
+ alias = PageAlias.objects.get_for_url(request, path, lang)
+ if not alias:
+ raise Http404
+ if alias.is_canonical:
+ return details(request, alias.page.slug(), *args, **kwargs)
+ else:
+ return
HttpResponsePermanentRedirect(alias.page.get_absolute_url(lang))
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"pinax-updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/pinax-updates?hl=en
-~----------~----~----~----~------~----~------~--~---