# HG changeset patch # User Todd Larsen # Date 1224293560 0 # Node ID 8595c1129c74816029f691749d6e139dcc6a59c8 # Parent 6dad90b78770481d8214ab0233981ee92ef641d8 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 diff -r 6dad90b78770 -r 8595c1129c74 app/soc/logic/site/map.py --- 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 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) diff -r 6dad90b78770 -r 8595c1129c74 app/soc/logic/site/page.py --- 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)