""" open/dulcinea/lib/ui/category.qpy """ from dulcinea.category import Category from dulcinea.sort import method_sort, str_sort from dulcinea.ui.form.select_widget import BigMultipleSelectWidget from dulcinea.ui.user.util import ensure_admin_access from dulcinea.util import is_new from qp.fill.directory import Directory from qp.fill.form import Form from qp.fill.html import href from qp.fill.widget import StringWidget, TextWidget from qp.lib.spec import require, match from qp.pub.common import header, footer, redirect from qpy import xml, xml_quote def default_decorate:xml(categories, body, title=None): header(title) '
' body '
' footer(title) class CategoryDirectory(Directory): def __init__(self, title='Categories', decorate=default_decorate): """(title : str) """ self.title = title self.decorate = decorate def get_exports(self): ensure_admin_access() yield ('', 'index', 'View', 'View all Categories') yield ('new', 'new', 'Add', 'Add a new Category') def get_category_db(self): """() -> CategoryDatabase """ raise NotImplementedError def get_categories(self): """() -> [(category_name, [Category])] """ yield ('Categories', self.get_category_db().get_categories()) def get_new_category(self): return Category() def index(self): def format_category_list:xml(categories, title): ''' %s Label Description ''' % title if categories: for category in str_sort(categories): ''' %s %s %s ''' % (href(category.get_name() + '/', category.get_name()), category.get_label(), category.get_description()) else: ''' None ''' def format_body:xml(): '''
''' for category_name, categories in self.get_categories(): format_category_list(categories, category_name) '
' '
' return self.decorate(self.get_categories(), format_body(), self.title) def new(self): category = self.get_new_category() category_db = self.get_category_db() form = category_form(category, category_db) if form.get('cancel'): redirect('.') def render(): return self.decorate([category], form.render(), 'New Category') if not form.get('add') or form.has_errors(): return render() name = form.get('name') for component, attr, crumb, title in self.get_exports(): if component == name: form.get_widget('name').set_error("Bad name '%s'" % name) return render() category.set_name(name) category_db.add_category(category) category.set_label(form.get('label')) category.set_description(form.get('description')) category.set_parents(form.get('parents') or []) redirect(category.get_name()) def get_category_edit_ui(self, category): return CategoryEditDirectory(category, self.get_category_db()) def _q_lookup(self, component): category = self.get_category_db().get_category(component) if category is None: return None return self.get_category_edit_ui(category) class CategoryEditDirectory(Directory): def __init__(self, category, category_db, decorate=default_decorate): self.category = category self.category_db = category_db self.decorate = decorate def get_exports(self): ensure_admin_access() yield ('', 'index', 'View', self.category.get_label() or self.category.get_name()) yield ('edit', 'edit', 'Edit', 'Edit %s' % (self.category.get_label() or self.category.get_name())) def format_category:xml(self): '''''' % (self.category.get_name(), self.category.get_label(), self.category.get_description()) def index(self): return self.decorate( [self.category], self.format_category(), self.category.get_label() or self.category.get_name()) def edit(self): form = category_form(self.category, self.category_db) if form.get('cancel'): redirect('.') if not form.get('update') or form.has_errors(): return self.decorate([self.category], form.render(), self.category.get_label() or self.category.get_name()) if self.category.get_name() != form.get('name'): self.category_db.rename_category(self.category, form.get('name')) self.category.set_label(form.get('label')) self.category.set_description(form.get('description')) self.category.set_parents(form.get('parents') or []) redirect('../%s/' % self.category.get_name()) def category_form(category, category_db): form = Form() if is_new(category): form.add_submit('add', 'Add') else: form.add_submit('update', 'Update') form.add(StringWidget, 'name', title='Name', value=category.get_name(), hint=('Name must contain only alphanumeric ' 'characters, dashes and underscores.'), size=20, required=1) form.add(StringWidget, 'label', title='Label', value=category.get_label(), size=30) form.add(TextWidget, 'description', title='Description', value=category.get_description(), rows=8, cols=80) category_options = [(grp, grp.get_label()) for grp in category_db.get_categories() if not category.is_ancestor_of(grp)] if category_options: form.add(BigMultipleSelectWidget, 'parents', title='Parents', value=list(category.get_parents()), options=category_options, required=1) form.add_submit('cancel', 'Cancel') if not match(form.get('name'), category.name_is): form.set_error('name', 'Invalid name') elif ((form.get('add') or category.get_name() != form.get('name')) and category_db.get_category(form.get('name'))): form.set_error('name', 'This name is already used') return form def format_hierarchy:xml(category, selections, base_url=None, label=None): # category: the children of category will be displayed # selections: the list of categories to be highlighted (parents of these # categories will also be highlighted) # base_url: the fragment of URL starting at the root and ending where # this hierarchy begins in the URL namespace # label: label to place above the list of children-of-category require(category, Category) require(selections, ([Category], None)) '' if label: '' % label _format_hierarchy(category, selections or [], base_url or '') '
%s
' def _format_hierarchy:xml(category, selections, base_url, level=0): children = method_sort(category.get_children(), str('get_label')) for child in children: url = base_url + child.get_name() + '/' label = child.get_label() hint = 'view all %s' % label.lower() indent = ' ' * (3 * level) link = href( url, xml(xml_quote(label).replace(' ', ' ')), hint) if selections: for selection in selections: if child.is_ancestor_of(selection): '%s%s\n' % (indent, link) _format_hierarchy(child, selections, base_url, level=level+1) break else: '%s%s\n' % (indent, link) else: '%s%s\n' % (indent, link) def hierarchy_header:xml(category, selections=None, title='Categories', base_url=None, label=None, **kwargs): # category: the children of category will be displayed # selections: the list of categories to be highlighted (parents of these # categories will also be highlighted) # title: the title of the page # base_url: the fragment of URL starting at the root and ending where # this hierarchy begins in the URL namespace # label: label to place above the list of children-of-category header(title, **kwargs) '
' format_hierarchy(category, selections, base_url=base_url, label=label) '' def hierarchy_footer:xml(title='Categories', **kwargs): '''
''' footer(title, **kwargs) def format_hierarchy_css:str(color_map): # Requires: # BODY_FG SELECTED_BG CRUMBS_BG LIGHT_A LIGHT_A_HOVER LIGHT_A_VISITED # to be defined in color_map """ table.hierarchy { font-size: x-small; width: 100%%; border: none; border-spacing: 0; } @media print { table.hierarchy { display: none; } } table.hierarchy tr { border: thin solid red; } table.hierarchy th { font-weight: bold; color: %(CRUMBS_BG)s; padding: 1ex; font-size: larger; } table.hierarchy tr td { background: %(CRUMBS_BG)s; padding: 1ex; } table.hierarchy td a:link, table.hierarchy td a:visited, table.hierarchy td a:active { text-decoration: none; color: white; } table.hierarchy td.selected a { color: %(LIGHT_A)s; } table.hierarchy td.selected { font-weight: bold; background: %(SELECTED_BG)s; } table.hierarchy a:hover { color: %(LIGHT_A_HOVER)s; text-decoration: underline; } """ % color_map def format_hierarchy_style:str( table_text_style=None, header_text_style=None, cell_text_style=None, selected_text_style=None): base = 'table.hierarchy' if table_text_style: table_text_style.format_rules(base, []) if header_text_style: header_text_style.format_rules(base, ['th']) if cell_text_style: cell_text_style.format_rules(base, ['td']) if selected_text_style: selected_text_style.format_rules(base, ['td.selected'])