categories: add new export_roles and statistics_roles attributes (#53667)

This commit is contained in:
Frédéric Péters 2021-05-14 14:55:38 +02:00
parent fe12631493
commit 7225537069
7 changed files with 139 additions and 39 deletions

View File

@ -191,6 +191,8 @@ def test_categories_new_duplicate_name(pub):
def test_categories_reorder(pub):
create_superuser(pub)
CardDefCategory.wipe()
category = CardDefCategory(name='foo')
category.store()

View File

@ -202,6 +202,8 @@ def test_categories_new_duplicate_name(pub):
def test_categories_reorder(pub):
create_superuser(pub)
Category.wipe()
category = Category(name='foo')
category.store()
@ -220,3 +222,34 @@ def test_categories_reorder(pub):
categories = Category.select()
Category.sort_by_position(categories)
assert [x.id for x in categories] == ['3', '1', '2']
def test_categories_edit_roles(pub):
create_superuser(pub)
pub.role_class.wipe()
role_a = pub.role_class(name='a')
role_a.store()
role_b = pub.role_class(name='b')
role_b.store()
Category.wipe()
category = Category(name='foobar')
category.store()
app = login(get_app(pub))
resp = app.get('/backoffice/forms/categories/1/edit')
resp.form['export_roles$element0'] = role_a.id
resp = resp.form.submit('export_roles$add_element')
resp.form['export_roles$element1'] = role_b.id
resp.form['statistics_roles$element0'] = role_a.id
resp = resp.form.submit('submit')
category = Category.get(category.id)
assert set(x.id for x in category.export_roles) == {role_a.id, role_b.id}
assert set(x.id for x in category.statistics_roles) == {role_a.id}
resp = app.get('/backoffice/forms/categories/1/edit')
assert resp.form['export_roles$element0'].value == role_a.id

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
from quixote import get_request, get_response, redirect
from quixote import get_publisher, get_request, get_response, redirect
from quixote.directory import Directory
from quixote.html import TemplateIO, htmltext
@ -23,7 +23,7 @@ from wcs.categories import CardDefCategory, Category
from wcs.formdef import FormDef
from wcs.qommon import _, template
from wcs.qommon.backoffice.menu import html_top
from wcs.qommon.form import Form, HtmlWidget, StringWidget, WysiwygTextWidget
from wcs.qommon.form import Form, HtmlWidget, SingleSelectWidget, StringWidget, WidgetList, WysiwygTextWidget
class CategoryUI:
@ -34,7 +34,7 @@ class CategoryUI:
if self.category is None:
self.category = self.category_class()
def get_form(self):
def get_form(self, new=False):
form = Form(enctype='multipart/form-data')
form.add(
StringWidget, 'name', title=_('Category Name'), required=True, size=30, value=self.category.name
@ -47,7 +47,7 @@ class CategoryUI:
rows=10,
value=self.category.description,
)
if self.category_class == Category:
if self.category_class is Category:
form.add(
StringWidget,
'redirect_url',
@ -56,6 +56,40 @@ class CategoryUI:
hint=_('If set, redirect the site category page to the given URL.'),
value=self.category.redirect_url,
)
if not new:
# include permission fields
roles = list(get_publisher().role_class.select(order_by='name'))
form.add(
WidgetList,
'export_roles',
title=_('Export Roles'),
element_type=SingleSelectWidget,
value=self.category.export_roles,
add_element_label=_('Add Role'),
element_kwargs={
'render_br': False,
'options': [(None, '---', None)]
+ [(x, x.name, x.id) for x in roles if not x.is_internal()],
},
hint=_('Roles allowed to export data'),
)
if self.category_class is Category:
form.add(
WidgetList,
'statistics_roles',
title=_('Statistics Roles'),
element_type=SingleSelectWidget,
value=self.category.statistics_roles,
add_element_label=_('Add Role'),
element_kwargs={
'render_br': False,
'options': [(None, '---', None)]
+ [(x, x.name, x.id) for x in roles if not x.is_internal()],
},
hint=_('Roles with access to the statistics page'),
)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
@ -69,9 +103,11 @@ class CategoryUI:
form.get_widget('name').set_error(_('This name is already used'))
raise ValueError()
self.category.description = form.get_widget('description').parse()
if form.get_widget('redirect_url'):
self.category.redirect_url = form.get_widget('redirect_url').parse()
for attribute in ('description', 'redirect_url', 'export_roles', 'statistics_roles'):
widget = form.get_widget(attribute)
if widget:
setattr(self.category, attribute, widget.parse())
self.category.store()
@ -107,10 +143,10 @@ class CategoryPage(Directory):
def edit(self):
form = self.category_ui.get_form()
if form.get_widget('cancel').parse():
if form.get_submit() == 'cancel':
return redirect('..')
if form.is_submitted() and not form.has_errors():
if form.get_submit() == 'submit' and not form.has_errors():
try:
self.category_ui.submit_form(form)
except ValueError:
@ -224,7 +260,7 @@ class CategoriesDirectory(Directory):
def new(self):
get_response().breadcrumb.append(('new', _('New')))
category_ui = self.category_ui_class(None)
form = category_ui.get_form()
form = category_ui.get_form(new=True)
if form.get_widget('cancel').parse():
return redirect('.')

View File

@ -14,12 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import xml.etree.ElementTree as ET
from quixote import get_publisher
from wcs.qommon.misc import xml_node_text
from wcs.qommon.storage import Equal, Or
from wcs.qommon.xml_storage import XmlStorableObject
@ -60,27 +54,6 @@ class ApiAccess(XmlStorableObject):
def get_roles(self):
return self.roles or []
def export_roles_to_xml(self, element, attribute_name, include_id=False, **kwargs):
for role in self.get_roles():
sub = ET.SubElement(element, 'role')
if include_id:
sub.attrib['role-id'] = role.id
sub.attrib['role-slug'] = role.slug
sub.text = role.name
def import_roles_from_xml(self, element, include_id=False, **kwargs):
criterias = []
for sub in element:
if include_id and 'role-id' in sub.attrib:
criterias.append(Equal('id', sub.attrib['role-id']))
elif 'role-slug' in sub.attrib:
criterias.append(Equal('slug', sub.attrib['role-slug']))
else:
role_name = xml_node_text(sub)
if role_name:
criterias.append(Equal('name', role_name))
return get_publisher().role_class.select([Or(criterias)], order_by='name')
def get_as_api_user(self):
class RestrictedApiUser:
# kept as inner class so cannot be pickled

View File

@ -33,6 +33,9 @@ class Category(XmlStorableObject):
position = None
redirect_url = None
export_roles = None
statistics_roles = None
# declarations for serialization
XML_NODES = [
('name', 'str'),
@ -40,6 +43,8 @@ class Category(XmlStorableObject):
('description', 'str'),
('redirect_url', 'str'),
('position', 'int'),
('export_roles', 'roles'),
('statistics_roles', 'roles'),
]
def __init__(self, name=None):
@ -125,7 +130,13 @@ class CardDefCategory(Category):
xml_root_node = 'carddef_category'
# declarations for serialization
XML_NODES = [('name', 'str'), ('url_name', 'str'), ('description', 'str'), ('position', 'int')]
XML_NODES = [
('name', 'str'),
('url_name', 'str'),
('description', 'str'),
('position', 'int'),
('export_roles', 'roles'),
]
Substitutions.register('category_name', category=_('General'), comment=_('Category Name'))

View File

@ -21,7 +21,7 @@ from django.utils.encoding import force_text
from quixote import get_publisher
from .misc import indent_xml, xml_node_text
from .storage import StorableObject
from .storage import Equal, Or, StorableObject
class XmlStorableObject(StorableObject):
@ -75,6 +75,14 @@ class XmlStorableObject(StorableObject):
indent_xml(x)
return ET.tostring(x)
def export_roles_to_xml(self, element, attribute_name, include_id=False, **kwargs):
for role in getattr(self, attribute_name, None) or []:
sub = ET.SubElement(element, 'role')
if include_id:
sub.attrib['role-id'] = role.id
sub.attrib['role-slug'] = role.slug
sub.text = role.name
@classmethod
def import_from_xml(cls, fd, charset=None, include_id=False):
try:
@ -127,3 +135,16 @@ class XmlStorableObject(StorableObject):
for item in element.findall('item'):
value.append(item.text)
return value
def import_roles_from_xml(self, element, include_id=False, **kwargs):
criterias = []
for sub in element:
if include_id and 'role-id' in sub.attrib:
criterias.append(Equal('id', sub.attrib['role-id']))
elif 'role-slug' in sub.attrib:
criterias.append(Equal('slug', sub.attrib['role-slug']))
else:
role_name = xml_node_text(sub)
if role_name:
criterias.append(Equal('name', role_name))
return get_publisher().role_class.select([Or(criterias)], order_by='name')

View File

@ -29,4 +29,28 @@
{% endif %}
{% endwith %}
</div>
{% if category.export_roles or category.statistics_roles %}
<div class="section">
<h3>{% trans "Permissions" %}</h3>
<div>
<ul>
{% if category.export_roles %}
<li>{% trans "Export roles:" %}
<ul>
{% for role in category.export_roles %}<li>{{ role.name }}</li>{% endfor %}
</ul>
</li>
{% endif %}
{% if category.statistics_roles %}
<li>{% trans "Statistics roles:" %}
<ul>
{% for role in category.statistics_roles %}<li>{{ role.name }}</li>{% endfor %}
</ul>
</li>
{% endif %}
</ul>
</div>
</div>
{% endif %}
{% endblock %}