from qp.fill.directory import get_crumb_tree, Directory, gen_crumb_tree_menu_items from qp.fill.html import htmltag, href from qp.lib.spec import Specified, add_getters_and_setters, nspec, string, spec, specify, instance from qp.pub.common import get_path, get_publisher from urlparse import urljoin class MenuItem (Specified): css_class_is = nspec( string, "The name of the css class that should be used for this menu item, if known.") crumb_is = spec( string, "The label that is shown on this menu item") title_is = nspec( string, "The longer explanation of this menu item, if known.") relative_path_is = nspec( string, "The path that leads to this menu item.") path_is = nspec( string, "The full path that leads to this menu item.") sub_menu_is = nspec( instance('Menu'), "The the next menu down, if this item is selected.") selected_in_is = nspec( instance("Menu"), "The menu this item is selected in, if any") def __init__(self, relative_path=None, crumb=None, title=None, css_class=None): if relative_path is not None: path = urljoin(get_path(), relative_path) else: path = None specify(self, relative_path=relative_path, crumb=crumb, title=title, css_class=css_class, selected_in=None, sub_menu=None, path=path) def is_selected(self): return self.get_selected_in() is not None def is_final_selected(self): if self.get_path(): if self.get_path() == get_path(): return True if self.get_path() + '/' == get_path(): return True return False def get_css_class_list(self, classes=None): if classes is None: classes = [] else: classes = list(classes) classes.append("menu_item") if self.get_css_class(): classes.append(self.get_css_class()) if self.get_last_component() is not None: classes.append("menu_item_%s" % self.get_last_component().replace('.', '_')) if self.is_final_selected(): classes.append("final_selected_menu_item") if self.is_selected(): classes.append("selected_menu_item") return sorted(set(classes)) def get_last_component(self): if self.get_path(): return self.get_path().rsplit('/', 1)[1] def __str__(self): return "" % (self.get_path(), self.get_crumb()) add_getters_and_setters(MenuItem) class Menu (Specified): menu_items_is = spec( [MenuItem], "The MenuItems in this Menu") path_is = spec( string, "The full path that leads to this menu.") selected_menu_item_is = nspec( MenuItem, "The element of menu_items that is selected.") def __init__(self, menu_items): specify(self, menu_items=menu_items, path=menu_items[0].get_path().rsplit('/', 1)[1], selected_menu_item=None) candidates = [menu_item for menu_item in self.get_menu_items() if menu_item.get_path() and get_path().startswith(menu_item.get_path())] if candidates: self.set_selected_menu_item( max(candidates, key=lambda x: len(x.get_path() or ''))) self.get_selected_menu_item().set_selected_in(self) def is_selected(self): return self.get_selected_menu_item() is not None def is_final_selected(self): if self.get_path(): if self.get_path() == get_path(): return True if self.get_path() + '/' == get_path(): return True return False def get_css_class_list(self, classes=None): if classes is None: classes = [] else: classes = list(classes) classes.append("menu") if self.is_selected(): classes.append("selected_menu") else: classes.append("unselected_menu") if self.is_final_selected(): classes.append("final_selected_menu") return sorted(set(classes)) def get_last_component(self): components = self.get_path().rsplit('/', 1) return components[-1] def get_unselected_menu_items(self): selected = self.get_selected_menu_item() return [menu_item for menu_item in self.get_menu_items() if menu_item.get_path() is not None and menu_item is not selected] def __str__(self): return '\n'.join(map(str, self.menu_items)) + '\n' add_getters_and_setters(Menu) def get_directory_for_path(path): """ Returns the last Directory instance it encounters in trying to traverse the given path. """ assert path and path.startswith('/'), repr(path) components = path.split('/')[1:] directory = get_publisher().get_root_directory() for component in components: name = directory._q_translate(component) if name is None: obj = directory._q_lookup(component) else: obj = getattr(directory, name) if isinstance(obj, Directory): directory = obj else: break return directory def get_menu_for_directory(directory, base_path): menu_items = [ MenuItem(relative_path=relative_path, crumb=crumb, title=title, css_class=css_class) for relative_path, crumb, title, css_class in gen_crumb_tree_menu_items(base_path, directory)] return Menu(menu_items) def get_menu_for_path(path): return get_menu_for_directory(get_directory_for_path(path), path) def format_menu_for_path(path=None, classes=None): if path is None: path = get_path() if not path.endswith('/'): path = path.rsplit('/', 1)[0] + '/' return format_menu(get_menu_for_path(path), classes=classes) def get_menu_list(): menu_list = [] for crumb_list in get_crumb_tree(): menu_items = [ MenuItem(relative_path=relative_path, crumb=crumb, title=title, css_class=css_class) for relative_path, crumb, title, css_class in crumb_list] menu_list.append(Menu(menu_items)) if len(menu_list) > 1: selected_menu_item = menu_list[-2].get_selected_menu_item() if selected_menu_item is not None: selected_menu_item.set_sub_menu(menu_list[-1]) return menu_list def format_menu_item:xml(menu_item, classes=None, depth=None, nested=True): href(menu_item.get_path(), menu_item.get_crumb(), title=menu_item.get_title(), classes=classes) if nested: format_menu(menu_item.get_sub_menu(), depth=depth + 1, nested=nested) def format_menu:xml(menu, classes=None, depth=0, nested=True): if menu: if classes is None: classes = [] if depth is not None: classes.append("menu_depth_%s" % depth) '\n\n' htmltag('div', classes=menu.get_css_class_list(classes=classes)) '\n' selected_menu_item = menu.get_selected_menu_item() for menu_item_position, menu_item in enumerate(menu.get_menu_items()): classes = [] if depth is not None: classes.append("menu_item_depth_%s" % depth) classes.append("menu_item_pos%s" % menu_item_position) classes.append("menu_item_pos%s" % (menu_item_position - len(menu.get_menu_items()))) if menu_item is selected_menu_item: classes.append("selected_menu_item") else: classes.append("unselected_menu_item") classes.append("menu_item_component_%s" % menu_item.get_last_component()) format_menu_item(menu_item, classes=classes, depth=depth, nested=nested) '\n' '
' '\n' def format_menu_list:xml(menu_list=None, classes=None): if menu_list is None: menu_list = get_menu_list() if classes is None: classes = [] classes.append('menu_list') classes.append('menu_list_nested') format_menu(menu_list[0], classes=classes, depth=0) def format_flat_menu_list:xml(menu_list=None, classes=None): if menu_list is None: menu_list = [menu for menu in get_menu_list() if menu.get_unselected_menu_items()] if classes is None: classes = [] classes.append('menu_list') classes.append('menu_list_flat') '\n' htmltag('div', classes=classes) '\n' for menu_position, menu in enumerate(menu_list): classes = ["menu"] classes.append("menu_pos%s" % menu_position) classes.append("menu_pos%s" % (menu_position - len(menu_list))) format_menu(menu, classes=classes, depth=menu_position, nested=False) '
' '\n\n'