0002-categories-add-new-export_roles-and-statistics_roles.patch
tests/admin_pages/test_carddefcategory.py | ||
---|---|---|
191 | 191 | |
192 | 192 | |
193 | 193 |
def test_categories_reorder(pub): |
194 |
create_superuser(pub) |
|
195 | ||
194 | 196 |
CardDefCategory.wipe() |
195 | 197 |
category = CardDefCategory(name='foo') |
196 | 198 |
category.store() |
tests/admin_pages/test_category.py | ||
---|---|---|
202 | 202 | |
203 | 203 | |
204 | 204 |
def test_categories_reorder(pub): |
205 |
create_superuser(pub) |
|
206 | ||
205 | 207 |
Category.wipe() |
206 | 208 |
category = Category(name='foo') |
207 | 209 |
category.store() |
... | ... | |
220 | 222 |
categories = Category.select() |
221 | 223 |
Category.sort_by_position(categories) |
222 | 224 |
assert [x.id for x in categories] == ['3', '1', '2'] |
225 | ||
226 | ||
227 |
def test_categories_edit_roles(pub): |
|
228 |
create_superuser(pub) |
|
229 | ||
230 |
pub.role_class.wipe() |
|
231 |
role_a = pub.role_class(name='a') |
|
232 |
role_a.store() |
|
233 |
role_b = pub.role_class(name='b') |
|
234 |
role_b.store() |
|
235 | ||
236 |
Category.wipe() |
|
237 |
category = Category(name='foobar') |
|
238 |
category.store() |
|
239 | ||
240 |
app = login(get_app(pub)) |
|
241 |
resp = app.get('/backoffice/forms/categories/1/edit') |
|
242 | ||
243 |
resp.form['export_roles$element0'] = role_a.id |
|
244 |
resp = resp.form.submit('export_roles$add_element') |
|
245 |
resp.form['export_roles$element1'] = role_b.id |
|
246 | ||
247 |
resp.form['statistics_roles$element0'] = role_a.id |
|
248 |
resp = resp.form.submit('submit') |
|
249 | ||
250 |
category = Category.get(category.id) |
|
251 |
assert set(x.id for x in category.export_roles) == {role_a.id, role_b.id} |
|
252 |
assert set(x.id for x in category.statistics_roles) == {role_a.id} |
|
253 | ||
254 |
resp = app.get('/backoffice/forms/categories/1/edit') |
|
255 |
assert resp.form['export_roles$element0'].value == role_a.id |
wcs/admin/categories.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU General Public License |
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
from quixote import get_request, get_response, redirect |
|
17 |
from quixote import get_publisher, get_request, get_response, redirect
|
|
18 | 18 |
from quixote.directory import Directory |
19 | 19 |
from quixote.html import TemplateIO, htmltext |
20 | 20 | |
... | ... | |
23 | 23 |
from wcs.formdef import FormDef |
24 | 24 |
from wcs.qommon import _, template |
25 | 25 |
from wcs.qommon.backoffice.menu import html_top |
26 |
from wcs.qommon.form import Form, HtmlWidget, StringWidget, WysiwygTextWidget
|
|
26 |
from wcs.qommon.form import Form, HtmlWidget, SingleSelectWidget, StringWidget, WidgetList, WysiwygTextWidget
|
|
27 | 27 | |
28 | 28 | |
29 | 29 |
class CategoryUI: |
... | ... | |
34 | 34 |
if self.category is None: |
35 | 35 |
self.category = self.category_class() |
36 | 36 | |
37 |
def get_form(self): |
|
37 |
def get_form(self, new=False):
|
|
38 | 38 |
form = Form(enctype='multipart/form-data') |
39 | 39 |
form.add( |
40 | 40 |
StringWidget, 'name', title=_('Category Name'), required=True, size=30, value=self.category.name |
... | ... | |
47 | 47 |
rows=10, |
48 | 48 |
value=self.category.description, |
49 | 49 |
) |
50 |
if self.category_class == Category:
|
|
50 |
if self.category_class is Category:
|
|
51 | 51 |
form.add( |
52 | 52 |
StringWidget, |
53 | 53 |
'redirect_url', |
... | ... | |
56 | 56 |
hint=_('If set, redirect the site category page to the given URL.'), |
57 | 57 |
value=self.category.redirect_url, |
58 | 58 |
) |
59 | ||
60 |
if not new: |
|
61 |
# include permission fields |
|
62 |
roles = list(get_publisher().role_class.select(order_by='name')) |
|
63 |
form.add( |
|
64 |
WidgetList, |
|
65 |
'export_roles', |
|
66 |
title=_('Export Roles'), |
|
67 |
element_type=SingleSelectWidget, |
|
68 |
value=self.category.export_roles, |
|
69 |
add_element_label=_('Add Role'), |
|
70 |
element_kwargs={ |
|
71 |
'render_br': False, |
|
72 |
'options': [(None, '---', None)] |
|
73 |
+ [(x, x.name, x.id) for x in roles if not x.is_internal()], |
|
74 |
}, |
|
75 |
hint=_('Roles allowed to export data'), |
|
76 |
) |
|
77 |
if self.category_class is Category: |
|
78 |
form.add( |
|
79 |
WidgetList, |
|
80 |
'statistics_roles', |
|
81 |
title=_('Statistics Roles'), |
|
82 |
element_type=SingleSelectWidget, |
|
83 |
value=self.category.statistics_roles, |
|
84 |
add_element_label=_('Add Role'), |
|
85 |
element_kwargs={ |
|
86 |
'render_br': False, |
|
87 |
'options': [(None, '---', None)] |
|
88 |
+ [(x, x.name, x.id) for x in roles if not x.is_internal()], |
|
89 |
}, |
|
90 |
hint=_('Roles with access to the statistics page'), |
|
91 |
) |
|
92 | ||
59 | 93 |
form.add_submit('submit', _('Submit')) |
60 | 94 |
form.add_submit('cancel', _('Cancel')) |
61 | 95 |
return form |
... | ... | |
69 | 103 |
form.get_widget('name').set_error(_('This name is already used')) |
70 | 104 |
raise ValueError() |
71 | 105 | |
72 |
self.category.description = form.get_widget('description').parse() |
|
73 |
if form.get_widget('redirect_url'): |
|
74 |
self.category.redirect_url = form.get_widget('redirect_url').parse() |
|
106 |
for attribute in ('description', 'redirect_url', 'export_roles', 'statistics_roles'): |
|
107 |
widget = form.get_widget(attribute) |
|
108 |
if widget: |
|
109 |
setattr(self.category, attribute, widget.parse()) |
|
110 | ||
75 | 111 |
self.category.store() |
76 | 112 | |
77 | 113 | |
... | ... | |
107 | 143 | |
108 | 144 |
def edit(self): |
109 | 145 |
form = self.category_ui.get_form() |
110 |
if form.get_widget('cancel').parse():
|
|
146 |
if form.get_submit() == 'cancel':
|
|
111 | 147 |
return redirect('..') |
112 | 148 | |
113 |
if form.is_submitted() and not form.has_errors():
|
|
149 |
if form.get_submit() == 'submit' and not form.has_errors():
|
|
114 | 150 |
try: |
115 | 151 |
self.category_ui.submit_form(form) |
116 | 152 |
except ValueError: |
... | ... | |
224 | 260 |
def new(self): |
225 | 261 |
get_response().breadcrumb.append(('new', _('New'))) |
226 | 262 |
category_ui = self.category_ui_class(None) |
227 |
form = category_ui.get_form() |
|
263 |
form = category_ui.get_form(new=True)
|
|
228 | 264 |
if form.get_widget('cancel').parse(): |
229 | 265 |
return redirect('.') |
230 | 266 |
wcs/api_access.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU General Public License |
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import xml.etree.ElementTree as ET |
|
18 | ||
19 |
from quixote import get_publisher |
|
20 | ||
21 |
from wcs.qommon.misc import xml_node_text |
|
22 |
from wcs.qommon.storage import Equal, Or |
|
23 | 17 |
from wcs.qommon.xml_storage import XmlStorableObject |
24 | 18 | |
25 | 19 | |
... | ... | |
60 | 54 |
def get_roles(self): |
61 | 55 |
return self.roles or [] |
62 | 56 | |
63 |
def export_roles_to_xml(self, element, attribute_name, include_id=False, **kwargs): |
|
64 |
for role in self.get_roles(): |
|
65 |
sub = ET.SubElement(element, 'role') |
|
66 |
if include_id: |
|
67 |
sub.attrib['role-id'] = role.id |
|
68 |
sub.attrib['role-slug'] = role.slug |
|
69 |
sub.text = role.name |
|
70 | ||
71 |
def import_roles_from_xml(self, element, include_id=False, **kwargs): |
|
72 |
criterias = [] |
|
73 |
for sub in element: |
|
74 |
if include_id and 'role-id' in sub.attrib: |
|
75 |
criterias.append(Equal('id', sub.attrib['role-id'])) |
|
76 |
elif 'role-slug' in sub.attrib: |
|
77 |
criterias.append(Equal('slug', sub.attrib['role-slug'])) |
|
78 |
else: |
|
79 |
role_name = xml_node_text(sub) |
|
80 |
if role_name: |
|
81 |
criterias.append(Equal('name', role_name)) |
|
82 |
return get_publisher().role_class.select([Or(criterias)], order_by='name') |
|
83 | ||
84 | 57 |
def get_as_api_user(self): |
85 | 58 |
class RestrictedApiUser: |
86 | 59 |
# kept as inner class so cannot be pickled |
wcs/categories.py | ||
---|---|---|
33 | 33 |
position = None |
34 | 34 |
redirect_url = None |
35 | 35 | |
36 |
export_roles = None |
|
37 |
statistics_roles = None |
|
38 | ||
36 | 39 |
# declarations for serialization |
37 | 40 |
XML_NODES = [ |
38 | 41 |
('name', 'str'), |
... | ... | |
40 | 43 |
('description', 'str'), |
41 | 44 |
('redirect_url', 'str'), |
42 | 45 |
('position', 'int'), |
46 |
('export_roles', 'roles'), |
|
47 |
('statistics_roles', 'roles'), |
|
43 | 48 |
] |
44 | 49 | |
45 | 50 |
def __init__(self, name=None): |
... | ... | |
125 | 130 |
xml_root_node = 'carddef_category' |
126 | 131 | |
127 | 132 |
# declarations for serialization |
128 |
XML_NODES = [('name', 'str'), ('url_name', 'str'), ('description', 'str'), ('position', 'int')] |
|
133 |
XML_NODES = [ |
|
134 |
('name', 'str'), |
|
135 |
('url_name', 'str'), |
|
136 |
('description', 'str'), |
|
137 |
('position', 'int'), |
|
138 |
('export_roles', 'roles'), |
|
139 |
] |
|
129 | 140 | |
130 | 141 | |
131 | 142 |
Substitutions.register('category_name', category=_('General'), comment=_('Category Name')) |
wcs/qommon/xml_storage.py | ||
---|---|---|
21 | 21 |
from quixote import get_publisher |
22 | 22 | |
23 | 23 |
from .misc import indent_xml, xml_node_text |
24 |
from .storage import StorableObject |
|
24 |
from .storage import Equal, Or, StorableObject
|
|
25 | 25 | |
26 | 26 | |
27 | 27 |
class XmlStorableObject(StorableObject): |
... | ... | |
75 | 75 |
indent_xml(x) |
76 | 76 |
return ET.tostring(x) |
77 | 77 | |
78 |
def export_roles_to_xml(self, element, attribute_name, include_id=False, **kwargs): |
|
79 |
for role in getattr(self, attribute_name, None) or []: |
|
80 |
sub = ET.SubElement(element, 'role') |
|
81 |
if include_id: |
|
82 |
sub.attrib['role-id'] = role.id |
|
83 |
sub.attrib['role-slug'] = role.slug |
|
84 |
sub.text = role.name |
|
85 | ||
78 | 86 |
@classmethod |
79 | 87 |
def import_from_xml(cls, fd, charset=None, include_id=False): |
80 | 88 |
try: |
... | ... | |
127 | 135 |
for item in element.findall('item'): |
128 | 136 |
value.append(item.text) |
129 | 137 |
return value |
138 | ||
139 |
def import_roles_from_xml(self, element, include_id=False, **kwargs): |
|
140 |
criterias = [] |
|
141 |
for sub in element: |
|
142 |
if include_id and 'role-id' in sub.attrib: |
|
143 |
criterias.append(Equal('id', sub.attrib['role-id'])) |
|
144 |
elif 'role-slug' in sub.attrib: |
|
145 |
criterias.append(Equal('slug', sub.attrib['role-slug'])) |
|
146 |
else: |
|
147 |
role_name = xml_node_text(sub) |
|
148 |
if role_name: |
|
149 |
criterias.append(Equal('name', role_name)) |
|
150 |
return get_publisher().role_class.select([Or(criterias)], order_by='name') |
wcs/templates/wcs/backoffice/category.html | ||
---|---|---|
29 | 29 |
{% endif %} |
30 | 30 |
{% endwith %} |
31 | 31 |
</div> |
32 | ||
33 |
{% if category.export_roles or category.statistics_roles %} |
|
34 |
<div class="section"> |
|
35 |
<h3>{% trans "Permissions" %}</h3> |
|
36 |
<div> |
|
37 |
<ul> |
|
38 |
{% if category.export_roles %} |
|
39 |
<li>{% trans "Export roles:" %} |
|
40 |
<ul> |
|
41 |
{% for role in category.export_roles %}<li>{{ role.name }}</li>{% endfor %} |
|
42 |
</ul> |
|
43 |
</li> |
|
44 |
{% endif %} |
|
45 |
{% if category.statistics_roles %} |
|
46 |
<li>{% trans "Statistics roles:" %} |
|
47 |
<ul> |
|
48 |
{% for role in category.statistics_roles %}<li>{{ role.name }}</li>{% endfor %} |
|
49 |
</ul> |
|
50 |
</li> |
|
51 |
{% endif %} |
|
52 |
</ul> |
|
53 |
</div> |
|
54 |
</div> |
|
55 |
{% endif %} |
|
32 | 56 |
{% endblock %} |
33 |
- |