Formalize the concept of a NonPage that can appear in the site-map, useful for
non-clickable sidebar menu divisions, for example. Convert "hacky" fake Pages
into NonPages in the site-map.
Also, pass page=self to every view as a keyword argument when generating the
Django urlpatterns.
Patch by: Todd Larsen
Review by: to-be-reviewed
--- a/app/soc/logic/site/map.py Sat Oct 18 01:00:10 2008 +0000
+++ b/app/soc/logic/site/map.py Sat Oct 18 01:32:40 2008 +0000
@@ -105,27 +105,9 @@
parent=home)
# Site User Profile views
-site_user_sub_menu = page.Page(
- page.Url(
- # not a real Django URL regex, just a unique placeholder
- # (this can be any unique string that re.compile() will not reject, but
- # that also contains characters that would be escaped, causing
- # soc.logic.site.page.Page.makeLinkUrl() to reject it and not make the
- # menu item into an <A HREF> link; this seems a bit hacky...)
- #
- # TODO(tlarsen): formalize this hack by subclassing Page (maybe calling
- # it something like NonPage) to add a non-linkable form of page for
- # use in dividers just like this
- # TODO(tlarsen): add an optional keyword parameter that can be used to
- # control where the collapsible sub-menus are and whether they are
- # collapsed or not by default in the sidebar menu
- 'site*user*sub*menu',
- # no view, since this is just a link-less menu divider
- # (this page will not be placed in urlpatterns)
- None,
- # name is alternate string for view when it is not unique
- name='site-user-sub-menu'),
- '',
+site_user_sub_menu = page.NonPage(
+ 'site-user-sub-menu',
+ 'Site: Users Sub-Menu',
short_name='Site Users',
parent=site_settings_edit)
@@ -170,15 +152,9 @@
parent=home)
# Site Document views
-site_docs_sub_menu = page.Page(
- page.Url(
- # (see site_user_sub_menu above for how this works...)
- 'site*docs*sub*menu',
- # no view, since this is just a link-less menu divider
- None,
- # name is alternate string for view when it is not unique
- name='site-docs-sub-menu'),
- '',
+site_docs_sub_menu = page.NonPage(
+ 'site-docs-sub-menu',
+ 'Site: Documents Sub-Menu',
short_name='Site Documents',
parent=site_settings_edit)
@@ -219,19 +195,13 @@
page.Url(
r'^sponsor/profile/%s' % path_link_name.LINKNAME_ARG_PATTERN,
'soc.views.sponsor.profile.public'),
- 'Public Profile',
+ 'Sponsor Public Profile',
parent=home)
# Sponsor Group Site views
-site_sponsor_sub_menu = page.Page(
- page.Url(
- # (see site_user_sub_menu above for how this works...)
- 'site*sponsor*sub*menu',
- # no view, since this is just a link-less menu divider
- None,
- # name is alternate string for view when it is not unique
- name='site-sponsor-sub-menu'),
- '',
+site_sponsor_sub_menu = page.NonPage(
+ 'site-sponsor-sub-menu',
+ 'Site: Sponsors Sub-Menu',
short_name='Site Sponsors',
parent=site_settings_edit)
--- a/app/soc/logic/site/page.py Sat Oct 18 01:00:10 2008 +0000
+++ b/app/soc/logic/site/page.py Sat Oct 18 01:32:40 2008 +0000
@@ -66,10 +66,15 @@
self.name = name
self.prefix = prefix
- def makeDjangoUrl(self):
- """Returns a Django url() used by urlpatterns.
+ def makeDjangoUrl(self, **extra_kwargs):
+ """Returns a Django url() used by urlpatterns, or None if not a view.
"""
- return defaults.url(self.regex, self.view, kwargs=self.kwargs,
+ if not self.view:
+ return None
+
+ kwargs = copy.deepcopy(self.kwargs)
+ kwargs.update(extra_kwargs)
+ return defaults.url(self.regex, self.view, kwargs=kwargs,
name=self.name, prefix=self.prefix)
_STR_FMT = '''%(indent)sregex: %(regex)s
@@ -154,7 +159,7 @@
def getChildren(self):
"""Returns an iterator over any child Pages
"""
- for page, _ in self.child_by_urls.itervalues():
+ for page in self.child_by_views.itervalues():
yield page
children = property(getChildren)
@@ -224,15 +229,17 @@
url = page.url
- if not isinstance(url.regex, basestring):
- raise ValueError('"regex" must be a string, not a compiled regex')
+ if url.regex:
+ if not isinstance(url.regex, basestring):
+ raise ValueError('"regex" must be a string, not a compiled regex')
- # TODO(tlarsen): see if Django has some way exposed in its API to get
- # the view name from the request path matched against urlpatterns;
- # if so, there would be no need for child_by_urls, because the
- # request path could be converted for us by Django into a view/name,
- # and we could just use child_by_views with that string instead
- self.child_by_urls[url.regex] = (page, re.compile(url.regex))
+ # TODO(tlarsen): see if Django has some way exposed in its API to get
+ # the view name from the request path matched against urlpatterns;
+ # if so, there would be no need for child_by_urls, because the
+ # request path could be converted for us by Django into a view/name,
+ # and we could just use child_by_views with that string instead
+ self.child_by_urls[url.regex] = (page, re.compile(url.regex))
+ # else: NonUrl does not get indexed by regex, because it has none
# TODO(tlarsen): make this work correctly if url has a prefix
# (not sure how to make this work with include() views...)
@@ -289,8 +296,9 @@
elif name in self.child_views:
regex = self.child_by_views[name].url.regex
- # regex must refer to an existing Page at this point
- del self.child_urls[regex]
+ if regex:
+ # regex must refer to an existing Page at this point
+ del self.child_urls[regex]
if not isinstance(view, basestring):
# use name if view is callable() or None, etc.
@@ -318,6 +326,9 @@
link = self.url.regex
+ if not link:
+ return None
+
if link.startswith('^'):
link = link[1:]
@@ -360,7 +371,7 @@
def makeDjangoUrl(self):
"""Returns the Django url() for the underlying self.url.
"""
- return self.url.makeDjangoUrl()
+ return self.url.makeDjangoUrl(page=self)
def makeDjangoUrls(self):
"""Returns an ordered mapping of unique Django url() objects.
@@ -379,9 +390,11 @@
Used to implement makeDjangoUrls(). See that method for details.
"""
urlpatterns = NoOverwriteSortedDict()
+
+ django_url = self.makeDjangoUrl()
- if self.url.view:
- urlpatterns[self.url.regex] = self.makeDjangoUrl()
+ if django_url:
+ urlpatterns[self.url.regex] = django_url
for child in self.children:
urlpatterns.update(child._makeDjangoUrlsDict())
@@ -418,3 +431,32 @@
"""Returns a string representation useful for logging.
"""
return self.asIndentedStr()
+
+
+class NonUrl(Url):
+ """Placeholder for when a site-map entry is not a linkable URL.
+ """
+
+ def __init__(self, name):
+ """Creates a non-linkable Url placeholder.
+
+ Args:
+ name: name of the non-view placeholder
+ """
+ Url.__init__(self, None, None, name=name)
+
+ def makeDjangoUrl(self, **extra_kwargs):
+ """Always returns None, since NonUrl is never a Django view.
+ """
+ return None
+
+
+class NonPage(Page):
+ """Placeholder for when a site-map entry is not a displayable page.
+ """
+
+ def __init__(self, non_url_name, long_name, **page_kwargs):
+ """
+ """
+ non_url = NonUrl(non_url_name)
+ Page.__init__(self, non_url, long_name, **page_kwargs)