0001-general-add-category-based-management-access-forms-c.patch
tests/admin_pages/test_card.py | ||
---|---|---|
1 | 1 |
import re |
2 |
import xml.etree.ElementTree as ET |
|
2 | 3 | |
3 | 4 |
import pytest |
5 |
from webtest import Upload |
|
4 | 6 | |
5 | 7 |
from wcs import fields |
6 | 8 |
from wcs.admin.settings import UserFieldsFormDef |
... | ... | |
516 | 518 |
user_formdef = UserFieldsFormDef(pub) |
517 | 519 |
user_formdef.fields = [] |
518 | 520 |
user_formdef.store() |
521 | ||
522 | ||
523 |
def test_card_category_management_roles(pub, backoffice_user, backoffice_role): |
|
524 |
app = login(get_app(pub), username='backoffice-user', password='backoffice-user') |
|
525 |
app.get('/backoffice/cards/', status=403) |
|
526 | ||
527 |
CardDefCategory.wipe() |
|
528 |
cat = CardDefCategory(name='Foo') |
|
529 |
cat.store() |
|
530 | ||
531 |
CardDef.wipe() |
|
532 |
carddef = CardDef() |
|
533 |
carddef.name = 'card title' |
|
534 |
carddef.category_id = cat.id |
|
535 |
carddef.fields = [] |
|
536 |
carddef.store() |
|
537 | ||
538 |
cat = CardDefCategory(name='Bar') |
|
539 |
cat.management_roles = [backoffice_role] |
|
540 |
cat.store() |
|
541 | ||
542 |
resp = app.get('/backoffice/cards/') |
|
543 |
assert 'Foo' not in resp.text # not a category managed by user |
|
544 |
assert 'card title' not in resp.text # carddef in that category |
|
545 |
assert 'Bar' not in resp.text # not yet any form in this category |
|
546 | ||
547 |
resp = resp.click('New Card') |
|
548 |
resp.forms[0]['name'] = 'card in category' |
|
549 |
assert len(resp.forms[0]['category_id'].options) == 1 # single option |
|
550 |
assert resp.forms[0]['category_id'].value == cat.id # the category managed by user |
|
551 |
resp = resp.forms[0].submit().follow() |
|
552 |
new_carddef = CardDef.get_by_urlname('card-in-category') |
|
553 | ||
554 |
# check category select only let choose one |
|
555 |
resp = resp.click(href='/category') |
|
556 |
assert len(resp.forms[0]['category_id'].options) == 1 # single option |
|
557 |
assert resp.forms[0]['category_id'].value == cat.id # the category managed by user |
|
558 | ||
559 |
resp = app.get('/backoffice/cards/') |
|
560 |
assert 'Bar' in resp.text # now there's a form in this category |
|
561 |
assert 'card in category' in resp.text |
|
562 | ||
563 |
# no access to subdirectories |
|
564 |
assert 'href="categories/"' not in resp.text |
|
565 |
app.get('/backoffice/cards/categories/', status=403) |
|
566 | ||
567 |
# no import into other category |
|
568 |
carddef_xml = ET.tostring(carddef.export_to_xml(include_id=True)) |
|
569 |
resp = resp.click(href='import') |
|
570 |
resp.forms[0]['file'] = Upload('carddef.wcs', carddef_xml) |
|
571 |
resp = resp.forms[0].submit() |
|
572 |
assert 'Invalid File (unauthorized category)' in resp.text |
|
573 | ||
574 |
# check access to inspect page |
|
575 |
carddef.workflow_roles = {'_viewer': str(backoffice_role.id)} |
|
576 |
carddef.store() |
|
577 | ||
578 |
carddata = carddef.data_class()() |
|
579 |
carddata.just_created() |
|
580 |
carddata.store() |
|
581 | ||
582 |
resp = app.get(carddata.get_backoffice_url()) |
|
583 |
assert 'inspect' not in resp.text |
|
584 |
resp = app.get(carddata.get_backoffice_url() + 'inspect', status=403) |
|
585 | ||
586 |
new_carddef.workflow_roles = {'_viewer': str(backoffice_role.id)} |
|
587 |
new_carddef.store() |
|
588 | ||
589 |
carddata = new_carddef.data_class()() |
|
590 |
carddata.just_created() |
|
591 |
carddata.store() |
|
592 |
resp = app.get(carddata.get_backoffice_url()) |
|
593 |
assert 'inspect' in resp.text |
|
594 |
resp = app.get(carddata.get_backoffice_url() + 'inspect') |
tests/admin_pages/test_form.py | ||
---|---|---|
2611 | 2611 |
assert FormDef.get(1).fields[0].key == 'computed' |
2612 | 2612 |
assert FormDef.get(1).fields[0].label == 'foobar' |
2613 | 2613 |
assert FormDef.get(1).fields[0].varname == 'foobar' |
2614 | ||
2615 | ||
2616 |
def test_form_category_management_roles(pub, backoffice_user, backoffice_role): |
|
2617 |
app = login(get_app(pub), username='backoffice-user', password='backoffice-user') |
|
2618 |
app.get('/backoffice/forms/', status=403) |
|
2619 | ||
2620 |
Category.wipe() |
|
2621 |
cat = Category(name='Foo') |
|
2622 |
cat.store() |
|
2623 | ||
2624 |
FormDef.wipe() |
|
2625 |
formdef = FormDef() |
|
2626 |
formdef.name = 'form title' |
|
2627 |
formdef.category_id = cat.id |
|
2628 |
formdef.fields = [] |
|
2629 |
formdef.store() |
|
2630 | ||
2631 |
cat = Category(name='Bar') |
|
2632 |
cat.management_roles = [backoffice_role] |
|
2633 |
cat.store() |
|
2634 | ||
2635 |
resp = app.get('/backoffice/forms/') |
|
2636 |
assert 'Foo' not in resp.text # not a category managed by user |
|
2637 |
assert 'form title' not in resp.text # formdef in that category |
|
2638 |
assert 'Bar' not in resp.text # not yet any form in this category |
|
2639 | ||
2640 |
app.get('/backoffice/forms/%s/' % formdef.id, status=403) |
|
2641 | ||
2642 |
resp = resp.click('New Form') |
|
2643 |
resp.forms[0]['name'] = 'form in category' |
|
2644 |
assert len(resp.forms[0]['category_id'].options) == 1 # single option |
|
2645 |
assert resp.forms[0]['category_id'].value == cat.id # the category managed by user |
|
2646 |
resp = resp.forms[0].submit().follow() |
|
2647 |
new_formdef = FormDef.get_by_urlname('form-in-category') |
|
2648 | ||
2649 |
# check category select only let choose one |
|
2650 |
resp = resp.click(href='/category') |
|
2651 |
assert len(resp.forms[0]['category_id'].options) == 1 # single option |
|
2652 |
assert resp.forms[0]['category_id'].value == cat.id # the category managed by user |
|
2653 | ||
2654 |
resp = app.get('/backoffice/forms/') |
|
2655 |
assert 'Bar' in resp.text # now there's a form in this category |
|
2656 |
assert 'form in category' in resp.text |
|
2657 | ||
2658 |
# no access to subdirectories |
|
2659 |
assert 'href="categories/"' not in resp.text |
|
2660 |
assert 'href="data-sources/"' not in resp.text |
|
2661 |
assert 'href="blocks/"' not in resp.text |
|
2662 |
app.get('/backoffice/forms/categories/', status=403) |
|
2663 |
app.get('/backoffice/forms/data-sources/', status=403) |
|
2664 |
app.get('/backoffice/forms/blocks/', status=403) |
|
2665 | ||
2666 |
# no import into other category |
|
2667 |
formdef_xml = ET.tostring(formdef.export_to_xml(include_id=True)) |
|
2668 |
resp = resp.click(href='import') |
|
2669 |
resp.forms[0]['file'] = Upload('formdef.wcs', formdef_xml) |
|
2670 |
resp = resp.forms[0].submit() |
|
2671 |
assert 'Invalid File (unauthorized category)' in resp.text |
|
2672 | ||
2673 |
# check access to inspect page |
|
2674 |
formdef.workflow_roles = {'_receiver': int(backoffice_role.id)} |
|
2675 |
formdef.store() |
|
2676 | ||
2677 |
formdata = formdef.data_class()() |
|
2678 |
formdata.just_created() |
|
2679 |
formdata.store() |
|
2680 | ||
2681 |
resp = app.get(formdata.get_backoffice_url()) |
|
2682 |
assert 'inspect' not in resp.text |
|
2683 |
resp = app.get(formdata.get_backoffice_url() + 'inspect', status=403) |
|
2684 | ||
2685 |
new_formdef.workflow_roles = {'_receiver': int(backoffice_role.id)} |
|
2686 |
new_formdef.store() |
|
2687 | ||
2688 |
formdata = new_formdef.data_class()() |
|
2689 |
formdata.just_created() |
|
2690 |
formdata.store() |
|
2691 |
resp = app.get(formdata.get_backoffice_url()) |
|
2692 |
assert 'inspect' in resp.text |
|
2693 |
resp = app.get(formdata.get_backoffice_url() + 'inspect') |
tests/admin_pages/test_workflow.py | ||
---|---|---|
1 | 1 |
import io |
2 | 2 |
import os |
3 | 3 |
import re |
4 |
import xml.etree.ElementTree as ET |
|
4 | 5 |
from unittest import mock |
5 | 6 | |
6 | 7 |
import pytest |
... | ... | |
2683 | 2684 |
resp = app.get('/backoffice/workflows/') |
2684 | 2685 |
assert 'Uncategorised' in resp.text |
2685 | 2686 |
assert 'XcategoryY' in resp.text |
2687 | ||
2688 | ||
2689 |
def test_workflow_category_management_roles(pub, backoffice_user, backoffice_role): |
|
2690 |
app = login(get_app(pub), username='backoffice-user', password='backoffice-user') |
|
2691 |
app.get('/backoffice/workflows/', status=403) |
|
2692 | ||
2693 |
WorkflowCategory.wipe() |
|
2694 |
cat = WorkflowCategory(name='Foo') |
|
2695 |
cat.store() |
|
2696 | ||
2697 |
Workflow.wipe() |
|
2698 |
workflow = Workflow() |
|
2699 |
workflow.name = 'workflow title' |
|
2700 |
workflow.category_id = cat.id |
|
2701 |
workflow.store() |
|
2702 | ||
2703 |
cat = WorkflowCategory(name='Bar') |
|
2704 |
cat.management_roles = [backoffice_role] |
|
2705 |
cat.store() |
|
2706 | ||
2707 |
resp = app.get('/backoffice/workflows/') |
|
2708 |
assert 'Foo' not in resp.text # not a category managed by user |
|
2709 |
assert 'workflow title' not in resp.text # workflow in that category |
|
2710 |
assert 'Bar' not in resp.text # not yet any form in this category |
|
2711 | ||
2712 |
resp = resp.click('New Workflow') |
|
2713 |
resp.forms[0]['name'] = 'workflow in category' |
|
2714 |
assert len(resp.forms[0]['category_id'].options) == 1 # single option |
|
2715 |
assert resp.forms[0]['category_id'].value == cat.id # the category managed by user |
|
2716 |
resp = resp.forms[0].submit().follow() |
|
2717 | ||
2718 |
# check category select only let choose one |
|
2719 |
resp = resp.click(href='category') |
|
2720 |
assert len(resp.forms[0]['category_id'].options) == 1 # single option |
|
2721 |
assert resp.forms[0]['category_id'].value == cat.id # the category managed by user |
|
2722 | ||
2723 |
resp = app.get('/backoffice/workflows/') |
|
2724 |
assert 'Bar' in resp.text # now there's a form in this category |
|
2725 |
assert 'workflow in category' in resp.text |
|
2726 | ||
2727 |
# no access to subdirectories |
|
2728 |
assert 'href="categories/"' not in resp.text |
|
2729 |
assert 'href="data-sources/"' not in resp.text |
|
2730 |
assert 'href="mail-templates/"' not in resp.text |
|
2731 |
app.get('/backoffice/workflows/categories/', status=403) |
|
2732 |
app.get('/backoffice/workflows/data-sources/', status=403) |
|
2733 |
app.get('/backoffice/workflows/mail-templates/', status=403) |
|
2734 | ||
2735 |
# no import into other category |
|
2736 |
workflow_xml = ET.tostring(workflow.export_to_xml(include_id=True)) |
|
2737 |
resp = resp.click(href='import') |
|
2738 |
resp.forms[0]['file'] = Upload('workflow.wcs', workflow_xml) |
|
2739 |
resp = resp.forms[0].submit() |
|
2740 |
assert 'Invalid File (unauthorized category)' in resp.text |
|
2741 | ||
2742 |
# access to default workflows |
|
2743 |
app.get('/backoffice/workflows/_carddef_default/') |
|
2744 |
resp = app.get('/backoffice/workflows/_default/') |
|
2745 | ||
2746 |
# duplicate on default workflows should open a dialog |
|
2747 |
resp = resp.click(href='duplicate') |
|
2748 |
assert len(resp.forms[0]['category_id'].options) == 1 # single option |
|
2749 |
resp = resp.forms[0].submit('cancel').follow() |
|
2750 |
resp = resp.click(href='duplicate') |
|
2751 |
resp = resp.forms[0].submit('submit').follow() |
tests/conftest.py | ||
---|---|---|
4 | 4 | |
5 | 5 |
import pytest |
6 | 6 | |
7 |
from wcs.qommon.ident.password_accounts import PasswordAccount |
|
8 | ||
7 | 9 |
from .utilities import EmailsMocking, HttpRequestsMocking, SMSMocking |
8 | 10 | |
9 | 11 | |
... | ... | |
115 | 117 |
monkeypatch.setattr(psycopg2, 'connect', connect) |
116 | 118 |
yield queries |
117 | 119 |
wcs.sql.cleanup_connection() |
120 | ||
121 | ||
122 |
@pytest.fixture |
|
123 |
def backoffice_role(pub): |
|
124 |
role = pub.role_class.get_on_index('backoffice-role', 'slug', ignore_errors=True) |
|
125 |
if not role: |
|
126 |
role = pub.role_class(name='backoffice role') |
|
127 |
role.allows_backoffice_access = True |
|
128 |
role.store() |
|
129 |
assert role.slug == 'backoffice-role' |
|
130 |
return role |
|
131 | ||
132 | ||
133 |
@pytest.fixture |
|
134 |
def backoffice_user(pub, backoffice_role): |
|
135 |
try: |
|
136 |
user = pub.user_class.get_users_with_email('backoffice-user@example.net')[0] |
|
137 |
except IndexError: |
|
138 |
user = pub.user_class() |
|
139 |
user.name = 'backoffice user' |
|
140 |
user.email = 'backoffice-user@example.net' |
|
141 |
user.roles = [backoffice_role.id] |
|
142 |
user.store() |
|
143 | ||
144 |
account1 = PasswordAccount(id='backoffice-user') |
|
145 |
account1.set_password('backoffice-user') |
|
146 |
account1.user_id = user.id |
|
147 |
account1.store() |
|
148 | ||
149 |
return user |
wcs/admin/blocks.py | ||
---|---|---|
24 | 24 |
from wcs.blocks import BlockDef, BlockdefImportError |
25 | 25 |
from wcs.qommon import _, misc, template |
26 | 26 |
from wcs.qommon.backoffice.menu import html_top |
27 |
from wcs.qommon.errors import TraversalError |
|
27 |
from wcs.qommon.errors import AccessForbiddenError, TraversalError
|
|
28 | 28 |
from wcs.qommon.form import FileWidget, Form, HtmlWidget, StringWidget |
29 | 29 | |
30 | 30 | |
... | ... | |
195 | 195 |
self.section = section |
196 | 196 | |
197 | 197 |
def _q_traverse(self, path): |
198 |
if not get_publisher().get_backoffice_root().is_global_accessible('forms'): |
|
199 |
raise AccessForbiddenError() |
|
198 | 200 |
get_response().breadcrumb.append(('blocks/', _('Fields Blocks'))) |
199 | 201 |
return super()._q_traverse(path) |
200 | 202 |
wcs/admin/categories.py | ||
---|---|---|
23 | 23 |
from wcs.formdef import FormDef |
24 | 24 |
from wcs.qommon import _, misc, template |
25 | 25 |
from wcs.qommon.backoffice.menu import html_top |
26 |
from wcs.qommon.errors import AccessForbiddenError |
|
26 | 27 |
from wcs.qommon.form import Form, HtmlWidget, SingleSelectWidget, StringWidget, WidgetList, WysiwygTextWidget |
27 | 28 |
from wcs.workflows import Workflow |
28 | 29 | |
29 | 30 | |
30 |
def get_categories(category_class): |
|
31 |
t = sorted((misc.simplify(x.name), x.id, x.name, x.id) for x in category_class.select()) |
|
31 |
def get_categories(category_class, filter_function): |
|
32 |
t = sorted( |
|
33 |
(misc.simplify(x.name), x.id, x.name, x.id) for x in category_class.select() if filter_function(x) |
|
34 |
) |
|
32 | 35 |
return [x[1:] for x in t] |
33 | 36 | |
34 | 37 | |
35 | 38 |
class CategoryUI: |
36 | 39 |
category_class = Category |
40 |
management_roles_hint_text = _('Roles allowed to create, edit and delete forms.') |
|
37 | 41 | |
38 | 42 |
def __init__(self, category): |
39 | 43 |
self.category = category |
... | ... | |
66 | 70 |
if not new: |
67 | 71 |
# include permission fields |
68 | 72 |
roles = list(get_publisher().role_class.select(order_by='name')) |
73 |
if 'management_roles' in [x[0] for x in self.category_class.XML_NODES]: |
|
74 |
form.add( |
|
75 |
WidgetList, |
|
76 |
'management_roles', |
|
77 |
title=_('Management Roles'), |
|
78 |
element_type=SingleSelectWidget, |
|
79 |
value=self.category.management_roles, |
|
80 |
add_element_label=_('Add Role'), |
|
81 |
element_kwargs={ |
|
82 |
'render_br': False, |
|
83 |
'options': [(None, '---', None)] |
|
84 |
+ [(x, x.name, x.id) for x in roles if not x.is_internal()], |
|
85 |
}, |
|
86 |
hint=self.management_roles_hint_text, |
|
87 |
) |
|
69 | 88 |
if 'export_roles' in [x[0] for x in self.category_class.XML_NODES]: |
70 | 89 |
form.add( |
71 | 90 |
WidgetList, |
... | ... | |
79 | 98 |
'options': [(None, '---', None)] |
80 | 99 |
+ [(x, x.name, x.id) for x in roles if not x.is_internal()], |
81 | 100 |
}, |
82 |
hint=_('Roles allowed to export data'), |
|
101 |
hint=_('Roles allowed to export data.'),
|
|
83 | 102 |
) |
84 | 103 |
if 'statistics_roles' in [x[0] for x in self.category_class.XML_NODES]: |
85 | 104 |
form.add( |
... | ... | |
94 | 113 |
'options': [(None, '---', None)] |
95 | 114 |
+ [(x, x.name, x.id) for x in roles if not x.is_internal()], |
96 | 115 |
}, |
97 |
hint=_('Roles with access to the statistics page'), |
|
116 |
hint=_('Roles with access to the statistics page.'),
|
|
98 | 117 |
) |
99 | 118 | |
100 | 119 |
form.add_submit('submit', _('Submit')) |
... | ... | |
110 | 129 |
form.get_widget('name').set_error(_('This name is already used')) |
111 | 130 |
raise ValueError() |
112 | 131 | |
113 |
for attribute in ('description', 'redirect_url', 'export_roles', 'statistics_roles'): |
|
132 |
for attribute in ( |
|
133 |
'description', |
|
134 |
'redirect_url', |
|
135 |
'management_roles', |
|
136 |
'export_roles', |
|
137 |
'statistics_roles', |
|
138 |
): |
|
114 | 139 |
widget = form.get_widget(attribute) |
115 | 140 |
if widget: |
116 | 141 |
setattr(self.category, attribute, widget.parse()) |
... | ... | |
120 | 145 | |
121 | 146 |
class CardDefCategoryUI(CategoryUI): |
122 | 147 |
category_class = CardDefCategory |
148 |
management_roles_hint_text = _('Roles allowed to create, edit and delete card models.') |
|
123 | 149 | |
124 | 150 | |
125 | 151 |
class WorkflowCategoryUI(CategoryUI): |
126 | 152 |
category_class = WorkflowCategory |
153 |
management_roles_hint_text = _('Roles allowed to create, edit and delete workflows.') |
|
127 | 154 | |
128 | 155 | |
129 | 156 |
class CategoryPage(Directory): |
... | ... | |
234 | 261 | |
235 | 262 |
class CategoriesDirectory(Directory): |
236 | 263 |
_q_exports = ['', 'new', 'update_order'] |
264 | ||
265 |
base_section = 'forms' |
|
237 | 266 |
category_class = Category |
238 | 267 |
category_ui_class = CategoryUI |
239 | 268 |
category_page_class = CategoryPage |
... | ... | |
301 | 330 |
return self.category_page_class(component) |
302 | 331 | |
303 | 332 |
def _q_traverse(self, path): |
333 |
if not get_publisher().get_backoffice_root().is_global_accessible(self.base_section): |
|
334 |
raise AccessForbiddenError() |
|
304 | 335 |
get_response().breadcrumb.append(('categories/', _('Categories'))) |
305 | 336 |
return super()._q_traverse(path) |
306 | 337 | |
307 | 338 | |
308 | 339 |
class CardDefCategoriesDirectory(CategoriesDirectory): |
340 |
base_section = 'cards' |
|
309 | 341 |
category_class = CardDefCategory |
310 | 342 |
category_ui_class = CardDefCategoryUI |
311 | 343 |
category_page_class = CardDefCategoryPage |
... | ... | |
313 | 345 | |
314 | 346 | |
315 | 347 |
class WorkflowCategoriesDirectory(CategoriesDirectory): |
348 |
base_section = 'workflows' |
|
316 | 349 |
category_class = WorkflowCategory |
317 | 350 |
category_ui_class = WorkflowCategoryUI |
318 | 351 |
category_page_class = WorkflowCategoryPage |
wcs/admin/data_sources.py | ||
---|---|---|
31 | 31 |
from wcs.formdef import get_formdefs_of_all_kinds |
32 | 32 |
from wcs.qommon import _, errors, misc, template |
33 | 33 |
from wcs.qommon.backoffice.menu import html_top |
34 |
from wcs.qommon.errors import AccessForbiddenError |
|
34 | 35 |
from wcs.qommon.form import ( |
35 | 36 |
CheckboxWidget, |
36 | 37 |
DurationWidget, |
... | ... | |
421 | 422 |
] |
422 | 423 | |
423 | 424 |
def _q_traverse(self, path): |
425 |
if ( |
|
426 |
not get_publisher().get_backoffice_root().is_global_accessible('forms') |
|
427 |
and not get_publisher().get_backoffice_root().is_global_accessible('workflows') |
|
428 |
and not get_publisher().get_backoffice_root().is_global_accessible('cards') |
|
429 |
): |
|
430 |
raise AccessForbiddenError() |
|
424 | 431 |
get_response().breadcrumb.append(('data-sources/', _('Data Sources'))) |
425 | 432 |
return super()._q_traverse(path) |
426 | 433 |
wcs/admin/forms.py | ||
---|---|---|
32 | 32 |
from wcs.qommon import _, force_str, get_logger, misc, template |
33 | 33 |
from wcs.qommon.afterjobs import AfterJob |
34 | 34 |
from wcs.qommon.backoffice.menu import html_top |
35 |
from wcs.qommon.errors import TraversalError |
|
35 |
from wcs.qommon.errors import AccessForbiddenError, TraversalError
|
|
36 | 36 |
from wcs.qommon.form import ( |
37 | 37 |
CheckboxesWidget, |
38 | 38 |
CheckboxWidget, |
... | ... | |
62 | 62 |
from .logged_errors import LoggedErrorsDirectory |
63 | 63 | |
64 | 64 | |
65 |
def is_global_accessible(section): |
|
66 |
return get_publisher().get_backoffice_root().is_global_accessible(section) |
|
67 | ||
68 | ||
65 | 69 |
class FormDefUI: |
66 | 70 |
formdef_class = FormDef |
67 | 71 |
category_class = Category |
72 |
section = 'forms' |
|
68 | 73 | |
69 | 74 |
def __init__(self, formdef): |
70 | 75 |
self.formdef = formdef |
71 | 76 | |
72 | 77 |
def get_categories(self): |
73 |
return get_categories(self.category_class) |
|
78 |
global_access = is_global_accessible(self.section) |
|
79 |
user_roles = set(get_request().user.get_roles()) |
|
80 | ||
81 |
def filter_function(category): |
|
82 |
if global_access: |
|
83 |
return True |
|
84 |
management_roles = {x.id for x in getattr(category, 'management_roles') or []} |
|
85 |
return bool(user_roles.intersection(management_roles)) |
|
86 | ||
87 |
return get_categories(self.category_class, filter_function=filter_function) |
|
74 | 88 | |
75 | 89 |
@classmethod |
76 | 90 |
def get_workflows(cls, condition=lambda x: True): |
... | ... | |
87 | 101 |
form.add(StringWidget, 'name', title=_('Name'), required=True, size=40, value=formdef.name) |
88 | 102 |
categories = self.get_categories() |
89 | 103 |
if categories: |
104 |
if is_global_accessible(self.section): |
|
105 |
categories = [(None, '---', '')] + list(categories) |
|
90 | 106 |
form.add( |
91 | 107 |
SingleSelectWidget, |
92 | 108 |
'category_id', |
93 | 109 |
title=_('Category'), |
94 | 110 |
value=formdef.category_id, |
95 |
options=[(None, '---', '')] + categories,
|
|
111 |
options=categories, |
|
96 | 112 |
) |
97 | 113 |
workflows = self.get_workflows() |
98 | 114 |
if len(workflows) > 1: |
... | ... | |
182 | 198 |
class OptionsDirectory(Directory): |
183 | 199 |
category_class = Category |
184 | 200 |
category_empty_choice = _('Select a category for this form') |
201 |
section = 'forms' |
|
202 | ||
185 | 203 |
_q_exports = [ |
186 | 204 |
'confirmation', |
187 | 205 |
'only_allow_one', |
... | ... | |
199 | 217 |
'user_support', |
200 | 218 |
] |
201 | 219 | |
202 |
def __init__(self, formdef): |
|
220 |
def __init__(self, formdef, formdefui):
|
|
203 | 221 |
self.formdef = formdef |
204 | 222 |
self.changed = False |
223 |
self.formdefui = formdefui |
|
205 | 224 | |
206 | 225 |
def confirmation(self): |
207 | 226 |
form = Form(enctype='multipart/form-data') |
... | ... | |
331 | 350 |
return self.handle(form, _('Keywords')) |
332 | 351 | |
333 | 352 |
def category(self): |
334 |
categories = get_categories(self.category_class) |
|
353 |
categories = self.formdefui.get_categories() |
|
354 |
if is_global_accessible(self.section): |
|
355 |
categories = [(None, '---', '')] + list(categories) |
|
335 | 356 |
form = Form(enctype='multipart/form-data') |
336 | 357 |
form.widgets.append(HtmlWidget('<p>%s</p>' % self.category_empty_choice)) |
337 | 358 |
form.add( |
... | ... | |
339 | 360 |
'category_id', |
340 | 361 |
title=_('Category'), |
341 | 362 |
value=self.formdef.category_id, |
342 |
options=[(None, '---', '')] + categories,
|
|
363 |
options=list(categories),
|
|
343 | 364 |
) |
344 | 365 |
return self.handle(form, _('Category')) |
345 | 366 | |
... | ... | |
572 | 593 |
formdef_export_prefix = 'form' |
573 | 594 |
formdef_ui_class = FormDefUI |
574 | 595 |
formdef_default_workflow = '_default' |
596 |
section = 'forms' |
|
575 | 597 |
options_directory_class = OptionsDirectory |
576 | 598 | |
577 | 599 |
delete_message = _('You are about to irrevocably delete this form.') |
... | ... | |
593 | 615 |
self.fields.html_top = self.html_top |
594 | 616 |
self.role = WorkflowRoleDirectory(self.formdef) |
595 | 617 |
self.role.html_top = self.html_top |
596 |
self.options = self.options_directory_class(self.formdef) |
|
618 |
self.options = self.options_directory_class(self.formdef, self.formdefui)
|
|
597 | 619 |
self.logged_errors_dir = LoggedErrorsDirectory( |
598 | 620 |
parent_dir=self, formdef_class=self.formdef_class, formdef_id=self.formdef.id |
599 | 621 |
) |
600 | 622 |
self.snapshots_dir = SnapshotsDirectory(self.formdef) |
601 | 623 | |
602 | 624 |
def html_top(self, title): |
603 |
return html_top('forms', title)
|
|
625 |
return html_top(self.section, title)
|
|
604 | 626 | |
605 | 627 |
def add_option_line(self, link, label, current_value, popup=True): |
606 | 628 |
return htmltext( |
... | ... | |
1672 | 1694 |
formdef_page_class = FormDefPage |
1673 | 1695 |
formdef_ui_class = FormDefUI |
1674 | 1696 | |
1697 |
section = 'forms' |
|
1675 | 1698 |
top_title = _('Forms') |
1676 | 1699 |
import_title = _('Import Form') |
1677 | 1700 |
import_submit_label = _('Import Form') |
... | ... | |
1687 | 1710 |
) |
1688 | 1711 | |
1689 | 1712 |
def html_top(self, title): |
1690 |
return html_top('forms', title)
|
|
1713 |
return html_top(self.section, title)
|
|
1691 | 1714 | |
1692 | 1715 |
def _q_traverse(self, path): |
1693 |
get_response().breadcrumb.append(('forms/', _('Forms')))
|
|
1716 |
get_response().breadcrumb.append(('%s/' % self.section, self.top_title))
|
|
1694 | 1717 |
return super()._q_traverse(path) |
1695 | 1718 | |
1719 |
def is_accessible(self, user): |
|
1720 |
if is_global_accessible(self.section): |
|
1721 |
return True |
|
1722 | ||
1723 |
# check for access to specific categories |
|
1724 |
user_roles = set(user.get_roles()) |
|
1725 |
for category in self.category_class.select(): |
|
1726 |
management_roles = {x.id for x in getattr(category, 'management_roles') or []} |
|
1727 |
if management_roles and user_roles.intersection(management_roles): |
|
1728 |
return True |
|
1729 | ||
1730 |
return False |
|
1731 | ||
1696 | 1732 |
def _q_index(self): |
1697 | 1733 |
self.html_top(title=self.top_title) |
1698 | 1734 |
r = TemplateIO(html=True) |
1699 | 1735 |
get_response().add_javascript(['jquery.js', 'widget_list.js']) |
1700 | 1736 |
r += self.form_actions() |
1701 | 1737 | |
1738 |
global_access = is_global_accessible(self.section) |
|
1739 | ||
1702 | 1740 |
cats = self.category_class.select() |
1703 | 1741 |
self.category_class.sort_by_position(cats) |
1704 | 1742 |
one = False |
1705 | 1743 |
formdefs = self.formdef_class.select(order_by='name', ignore_errors=True, lightweight=True) |
1706 | 1744 |
for c in cats: |
1745 |
if not global_access: |
|
1746 |
user_roles = set(get_request().user.get_roles()) |
|
1747 |
management_roles = {x.id for x in getattr(c, 'management_roles') or []} |
|
1748 |
if not user_roles.intersection(management_roles): |
|
1749 |
continue |
|
1707 | 1750 |
l2 = [x for x in formdefs if str(x.category_id) == str(c.id)] |
1708 | 1751 |
l2 = [x for x in l2 if not x.disabled or (x.disabled and x.disabled_redirection)] + [ |
1709 | 1752 |
x for x in l2 if x.disabled and not x.disabled_redirection |
... | ... | |
1712 | 1755 |
r += self.form_list(l2, title=c.name) |
1713 | 1756 |
one = True |
1714 | 1757 | |
1715 |
l2 = [x for x in formdefs if not x.category] |
|
1716 |
if l2: |
|
1717 |
if one: |
|
1718 |
title = _('Misc') |
|
1719 |
else: |
|
1720 |
title = None |
|
1721 |
l2 = [x for x in l2 if not x.disabled or (x.disabled and x.disabled_redirection)] + [ |
|
1722 |
x for x in l2 if x.disabled and not x.disabled_redirection |
|
1723 |
] |
|
1724 |
r += self.form_list(l2, title=title) |
|
1758 |
if global_access: |
|
1759 |
l2 = [x for x in formdefs if not x.category] |
|
1760 |
if l2: |
|
1761 |
if one: |
|
1762 |
title = _('Misc') |
|
1763 |
else: |
|
1764 |
title = None |
|
1765 |
l2 = [x for x in l2 if not x.disabled or (x.disabled and x.disabled_redirection)] + [ |
|
1766 |
x for x in l2 if x.disabled and not x.disabled_redirection |
|
1767 |
] |
|
1768 |
r += self.form_list(l2, title=title) |
|
1725 | 1769 |
return r.getvalue() |
1726 | 1770 | |
1727 | 1771 |
def form_actions(self): |
... | ... | |
1731 | 1775 |
r += htmltext('<h2>%s</h2>') % _('Forms') |
1732 | 1776 |
if has_roles: |
1733 | 1777 |
r += htmltext('<span class="actions">') |
1734 |
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources') |
|
1735 |
if get_publisher().has_site_option('fields-blocks'): |
|
1736 |
r += htmltext('<a href="blocks/">%s</a>') % _('Fields blocks') |
|
1737 |
if get_publisher().get_backoffice_root().is_accessible('categories'): |
|
1738 |
r += htmltext('<a href="categories/">%s</a>') % _('Categories') |
|
1778 |
if is_global_accessible('forms'): |
|
1779 |
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources') |
|
1780 |
if get_publisher().has_site_option('fields-blocks'): |
|
1781 |
r += htmltext('<a href="blocks/">%s</a>') % _('Fields blocks') |
|
1782 |
if get_publisher().get_backoffice_root().is_accessible('categories'): |
|
1783 |
r += htmltext('<a href="categories/">%s</a>') % _('Categories') |
|
1739 | 1784 |
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import') |
1740 | 1785 |
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Form') |
1741 | 1786 |
r += htmltext('</span>') |
... | ... | |
1768 | 1813 |
def new(self): |
1769 | 1814 |
get_response().breadcrumb.append(('new', _('New'))) |
1770 | 1815 |
if get_publisher().role_class.count() == 0: |
1771 |
return template.error_page('forms', _('You first have to define roles.'))
|
|
1816 |
return template.error_page(self.section, _('You first have to define roles.'))
|
|
1772 | 1817 |
formdefui = self.formdef_ui_class(None) |
1773 | 1818 |
form = formdefui.new_form_ui() |
1774 | 1819 |
if form.get_widget('cancel').parse(): |
... | ... | |
1791 | 1836 |
return r.getvalue() |
1792 | 1837 | |
1793 | 1838 |
def _q_lookup(self, component): |
1794 |
return self.formdef_page_class(component) |
|
1839 |
directory = self.formdef_page_class(component) |
|
1840 |
global_access = is_global_accessible(self.section) |
|
1841 |
if not global_access: |
|
1842 |
user_roles = set(get_request().user.get_roles()) |
|
1843 |
management_roles = set() |
|
1844 |
if directory.formdef.category: |
|
1845 |
management_roles = { |
|
1846 |
x.id for x in getattr(directory.formdef.category, 'management_roles') or [] |
|
1847 |
} |
|
1848 |
if not management_roles.intersection(user_roles): |
|
1849 |
raise AccessForbiddenError() |
|
1850 |
return directory |
|
1795 | 1851 | |
1796 | 1852 |
def p_import(self): |
1797 | 1853 |
form = Form(enctype='multipart/form-data') |
... | ... | |
1850 | 1906 |
except ValueError: |
1851 | 1907 |
error = True |
1852 | 1908 | |
1909 |
global_access = is_global_accessible(self.section) |
|
1910 |
if not global_access: |
|
1911 |
management_roles = {x.id for x in getattr(formdef.category, 'management_roles', None) or []} |
|
1912 |
user_roles = set(get_request().user.get_roles()) |
|
1913 |
if not user_roles.intersection(management_roles): |
|
1914 |
error = True |
|
1915 |
reason = _('unauthorized category') |
|
1916 | ||
1853 | 1917 |
if error: |
1854 | 1918 |
if reason: |
1855 | 1919 |
msg = _('Invalid File (%s)') % reason |
wcs/admin/mail_templates.py | ||
---|---|---|
40 | 40 |
do_not_call_in_templates = True |
41 | 41 | |
42 | 42 |
def _q_traverse(self, path): |
43 |
if not get_publisher().get_backoffice_root().is_global_accessible('workflows'): |
|
44 |
raise errors.AccessForbiddenError() |
|
43 | 45 |
get_response().breadcrumb.append(('mail-templates/', _('Mail Templates'))) |
44 | 46 |
return super()._q_traverse(path) |
45 | 47 |
wcs/admin/workflows.py | ||
---|---|---|
66 | 66 |
from .mail_templates import MailTemplatesDirectory |
67 | 67 | |
68 | 68 | |
69 |
def is_global_accessible(): |
|
70 |
return get_publisher().get_backoffice_root().is_global_accessible('workflows') |
|
71 | ||
72 | ||
69 | 73 |
def svg(tag): |
70 | 74 |
return '{http://www.w3.org/2000/svg}%s' % tag |
71 | 75 | |
... | ... | |
295 | 299 |
def __init__(self, workflow): |
296 | 300 |
self.workflow = workflow |
297 | 301 | |
302 |
def get_categories(self): |
|
303 |
global_access = is_global_accessible() |
|
304 |
user_roles = set(get_request().user.get_roles()) |
|
305 | ||
306 |
def filter_function(category): |
|
307 |
if global_access: |
|
308 |
return True |
|
309 |
management_roles = {x.id for x in getattr(category, 'management_roles') or []} |
|
310 |
return bool(user_roles.intersection(management_roles)) |
|
311 | ||
312 |
return get_categories(WorkflowCategory, filter_function=filter_function) |
|
313 | ||
298 | 314 |
def form_new(self): |
299 | 315 |
form = Form(enctype='multipart/form-data') |
300 | 316 |
form.add(StringWidget, 'name', title=_('Workflow Name'), required=True, size=30) |
301 |
category_options = get_categories(WorkflowCategory)
|
|
317 |
category_options = self.get_categories()
|
|
302 | 318 |
if category_options: |
319 |
if is_global_accessible(): |
|
320 |
category_options = [(None, '---', '')] + list(category_options) |
|
303 | 321 |
form.add( |
304 | 322 |
SingleSelectWidget, |
305 | 323 |
'category_id', |
306 | 324 |
title=_('Category'), |
307 |
options=[(None, '---', '')] + category_options,
|
|
325 |
options=category_options, |
|
308 | 326 |
) |
309 | 327 |
form.add_submit('submit', _('Submit')) |
310 | 328 |
form.add_submit('cancel', _('Cancel')) |
... | ... | |
332 | 350 |
form.get_widget('name').set_error(_('This name is already used')) |
333 | 351 |
raise ValueError() |
334 | 352 | |
335 |
for f in ('name',): |
|
336 |
setattr(workflow, f, form.get_widget(f).parse()) |
|
353 |
for f in ('name', 'category_id'): |
|
354 |
widget = form.get_widget(f) |
|
355 |
if widget: |
|
356 |
setattr(workflow, f, widget.parse()) |
|
337 | 357 |
workflow.store() |
338 | 358 |
return workflow |
339 | 359 | |
... | ... | |
1487 | 1507 |
return html_top('workflows', title) |
1488 | 1508 | |
1489 | 1509 |
def category(self): |
1490 |
categories = sorted((misc.simplify(x.name), x.id, x.name, x.id) for x in WorkflowCategory.select()) |
|
1510 |
category_options = self.workflow_ui.get_categories() |
|
1511 |
if is_global_accessible(): |
|
1512 |
category_options = [(None, '---', '')] + list(category_options) |
|
1491 | 1513 | |
1492 | 1514 |
form = Form(enctype='multipart/form-data') |
1493 | 1515 |
form.widgets.append(HtmlWidget('<p>%s</p>' % _('Select a category for this workflow.'))) |
... | ... | |
1496 | 1518 |
'category_id', |
1497 | 1519 |
title=_('Category'), |
1498 | 1520 |
value=self.workflow.category_id, |
1499 |
options=[(None, '---', '')] + [x[1:] for x in categories],
|
|
1521 |
options=category_options,
|
|
1500 | 1522 |
) |
1501 | 1523 | |
1502 | 1524 |
if not self.workflow.is_readonly(): |
... | ... | |
1560 | 1582 |
r += htmltext('<ul id="sidebar-actions">') |
1561 | 1583 |
if not self.workflow.is_readonly(): |
1562 | 1584 |
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete') |
1563 |
r += htmltext('<li><a href="duplicate">%s</a></li>') % _('Duplicate') |
|
1585 |
if not is_global_accessible() and self.workflow.id in ('_default', '_carddef_default'): |
|
1586 |
r += htmltext('<li><a rel="popup" href="duplicate">%s</a></li>') % _('Duplicate') |
|
1587 |
else: |
|
1588 |
r += htmltext('<li><a href="duplicate">%s</a></li>') % _('Duplicate') |
|
1564 | 1589 |
r += htmltext('<li><a href="export">%s</a></li>') % _('Export') |
1565 | 1590 |
if get_publisher().snapshot_class: |
1566 | 1591 |
r += htmltext('<li><a rel="popup" href="history/save">%s</a></li>') % _('Save snapshot') |
... | ... | |
1746 | 1771 | |
1747 | 1772 |
return redirect('.') |
1748 | 1773 | |
1749 |
def edit(self, duplicate=False):
|
|
1774 |
def edit(self): |
|
1750 | 1775 |
form = self.workflow_ui.form_edit() |
1751 | 1776 |
if form.get_widget('cancel').parse(): |
1752 | 1777 |
return redirect('.') |
... | ... | |
1761 | 1786 | |
1762 | 1787 |
self.html_top(title=_('Edit Workflow')) |
1763 | 1788 |
r = TemplateIO(html=True) |
1764 |
if duplicate: |
|
1765 |
get_response().breadcrumb.append(('edit', _('Duplicate'))) |
|
1766 |
r += htmltext('<h2>%s</h2>') % _('Duplicate Workflow') |
|
1767 |
else: |
|
1768 |
get_response().breadcrumb.append(('edit', _('Edit'))) |
|
1769 |
r += htmltext('<h2>%s</h2>') % _('Edit Workflow') |
|
1789 |
get_response().breadcrumb.append(('edit', _('Edit'))) |
|
1790 |
r += htmltext('<h2>%s</h2>') % _('Edit Workflow') |
|
1770 | 1791 |
r += form.render() |
1771 | 1792 |
return r.getvalue() |
1772 | 1793 | |
... | ... | |
1801 | 1822 |
return redirect('..') |
1802 | 1823 | |
1803 | 1824 |
def duplicate(self): |
1825 |
if not is_global_accessible() and self.workflow.id in ('_default', '_carddef_default'): |
|
1826 |
category_options = self.workflow_ui.get_categories() |
|
1827 |
form = Form(enctype='multipart/form-data') |
|
1828 |
form.widgets.append(HtmlWidget('<p>%s</p>' % _('Select a category for this workflow.'))) |
|
1829 |
form.add( |
|
1830 |
SingleSelectWidget, |
|
1831 |
'category_id', |
|
1832 |
title=_('Category'), |
|
1833 |
options=category_options, |
|
1834 |
) |
|
1835 |
form.add_submit('submit', _('Submit')) |
|
1836 |
form.add_submit('cancel', _('Cancel')) |
|
1837 |
if form.get_widget('cancel').parse(): |
|
1838 |
return redirect('.') |
|
1839 | ||
1840 |
if not form.is_submitted() or form.has_errors(): |
|
1841 |
self.html_top(title=_('Duplicate Workflow')) |
|
1842 |
r = TemplateIO(html=True) |
|
1843 |
get_response().breadcrumb.append(('duplicate', _('Duplicate'))) |
|
1844 |
r += htmltext('<h2>%s</h2>') % _('Duplicate Workflow') |
|
1845 |
r += form.render() |
|
1846 |
return r.getvalue() |
|
1847 | ||
1848 |
self.workflow_ui.workflow.category_id = form.get_widget('category_id').parse() |
|
1849 | ||
1804 | 1850 |
self.workflow_ui.workflow.id = None |
1805 | 1851 |
original_name = self.workflow_ui.workflow.name |
1806 | 1852 |
self.workflow_ui.workflow.name = '%s %s' % (self.workflow_ui.workflow.name, _('(copy)')) |
... | ... | |
1832 | 1878 | |
1833 | 1879 |
data_sources = NamedDataSourcesDirectoryInWorkflows() |
1834 | 1880 |
mail_templates = MailTemplatesDirectory() |
1835 |
category_class = WorkflowCategory |
|
1836 | 1881 |
categories = WorkflowCategoriesDirectory() |
1837 | 1882 | |
1838 | 1883 |
def html_top(self, title): |
... | ... | |
1842 | 1887 |
get_response().breadcrumb.append(('workflows/', _('Workflows'))) |
1843 | 1888 |
return super()._q_traverse(path) |
1844 | 1889 | |
1890 |
def is_accessible(self, user): |
|
1891 |
if is_global_accessible(): |
|
1892 |
return True |
|
1893 | ||
1894 |
# check for access to specific categories |
|
1895 |
user_roles = set(user.get_roles()) |
|
1896 |
for category in WorkflowCategory.select(): |
|
1897 |
management_roles = {x.id for x in getattr(category, 'management_roles') or []} |
|
1898 |
if management_roles and user_roles.intersection(management_roles): |
|
1899 |
return True |
|
1900 | ||
1901 |
return False |
|
1902 | ||
1845 | 1903 |
def _q_index(self): |
1846 | 1904 |
self.html_top(title=_('Workflows')) |
1847 | 1905 |
r = TemplateIO(html=True) |
... | ... | |
1849 | 1907 |
r += htmltext('<div id="appbar">') |
1850 | 1908 |
r += htmltext('<h2>%s</h2>') % _('Workflows') |
1851 | 1909 |
r += htmltext('<span class="actions">') |
1852 |
if get_publisher().has_site_option('mail-templates'):
|
|
1853 |
r += htmltext('<a href="mail-templates/">%s</a>') % _('Mail Templates')
|
|
1854 |
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources')
|
|
1855 |
if get_publisher().get_backoffice_root().is_accessible('categories'):
|
|
1910 |
if is_global_accessible():
|
|
1911 |
if get_publisher().has_site_option('mail-templates'):
|
|
1912 |
r += htmltext('<a href="mail-templates/">%s</a>') % _('Mail Templates')
|
|
1913 |
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources')
|
|
1856 | 1914 |
r += htmltext('<a href="categories/">%s</a>') % _('Categories') |
1857 | 1915 |
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import') |
1858 | 1916 |
r += htmltext('<a class="new-item" rel="popup" href="new">%s</a>') % _('New Workflow') |
... | ... | |
1889 | 1947 |
else: |
1890 | 1948 |
unused_workflows.append(workflow) |
1891 | 1949 | |
1892 |
categories = sorted(WorkflowCategory.select(), key=lambda x: misc.simplify(x.name)) |
|
1950 |
if is_global_accessible(): |
|
1951 |
categories = WorkflowCategory.select() |
|
1952 |
else: |
|
1953 |
categories = [] |
|
1954 |
user_roles = set(get_request().user.get_roles()) |
|
1955 |
for category in WorkflowCategory.select(): |
|
1956 |
management_roles = {x.id for x in getattr(category, 'management_roles') or []} |
|
1957 |
if management_roles and user_roles.intersection(management_roles): |
|
1958 |
categories.append(category) |
|
1959 | ||
1960 |
categories.sort(key=lambda x: misc.simplify(x.name)) |
|
1893 | 1961 | |
1894 | 1962 |
if categories: |
1895 | 1963 |
default_category = WorkflowCategory('Default') |
... | ... | |
1899 | 1967 |
workflow.category_id = default_category.id |
1900 | 1968 |
categories = [default_category] + categories |
1901 | 1969 | |
1970 |
if is_global_accessible(): |
|
1971 |
categories = categories + [None] |
|
1972 | ||
1902 | 1973 |
def workflow_section(r, workflows): |
1903 | 1974 |
r += htmltext('<ul class="objects-list single-links">') |
1904 | 1975 |
for workflow in workflows: |
... | ... | |
1924 | 1995 |
r += htmltext('</li>') |
1925 | 1996 |
r += htmltext('</ul>') |
1926 | 1997 | |
1927 |
for category in categories + [None]:
|
|
1998 |
for category in categories: |
|
1928 | 1999 |
if category is None: |
1929 | 2000 |
category_workflows = [x for x in workflows + unused_workflows if not x.category_id] |
1930 | 2001 |
else: |
... | ... | |
1969 | 2040 |
return r.getvalue() |
1970 | 2041 | |
1971 | 2042 |
def _q_lookup(self, component): |
1972 |
return WorkflowPage(component) |
|
2043 |
directory = WorkflowPage(component) |
|
2044 |
global_access = is_global_accessible() |
|
2045 |
if directory.workflow.id not in ('_default', '_carddef_default') and not global_access: |
|
2046 |
user_roles = set(get_request().user.get_roles()) |
|
2047 |
management_roles = set() |
|
2048 |
if directory.workflow.category: |
|
2049 |
management_roles = { |
|
2050 |
x.id for x in getattr(directory.workflow.category, 'management_roles') or [] |
|
2051 |
} |
|
2052 |
if not management_roles.intersection(user_roles): |
|
2053 |
raise errors.AccessForbiddenError() |
|
2054 |
return directory |
|
1973 | 2055 | |
1974 | 2056 |
def p_import(self): |
1975 | 2057 |
form = Form(enctype='multipart/form-data') |
... | ... | |
2023 | 2105 |
except ValueError: |
2024 | 2106 |
error = True |
2025 | 2107 | |
2108 |
global_access = is_global_accessible() |
|
2109 |
if not global_access: |
|
2110 |
management_roles = {x.id for x in getattr(workflow.category, 'management_roles', None) or []} |
|
2111 |
user_roles = set(get_request().user.get_roles()) |
|
2112 |
if not user_roles.intersection(management_roles): |
|
2113 |
error = True |
|
2114 |
reason = _('unauthorized category') |
|
2115 | ||
2026 | 2116 |
if error: |
2027 | 2117 |
if reason: |
2028 | 2118 |
msg = _('Invalid File (%s)') % reason |
wcs/backoffice/cards.py | ||
---|---|---|
15 | 15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 | 17 |
from quixote import get_publisher, get_response, get_session, redirect |
18 |
from quixote.directory import Directory |
|
19 | 18 |
from quixote.html import TemplateIO, htmltext |
20 | 19 | |
21 | 20 |
from wcs.admin import utils |
22 | 21 |
from wcs.admin.categories import CardDefCategoriesDirectory |
23 |
from wcs.admin.forms import FormDefPage, FormDefUI, FormsDirectory, OptionsDirectory, html_top
|
|
22 |
from wcs.admin.forms import FormDefPage, FormDefUI, FormsDirectory, OptionsDirectory |
|
24 | 23 |
from wcs.admin.logged_errors import LoggedErrorsDirectory |
25 | 24 |
from wcs.carddef import CardDef |
26 | 25 |
from wcs.categories import CardDefCategory |
... | ... | |
34 | 33 |
class CardDefUI(FormDefUI): |
35 | 34 |
formdef_class = CardDef |
36 | 35 |
category_class = CardDefCategory |
36 |
section = 'cards' |
|
37 | 37 | |
38 | 38 | |
39 | 39 |
class CardDefOptionsDirectory(OptionsDirectory): |
40 | 40 |
category_class = CardDefCategory |
41 | 41 |
category_empty_choice = _('Select a category for this card model') |
42 |
section = 'cards' |
|
42 | 43 | |
43 | 44 | |
44 | 45 |
class CardDefPage(FormDefPage): |
... | ... | |
46 | 47 |
formdef_export_prefix = 'card' |
47 | 48 |
formdef_ui_class = CardDefUI |
48 | 49 |
formdef_default_workflow = '_carddef_default' |
50 |
section = 'cards' |
|
49 | 51 | |
50 | 52 |
options_directory_class = CardDefOptionsDirectory |
51 | 53 | |
... | ... | |
59 | 61 |
'Do note it kept its existing address and role and workflow parameters.' |
60 | 62 |
) |
61 | 63 | |
62 |
def html_top(self, title): |
|
63 |
return html_top('cards', title) |
|
64 | ||
65 | 64 |
def _q_index(self): |
66 | 65 |
self.html_top(title=self.formdef.name) |
67 | 66 |
r = TemplateIO(html=True) |
... | ... | |
248 | 247 |
formdef_page_class = CardDefPage |
249 | 248 |
formdef_ui_class = CardDefUI |
250 | 249 | |
250 |
section = 'cards' |
|
251 | 251 |
top_title = _('Card Models') |
252 | 252 |
import_title = _('Import Card Model') |
253 | 253 |
import_submit_label = _('Import Card Model') |
... | ... | |
261 | 261 |
'you should nevertheless check everything is ok. ' |
262 | 262 |
) |
263 | 263 | |
264 |
def html_top(self, title): |
|
265 |
return html_top('cards', title) |
|
266 | ||
267 |
def _q_traverse(self, path): |
|
268 |
get_response().breadcrumb.append(('cards/', _('Card Models'))) |
|
269 |
return Directory._q_traverse(self, path) |
|
270 | ||
271 | 264 |
def form_actions(self): |
272 | 265 |
r = TemplateIO(html=True) |
273 | 266 |
r += htmltext('<div id="appbar">') |
274 | 267 |
r += htmltext('<h2>%s</h2>') % _('Card Models') |
275 | 268 |
r += htmltext('<span class="actions">') |
276 |
r += htmltext('<a href="../forms/data-sources/">%s</a>') % _('Data sources') |
|
277 |
if get_publisher().has_site_option('fields-blocks'): |
|
278 |
r += htmltext('<a href="../forms/blocks/">%s</a>') % _('Fields blocks') |
|
279 |
r += htmltext('<a href="categories/">%s</a>') % _('Categories') |
|
269 |
if get_publisher().get_backoffice_root().is_global_accessible('forms'): |
|
270 |
r += htmltext('<a href="../forms/data-sources/">%s</a>') % _('Data sources') |
|
271 |
if get_publisher().has_site_option('fields-blocks'): |
|
272 |
r += htmltext('<a href="../forms/blocks/">%s</a>') % _('Fields blocks') |
|
273 |
if get_publisher().get_backoffice_root().is_global_accessible('cards'): |
|
274 |
r += htmltext('<a href="categories/">%s</a>') % _('Categories') |
|
280 | 275 |
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import') |
281 | 276 |
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Card Model') |
282 | 277 |
r += htmltext('</span>') |
wcs/backoffice/management.py | ||
---|---|---|
2905 | 2905 | |
2906 | 2906 |
return response |
2907 | 2907 | |
2908 |
def can_go_in_inspector(self): |
|
2909 |
if get_publisher().get_backoffice_root().is_global_accessible('worflows'): |
|
2910 |
return True |
|
2911 |
if ( |
|
2912 |
get_publisher() |
|
2913 |
.get_backoffice_root() |
|
2914 |
.is_global_accessible(self.formdata.formdef.backoffice_section) |
|
2915 |
): |
|
2916 |
return True |
|
2917 | ||
2918 |
user_roles = set(get_request().user.get_roles()) |
|
2919 |
for category in (self.formdata.formdef.category, self.formdata.formdef.workflow.category): |
|
2920 |
if not category: |
|
2921 |
continue |
|
2922 |
management_roles = {x.id for x in getattr(category, 'management_roles') or []} |
|
2923 |
if user_roles.intersection(management_roles): |
|
2924 |
return True |
|
2925 |
return False |
|
2926 | ||
2908 | 2927 |
def get_extra_context_bar(self, parent=None): |
2909 | 2928 |
formdata = self.filled |
2910 | 2929 | |
... | ... | |
3014 | 3033 |
'<div data-async-url="%suser-pending-forms"></div>' % formdata.get_url(backoffice=True) |
3015 | 3034 |
) |
3016 | 3035 | |
3017 |
if not formdata.is_draft() and ( |
|
3018 |
get_publisher().get_backoffice_root().is_accessible('forms') |
|
3019 |
or get_publisher().get_backoffice_root().is_accessible('workflows') |
|
3020 |
): |
|
3036 |
if not formdata.is_draft() and self.can_go_in_inspector(): |
|
3021 | 3037 |
r += htmltext('<div class="extra-context">') |
3022 | 3038 |
r += htmltext('<p><a href="%sinspect">' % formdata.get_url(backoffice=True)) |
3023 | 3039 |
r += htmltext('%s</a></p>') % _('Data Inspector') |
... | ... | |
3393 | 3409 |
return r.getvalue() |
3394 | 3410 | |
3395 | 3411 |
def inspect(self): |
3396 |
if not ( |
|
3397 |
get_publisher().get_backoffice_root().is_accessible('forms') |
|
3398 |
or get_publisher().get_backoffice_root().is_accessible('workflows') |
|
3399 |
): |
|
3412 |
if not self.can_go_in_inspector(): |
|
3400 | 3413 |
raise errors.AccessForbiddenError() |
3401 | 3414 |
charset = get_publisher().site_charset |
3402 | 3415 |
get_response().breadcrumb.append(('inspect', _('Data Inspector'))) |
wcs/backoffice/root.py | ||
---|---|---|
98 | 98 |
return subdirectory in ('settings', 'users') |
99 | 99 |
return False |
100 | 100 | |
101 |
# if the directory defines a is_accessible method, use it. |
|
102 |
if hasattr(getattr(cls, subdirectory, None), 'is_accessible'): |
|
103 |
return getattr(cls, subdirectory).is_accessible(get_request().user) |
|
104 | ||
105 |
return cls.is_global_accessible(subdirectory) |
|
106 | ||
107 |
@classmethod |
|
108 |
def is_global_accessible(cls, subdirectory): |
|
109 |
if cls.check_admin_for_all(): |
|
110 |
return True |
|
101 | 111 |
user_roles = set(get_request().user.get_roles()) |
102 | 112 |
authorised_roles = set(get_cfg('admin-permissions', {}).get(subdirectory) or []) |
103 | 113 |
if authorised_roles: |
104 | 114 |
# access is governed by roles set in the settings panel |
105 | 115 |
return user_roles.intersection(authorised_roles) |
106 | 116 | |
107 |
# if the directory defines a is_accessible method, use it. |
|
108 |
if hasattr(getattr(cls, subdirectory, None), 'is_accessible'): |
|
109 |
return getattr(cls, subdirectory).is_accessible(get_request().user) |
|
110 | ||
111 | 117 |
# as a last resort, for the other directories, the user needs to be |
112 | 118 |
# marked as admin |
113 | 119 |
return get_request().user.can_go_in_admin() |
114 | 120 | |
115 |
def check_admin_for_all(self): |
|
121 |
@classmethod |
|
122 |
def check_admin_for_all(cls): |
|
116 | 123 |
admin_for_all_file_path = os.path.join(get_publisher().app_dir, 'ADMIN_FOR_ALL') |
117 | 124 |
if not os.path.exists(os.path.join(admin_for_all_file_path)): |
118 | 125 |
return False |
wcs/carddef.py | ||
---|---|---|
33 | 33 | |
34 | 34 |
class CardDef(FormDef): |
35 | 35 |
_names = 'carddefs' |
36 |
backoffice_section = 'cards' |
|
36 | 37 |
data_sql_prefix = 'carddata' |
37 | 38 |
pickle_module_name = 'carddef' |
38 | 39 |
xml_root_node = 'carddef' |
wcs/categories.py | ||
---|---|---|
35 | 35 | |
36 | 36 |
export_roles = None |
37 | 37 |
statistics_roles = None |
38 |
management_roles = None |
|
38 | 39 | |
39 | 40 |
# declarations for serialization |
40 | 41 |
XML_NODES = [ |
... | ... | |
45 | 46 |
('position', 'int'), |
46 | 47 |
('export_roles', 'roles'), |
47 | 48 |
('statistics_roles', 'roles'), |
49 |
('management_roles', 'roles'), |
|
48 | 50 |
] |
49 | 51 | |
50 | 52 |
def __init__(self, name=None): |
... | ... | |
149 | 151 |
('description', 'str'), |
150 | 152 |
('position', 'int'), |
151 | 153 |
('export_roles', 'roles'), |
154 |
('management_roles', 'roles'), |
|
152 | 155 |
] |
153 | 156 | |
154 | 157 |
@classmethod |
... | ... | |
167 | 170 |
('name', 'str'), |
168 | 171 |
('url_name', 'str'), |
169 | 172 |
('description', 'str'), |
173 |
('management_roles', 'roles'), |
|
170 | 174 |
] |
171 | 175 | |
172 | 176 |
@classmethod |
wcs/formdef.py | ||
---|---|---|
85 | 85 |
data_sql_prefix = 'formdata' |
86 | 86 |
pickle_module_name = 'formdef' |
87 | 87 |
xml_root_node = 'formdef' |
88 |
backoffice_section = 'forms' |
|
88 | 89 |
verbose_name = _('Form') |
89 | 90 |
verbose_name_plural = _('Forms') |
90 | 91 |
wcs/templates/wcs/backoffice/category.html | ||
---|---|---|
30 | 30 |
{% endwith %} |
31 | 31 |
</div> |
32 | 32 | |
33 |
{% if category.export_roles or category.statistics_roles %} |
|
33 |
{% if category.export_roles or category.statistics_roles or category.management_roles %}
|
|
34 | 34 |
<div class="section"> |
35 | 35 |
<h3>{% trans "Permissions" %}</h3> |
36 | 36 |
<div> |
37 | 37 |
<ul> |
38 |
{% if category.management_roles %} |
|
39 |
<li>{% trans "Management roles:" %} |
|
40 |
<ul> |
|
41 |
{% for role in category.management_roles %}<li>{{ role.name }}</li>{% endfor %} |
|
42 |
</ul> |
|
43 |
</li> |
|
44 |
{% endif %} |
|
38 | 45 |
{% if category.export_roles %} |
39 | 46 |
<li>{% trans "Export roles:" %} |
40 | 47 |
<ul> |
41 |
- |