"""
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:
'%s |
' % label
_format_hierarchy(category, selections or [], base_url or '')
'
'
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'])