Projet

Général

Profil

0002-categories-add-new-export_roles-and-statistics_roles.patch

Frédéric Péters, 18 mai 2021 16:13

Télécharger (12,9 ko)

Voir les différences:

Subject: [PATCH 2/3] categories: add new export_roles and statistics_roles
 attributes (#53667)

 tests/admin_pages/test_carddefcategory.py  |  2 +
 tests/admin_pages/test_category.py         | 33 +++++++++++++
 wcs/admin/categories.py                    | 56 ++++++++++++++++++----
 wcs/api_access.py                          | 27 -----------
 wcs/categories.py                          | 13 ++++-
 wcs/qommon/xml_storage.py                  | 23 ++++++++-
 wcs/templates/wcs/backoffice/category.html | 24 ++++++++++
 7 files changed, 139 insertions(+), 39 deletions(-)
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
-