# -*- test-case-name: xmantissa.test.test_webnav -*-
from epsilon.structlike import record
from zope.interface import implements
from xmantissa.ixmantissa import ITab
class TabMisconfiguration(Exception):
def __init__(self, info, tab):
Exception.__init__(
self,
"Inconsistent tab item factory information",
info, tab)
TabInfo = record('priority storeID children linkURL authoritative',
authoritative=None)
class Tab(object):
"""Represent part or all of the layout of a single navigation tab.
@ivar name: This tab's name.
@ivar storeID: A /-separated string containing URL segments to be
rendered as part of a link on the web.
@ivar priority: A float between 0 and 1 indicating the relative ordering of
this tab amongst its peers. Higher priorities sort sooner.
@ivar children: A tuple of tabs beneath this one.
@ivar authoritative: A flag indicating whether this instance of the
conceptual tab with this name takes precedent over any other instance of
the conceptual tab with this name. It is an error for two instances of the
same conceptual tab to be authoritative.
"""
_item = None
implements(ITab)
def __init__(self, name, storeID, priority, children=(), authoritative=True, linkURL=None):
self.name = name
self.storeID = storeID
self.priority = priority
self.children = tuple(children)
self.authoritative = authoritative
self.linkURL = linkURL
def __repr__(self):
return '<%s%s %r/%0.3f %r [%r]>' % (self.authoritative and '*' or '',
self.__class__.__name__,
self.name,
self.priority,
self.storeID,
self.children)
def __iter__(self):
raise TypeError("%r are not iterable" % (self.__class__.__name__,))
def __getitem__(self, key):
"""Retrieve a sub-tab from this tab by name.
"""
tabs = [t for t in self.children if t.name == key]
assert len(tabs) < 2, "children mis-specified for " + repr(self)
if tabs:
return tabs[0]
raise KeyError(key)
def pathFromItem(self, item, avatar):
"""
@param item: A thing that we linked to, and such.
@return: a list of [child, grandchild, great-grandchild, ...] that
indicates a path from me to the navigation for that item, or [] if
there is no path from here to there.
"""
for subnav in self.children:
subpath = subnav.pathFromItem(item, avatar)
if subpath:
subpath.insert(0, self)
return subpath
else:
myItem = self.loadForAvatar(avatar)
if myItem is item:
return [self]
return []
def getTabs(navElements):
# XXX TODO: multiple levels of nesting, this is hard-coded to 2.
# Map primary tab names to a TabInfo
primary = {}
# Merge tab information from all nav plugins into one big structure
for plg in navElements:
for tab in plg.getTabs():
if tab.name not in primary:
primary[tab.name] = TabInfo(
priority=tab.priority,
storeID=tab.storeID,
children=list(tab.children),
linkURL=tab.linkURL)
else:
info = primary[tab.name]
if info.authoritative:
if tab.authoritative:
raise TabMisconfiguration(info, tab)
else:
if tab.authoritative:
info.authoritative = True
info.priority = tab.priority
info.storeID = tab.storeID
info.children.extend(tab.children)
# Sort the tabs and their children by their priority
def key(o):
return -o.priority
resultTabs = []
for (name, info) in primary.iteritems():
info.children.sort(key=key)
resultTabs.append(
Tab(name, info.storeID, info.priority, info.children))
resultTabs.sort(key=key)
return resultTabs
def setTabURLs(tabs, webTranslator):
"""
Sets the C{linkURL} attribute on each L{Tab} instance
in C{tabs} that does not already have it set
@param tabs: sequence of L{Tab} instances
@param webTranslator: L{xmantissa.ixmantissa.IWebTranslator}
implementor
@return: None
"""
for tab in tabs:
if not tab.linkURL:
tab.linkURL = webTranslator.linkTo(tab.storeID)
setTabURLs(tab.children, webTranslator)
def getSelectedTab(tabs, forURL):
"""
Returns the tab that should be selected when the current
resource lives at C{forURL}. Call me after L{setTabURLs}
@param tabs: sequence of L{Tab} instances
@param forURL: L{nevow.url.URL}
@return: L{Tab} instance
"""
flatTabs = []
def flatten(tabs):
for t in tabs:
flatTabs.append(t)
flatten(t.children)
flatten(tabs)
forURL = '/' + forURL.path
for t in flatTabs:
if forURL == t.linkURL:
return t
flatTabs.sort(key=lambda t: len(t.linkURL), reverse=True)
for t in flatTabs:
if not t.linkURL.endswith('/'):
linkURL = t.linkURL + '/'
else:
linkURL = t.linkURL
if forURL.startswith(linkURL):
return t
syntax highlighted by Code2HTML, v. 0.9.1