0002-cards-add-category-48111.patch
tests/admin_pages/test_all.py | ||
---|---|---|
23 | 23 |
from wcs.qommon.http_request import HTTPRequest |
24 | 24 |
from wcs.qommon.template import get_current_theme |
25 | 25 |
from wcs.admin.settings import UserFieldsFormDef |
26 |
from wcs.categories import Category |
|
26 |
from wcs.categories import Category, CardDefCategory
|
|
27 | 27 |
from wcs.data_sources import NamedDataSource |
28 | 28 |
from wcs.wscalls import NamedWsCall |
29 | 29 |
from wcs.roles import Role |
... | ... | |
641 | 641 |
Workflow.wipe() |
642 | 642 |
Role.wipe() |
643 | 643 |
Category.wipe() |
644 |
CardDefCategory.wipe() |
|
644 | 645 |
NamedDataSource.wipe() |
645 | 646 |
NamedWsCall.wipe() |
646 | 647 | |
... | ... | |
672 | 673 |
carddef.name = 'bar' |
673 | 674 |
carddef.store() |
674 | 675 |
Category(name='baz').store() |
676 |
CardDefCategory(name='foobar').store() |
|
675 | 677 |
Role(name='qux').store() |
676 | 678 |
NamedDataSource(name='quux').store() |
677 | 679 |
NamedWsCall(name='corge').store() |
... | ... | |
708 | 710 |
assert 'models/export_to_model-1.upload' not in filelist |
709 | 711 |
assert 'roles/1' in filelist |
710 | 712 |
assert 'categories/1' in filelist |
713 |
assert 'carddef_categories/1' in filelist |
|
711 | 714 |
assert 'datasources/1' in filelist |
712 | 715 |
assert 'wscalls/corge' in filelist |
713 | 716 |
for filename in filelist: |
714 |
assert not '.indexes' in filename
|
|
717 |
assert '.indexes' not in filename
|
|
715 | 718 | |
716 | 719 |
wipe() |
717 | 720 |
assert FormDef.count() == 0 |
tests/admin_pages/test_card.py | ||
---|---|---|
4 | 4 | |
5 | 5 |
from wcs import fields |
6 | 6 |
from wcs.admin.settings import UserFieldsFormDef |
7 |
from wcs.categories import CardDefCategory |
|
7 | 8 |
from wcs.carddef import CardDef |
8 | 9 |
from wcs.formdef import FormDef |
9 | 10 |
from wcs.qommon.http_request import HTTPRequest |
... | ... | |
42 | 43 |
clean_temporary_pub() |
43 | 44 | |
44 | 45 | |
46 |
def test_cards_list(pub, studio): |
|
47 |
create_superuser(pub) |
|
48 | ||
49 |
CardDef.wipe() |
|
50 |
carddef = CardDef() |
|
51 |
carddef.name = 'card title' |
|
52 |
carddef.fields = [] |
|
53 |
carddef.store() |
|
54 | ||
55 |
carddef2 = CardDef() |
|
56 |
carddef2.name = 'card title 2' |
|
57 |
carddef2.fields = [] |
|
58 |
carddef2.store() |
|
59 | ||
60 |
CardDefCategory.wipe() |
|
61 |
cat = CardDefCategory(name='Foo') |
|
62 |
cat.store() |
|
63 |
cat2 = CardDefCategory(name='Bar') |
|
64 |
cat2.store() |
|
65 | ||
66 |
app = login(get_app(pub)) |
|
67 |
resp = app.get('/backoffice/cards/') |
|
68 |
assert '<h2>Misc</h2>' not in resp.text |
|
69 |
assert '<h2>Foo</h2>' not in resp.text |
|
70 |
assert '<h2>Bar</h2>' not in resp.text |
|
71 | ||
72 |
carddef.category = cat2 |
|
73 |
carddef.store() |
|
74 |
resp = app.get('/backoffice/cards/') |
|
75 |
assert '<h2>Misc</h2>' in resp.text |
|
76 |
assert '<h2>Foo</h2>' not in resp.text |
|
77 |
assert '<h2>Bar</h2>' in resp.text |
|
78 | ||
79 |
carddef2.category = cat |
|
80 |
carddef2.store() |
|
81 |
resp = app.get('/backoffice/cards/') |
|
82 |
assert '<h2>Misc</h2>' not in resp.text |
|
83 |
assert '<h2>Foo</h2>' in resp.text |
|
84 |
assert '<h2>Bar</h2>' in resp.text |
|
85 | ||
86 | ||
45 | 87 |
def test_cards_new(pub, studio): |
46 | 88 |
CardDef.wipe() |
47 | 89 |
create_superuser(pub) |
... | ... | |
197 | 239 |
assert 'Existing cards will be updated in the background.' not in resp.text |
198 | 240 | |
199 | 241 | |
242 |
def test_card_category(pub, studio): |
|
243 |
create_superuser(pub) |
|
244 | ||
245 |
CardDef.wipe() |
|
246 |
carddef = CardDef() |
|
247 |
carddef.name = 'card title' |
|
248 |
carddef.fields = [] |
|
249 |
carddef.store() |
|
250 | ||
251 |
CardDefCategory.wipe() |
|
252 |
cat = CardDefCategory(name='Foo') |
|
253 |
cat.store() |
|
254 |
cat = CardDefCategory(name='Bar') |
|
255 |
cat.store() |
|
256 | ||
257 |
app = login(get_app(pub)) |
|
258 |
resp = app.get('/backoffice/cards/1/') |
|
259 |
assert '<span class="label">Category</span> <span class="value">None</span>' in resp.text |
|
260 |
assert '<span class="label">Category</span> <span class="value">Foo</span>' not in resp.text |
|
261 |
assert '<span class="label">Category</span> <span class="value">Bar</span>' not in resp.text |
|
262 |
resp = resp.click(href='category') |
|
263 |
resp.forms[0].submit('cancel') |
|
264 |
assert CardDef.get(carddef.id).category_id is None |
|
265 | ||
266 |
resp = app.get('/backoffice/cards/1/') |
|
267 |
assert '<span class="label">Category</span> <span class="value">None</span>' in resp.text |
|
268 |
assert '<span class="label">Category</span> <span class="value">Foo</span>' not in resp.text |
|
269 |
assert '<span class="label">Category</span> <span class="value">Bar</span>' not in resp.text |
|
270 |
resp = resp.click(href='category') |
|
271 |
resp.forms[0]['category_id'] = cat.id |
|
272 |
resp.forms[0].submit('submit') |
|
273 |
assert CardDef.get(carddef.id).category_id == cat.id |
|
274 | ||
275 |
resp = app.get('/backoffice/cards/1/') |
|
276 |
assert '<span class="label">Category</span> <span class="value">None</span>' not in resp.text |
|
277 |
assert '<span class="label">Category</span> <span class="value">Foo</span>' not in resp.text |
|
278 |
assert '<span class="label">Category</span> <span class="value">Bar</span>' in resp.text |
|
279 | ||
280 | ||
200 | 281 |
def test_card_custom_view_data_source(pub, studio): |
201 | 282 |
user = create_superuser(pub) |
202 | 283 |
Role.wipe() |
tests/admin_pages/test_carddefcategory.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
import pytest |
|
4 | ||
5 |
from wcs.qommon.http_request import HTTPRequest |
|
6 |
from wcs.carddef import CardDef |
|
7 |
from wcs.categories import CardDefCategory |
|
8 | ||
9 |
from utilities import get_app, login, create_temporary_pub, clean_temporary_pub |
|
10 |
from .test_all import create_superuser |
|
11 | ||
12 | ||
13 |
def pytest_generate_tests(metafunc): |
|
14 |
if 'pub' in metafunc.fixturenames: |
|
15 |
metafunc.parametrize('pub', ['pickle', 'sql', 'pickle-templates'], indirect=True) |
|
16 | ||
17 | ||
18 |
@pytest.fixture |
|
19 |
def pub(request): |
|
20 |
pub = create_temporary_pub( |
|
21 |
sql_mode=bool('sql' in request.param), |
|
22 |
templates_mode=bool('templates' in request.param) |
|
23 |
) |
|
24 | ||
25 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
26 |
pub.set_app_dir(req) |
|
27 |
pub.cfg['identification'] = {'methods': ['password']} |
|
28 |
pub.cfg['language'] = {'language': 'en'} |
|
29 |
pub.write_cfg() |
|
30 | ||
31 |
return pub |
|
32 | ||
33 | ||
34 |
def teardown_module(module): |
|
35 |
clean_temporary_pub() |
|
36 | ||
37 | ||
38 |
def test_categories(pub): |
|
39 |
create_superuser(pub) |
|
40 |
app = login(get_app(pub)) |
|
41 |
app.get('/backoffice/cards/categories/') |
|
42 | ||
43 | ||
44 |
def test_categories_new(pub): |
|
45 |
create_superuser(pub) |
|
46 |
CardDefCategory.wipe() |
|
47 |
app = login(get_app(pub)) |
|
48 | ||
49 |
# go to the page and cancel |
|
50 |
resp = app.get('/backoffice/cards/categories/') |
|
51 |
resp = resp.click('New Category') |
|
52 |
resp = resp.forms[0].submit('cancel') |
|
53 |
assert resp.location == 'http://example.net/backoffice/cards/categories/' |
|
54 | ||
55 |
# go to the page and add a category |
|
56 |
resp = app.get('/backoffice/cards/categories/') |
|
57 |
resp = resp.click('New Category') |
|
58 |
resp.forms[0]['name'] = 'a new category' |
|
59 |
resp.forms[0]['description'] = 'description of the category' |
|
60 |
resp = resp.forms[0].submit('submit') |
|
61 |
assert resp.location == 'http://example.net/backoffice/cards/categories/' |
|
62 |
resp = resp.follow() |
|
63 |
assert 'a new category' in resp.text |
|
64 |
resp = resp.click('a new category') |
|
65 |
assert '<h2>a new category' in resp.text |
|
66 | ||
67 |
assert CardDefCategory.get(1).name == 'a new category' |
|
68 |
assert CardDefCategory.get(1).description == 'description of the category' |
|
69 | ||
70 | ||
71 |
def test_categories_edit(pub): |
|
72 |
create_superuser(pub) |
|
73 |
CardDefCategory.wipe() |
|
74 |
category = CardDefCategory(name='foobar') |
|
75 |
category.store() |
|
76 | ||
77 |
app = login(get_app(pub)) |
|
78 |
resp = app.get('/backoffice/cards/categories/1/') |
|
79 |
assert 'no card model associated to this category' in resp.text |
|
80 | ||
81 |
resp = resp.click(href='edit') |
|
82 |
assert resp.forms[0]['name'].value == 'foobar' |
|
83 |
resp.forms[0]['description'] = 'category description' |
|
84 |
resp = resp.forms[0].submit('submit') |
|
85 |
assert resp.location == 'http://example.net/backoffice/cards/categories/' |
|
86 |
resp = resp.follow() |
|
87 |
resp = resp.click('foobar') |
|
88 |
assert '<h2>foobar' in resp.text |
|
89 | ||
90 |
assert CardDefCategory.get(1).description == 'category description' |
|
91 | ||
92 | ||
93 |
def test_categories_edit_duplicate_name(pub): |
|
94 |
CardDefCategory.wipe() |
|
95 |
category = CardDefCategory(name='foobar') |
|
96 |
category.store() |
|
97 |
category = CardDefCategory(name='foobar2') |
|
98 |
category.store() |
|
99 | ||
100 |
app = login(get_app(pub)) |
|
101 |
resp = app.get('/backoffice/cards/categories/1/') |
|
102 | ||
103 |
resp = resp.click(href='edit') |
|
104 |
assert resp.forms[0]['name'].value == 'foobar' |
|
105 |
resp.forms[0]['name'] = 'foobar2' |
|
106 |
resp = resp.forms[0].submit('submit') |
|
107 |
assert 'This name is already used' in resp.text |
|
108 | ||
109 |
resp = resp.forms[0].submit('cancel') |
|
110 |
assert resp.location == 'http://example.net/backoffice/cards/categories/' |
|
111 | ||
112 | ||
113 |
def test_categories_with_carddefs(pub): |
|
114 |
CardDefCategory.wipe() |
|
115 |
category = CardDefCategory(name='foobar') |
|
116 |
category.store() |
|
117 | ||
118 |
CardDef.wipe() |
|
119 |
app = login(get_app(pub)) |
|
120 |
resp = app.get('/backoffice/cards/categories/1/') |
|
121 |
assert 'form bar' not in resp.text |
|
122 | ||
123 |
formdef = CardDef() |
|
124 |
formdef.name = 'form bar' |
|
125 |
formdef.fields = [] |
|
126 |
formdef.category_id = category.id |
|
127 |
formdef.store() |
|
128 | ||
129 |
resp = app.get('/backoffice/cards/categories/1/') |
|
130 |
assert 'form bar' in resp.text |
|
131 |
assert 'no card model associated to this category' not in resp.text |
|
132 | ||
133 | ||
134 |
def test_categories_delete(pub): |
|
135 |
CardDefCategory.wipe() |
|
136 |
category = CardDefCategory(name='foobar') |
|
137 |
category.store() |
|
138 | ||
139 |
CardDef.wipe() |
|
140 |
app = login(get_app(pub)) |
|
141 |
resp = app.get('/backoffice/cards/categories/1/') |
|
142 | ||
143 |
resp = resp.click(href='delete') |
|
144 |
resp = resp.forms[0].submit('cancel') |
|
145 |
assert resp.location == 'http://example.net/backoffice/cards/categories/1/' |
|
146 |
assert CardDefCategory.count() == 1 |
|
147 | ||
148 |
resp = app.get('/backoffice/cards/categories/1/') |
|
149 |
resp = resp.click(href='delete') |
|
150 |
resp = resp.forms[0].submit() |
|
151 |
assert resp.location == 'http://example.net/backoffice/cards/categories/' |
|
152 |
resp = resp.follow() |
|
153 |
assert CardDefCategory.count() == 0 |
|
154 | ||
155 | ||
156 |
def test_categories_edit_description(pub): |
|
157 |
CardDefCategory.wipe() |
|
158 |
category = CardDefCategory(name='foobar') |
|
159 |
category.description = 'category description' |
|
160 |
category.store() |
|
161 | ||
162 |
app = login(get_app(pub)) |
|
163 |
# this URL is used for editing from the frontoffice, there's no link |
|
164 |
# pointing to it in the admin. |
|
165 |
resp = app.get('/backoffice/cards/categories/1/description') |
|
166 |
assert resp.forms[0]['description'].value == 'category description' |
|
167 |
resp.forms[0]['description'] = 'updated description' |
|
168 | ||
169 |
# check cancel doesn't save the change |
|
170 |
resp2 = resp.forms[0].submit('cancel') |
|
171 |
assert resp2.location == 'http://example.net/backoffice/cards/categories/1/' |
|
172 |
assert CardDefCategory.get(1).description == 'category description' |
|
173 | ||
174 |
# check submit does it properly |
|
175 |
resp2 = resp.forms[0].submit('submit') |
|
176 |
assert resp2.location == 'http://example.net/backoffice/cards/categories/1/' |
|
177 |
resp2 = resp2.follow() |
|
178 |
assert CardDefCategory.get(1).description == 'updated description' |
|
179 | ||
180 | ||
181 |
def test_categories_new_duplicate_name(pub): |
|
182 |
CardDefCategory.wipe() |
|
183 |
category = CardDefCategory(name='foobar') |
|
184 |
category.store() |
|
185 | ||
186 |
app = login(get_app(pub)) |
|
187 |
resp = app.get('/backoffice/cards/categories/') |
|
188 |
resp = resp.click('New Category') |
|
189 |
resp.forms[0]['name'] = 'foobar' |
|
190 |
resp = resp.forms[0].submit('submit') |
|
191 |
assert 'This name is already used' in resp.text |
|
192 | ||
193 | ||
194 |
def test_categories_reorder(pub): |
|
195 |
CardDefCategory.wipe() |
|
196 |
category = CardDefCategory(name='foo') |
|
197 |
category.store() |
|
198 |
category = CardDefCategory(name='bar') |
|
199 |
category.store() |
|
200 |
category = CardDefCategory(name='baz') |
|
201 |
category.store() |
|
202 | ||
203 |
app = login(get_app(pub)) |
|
204 |
app.get('/backoffice/cards/categories/update_order?order=1;2;3;') |
|
205 |
categories = CardDefCategory.select() |
|
206 |
CardDefCategory.sort_by_position(categories) |
|
207 |
assert [x.id for x in categories] == ['1', '2', '3'] |
|
208 | ||
209 |
app.get('/backoffice/cards/categories/update_order?order=3;1;2;') |
|
210 |
categories = CardDefCategory.select() |
|
211 |
CardDefCategory.sort_by_position(categories) |
|
212 |
assert [x.id for x in categories] == ['3', '1', '2'] |
tests/backoffice_pages/test_all.py | ||
---|---|---|
46 | 46 |
from wcs.wf.create_formdata import CreateFormdataWorkflowStatusItem, Mapping |
47 | 47 |
from wcs.wf.create_carddata import CreateCarddataWorkflowStatusItem |
48 | 48 |
from wcs.carddef import CardDef |
49 |
from wcs.categories import Category |
|
49 |
from wcs.categories import Category, CardDefCategory
|
|
50 | 50 |
from wcs.formdef import FormDef |
51 | 51 |
from wcs.logged_errors import LoggedError |
52 | 52 |
from wcs import fields |
... | ... | |
6144 | 6144 |
user = create_user(pub) |
6145 | 6145 |
app = login(get_app(pub)) |
6146 | 6146 |
resp = app.get('/backoffice/') |
6147 |
assert not 'Cards' in resp.text
|
|
6147 |
assert 'Cards' not in resp.text
|
|
6148 | 6148 |
carddef = CardDef() |
6149 | 6149 |
carddef.name = 'foo' |
6150 | 6150 |
carddef.fields = [ |
... | ... | |
6156 | 6156 |
carddef.data_class().wipe() |
6157 | 6157 | |
6158 | 6158 |
resp = app.get('/backoffice/') |
6159 |
assert not 'Cards' in resp.text
|
|
6159 |
assert 'Cards' not in resp.text
|
|
6160 | 6160 | |
6161 | 6161 |
carddef.backoffice_submission_roles = user.roles |
6162 | 6162 |
carddef.store() |
... | ... | |
6212 | 6212 |
assert resp.text.count('<tr') == 2 # header + row of data |
6213 | 6213 | |
6214 | 6214 | |
6215 |
def test_carddata_management_categories(pub, studio): |
|
6216 |
user = create_user(pub) |
|
6217 | ||
6218 |
CardDef.wipe() |
|
6219 |
carddef = CardDef() |
|
6220 |
carddef.name = 'foo' |
|
6221 |
carddef.fields = [] |
|
6222 |
carddef.backoffice_submission_roles = None |
|
6223 |
carddef.workflow_roles = {'_editor': user.roles[0]} |
|
6224 |
carddef.store() |
|
6225 | ||
6226 |
carddef2 = CardDef() |
|
6227 |
carddef2.name = 'card title 2' |
|
6228 |
carddef2.fields = [] |
|
6229 |
carddef2.backoffice_submission_roles = None |
|
6230 |
carddef2.workflow_roles = {'_editor': user.roles[0]} |
|
6231 |
carddef2.store() |
|
6232 | ||
6233 |
CardDefCategory.wipe() |
|
6234 |
cat = CardDefCategory(name='Foo') |
|
6235 |
cat.store() |
|
6236 |
cat2 = CardDefCategory(name='Bar') |
|
6237 |
cat2.store() |
|
6238 | ||
6239 |
app = login(get_app(pub)) |
|
6240 |
resp = app.get('/backoffice/data/') |
|
6241 |
assert '<h3>Misc</h3>' not in resp.text |
|
6242 |
assert '<h3>Foo</h3>' not in resp.text |
|
6243 |
assert '<h3>Bar</h3>' not in resp.text |
|
6244 | ||
6245 |
carddef.category = cat2 |
|
6246 |
carddef.store() |
|
6247 |
resp = app.get('/backoffice/data/') |
|
6248 |
assert '<h3>Misc</h3>' in resp.text |
|
6249 |
assert '<h3>Foo</h3>' not in resp.text |
|
6250 |
assert '<h3>Bar</h3>' in resp.text |
|
6251 | ||
6252 |
carddef2.category = cat |
|
6253 |
carddef2.store() |
|
6254 |
resp = app.get('/backoffice/data/') |
|
6255 |
assert '<h3>Misc</h3>' not in resp.text |
|
6256 |
assert '<h3>Foo</h3>' in resp.text |
|
6257 |
assert '<h3>Bar</h3>' in resp.text |
|
6258 | ||
6259 | ||
6215 | 6260 |
def test_studio_card_item_link(pub, studio): |
6216 | 6261 |
user = create_user(pub) |
6217 | 6262 |
CardDef.wipe() |
tests/test_api.py | ||
---|---|---|
31 | 31 |
from wcs.carddef import CardDef |
32 | 32 |
from wcs.formdef import FormDef |
33 | 33 |
from wcs.formdata import Evolution |
34 |
from wcs.categories import Category |
|
34 |
from wcs.categories import Category, CardDefCategory
|
|
35 | 35 |
from wcs.data_sources import NamedDataSource |
36 | 36 |
from wcs.workflows import Workflow, EditableWorkflowStatusItem, WorkflowBackofficeFieldsFormDef, WorkflowVariablesFieldsFormDef |
37 | 37 |
from wcs.wf.jump import JumpWorkflowStatusItem |
... | ... | |
3312 | 3312 |
local_user.roles = [role.id] |
3313 | 3313 |
local_user.store() |
3314 | 3314 | |
3315 |
CardDefCategory.wipe() |
|
3316 |
category = CardDefCategory() |
|
3317 |
category.name = 'Category A' |
|
3318 |
category.store() |
|
3319 | ||
3315 | 3320 |
CardDef.wipe() |
3316 | 3321 |
carddef = CardDef() |
3317 | 3322 |
carddef.name = 'test' |
... | ... | |
3355 | 3360 |
resp = get_app(pub).get(sign_uri('/api/cards/@list')) |
3356 | 3361 |
assert len(resp.json['data']) == 1 |
3357 | 3362 |
assert resp.json['data'][0]['slug'] == 'test' |
3363 |
assert resp.json['data'][0]['category_slug'] is None |
|
3364 |
assert resp.json['data'][0]['category_name'] is None |
|
3358 | 3365 |
assert resp.json['data'][0]['custom_views'] == [ |
3359 | 3366 |
{'id': 'datasource-carddef-custom-view', 'text': 'datasource carddef custom view'}, |
3360 | 3367 |
{'id': 'shared-carddef-custom-view', 'text': 'shared carddef custom view'}, |
3361 | 3368 |
] |
3362 | 3369 | |
3370 |
carddef.category = category |
|
3371 |
carddef.store() |
|
3372 |
resp = get_app(pub).get(sign_uri('/api/cards/@list')) |
|
3373 |
assert len(resp.json['data']) == 1 |
|
3374 |
assert resp.json['data'][0]['slug'] == 'test' |
|
3375 |
assert resp.json['data'][0]['category_slug'] == 'category-a' |
|
3376 |
assert resp.json['data'][0]['category_name'] == 'Category A' |
|
3377 | ||
3363 | 3378 |
resp = get_app(pub).get(sign_uri('/api/cards/test/list'), status=403) |
3364 | 3379 | |
3365 | 3380 |
resp = get_app(pub).get(sign_uri( |
tests/test_carddef.py | ||
---|---|---|
4 | 4 |
from django.utils.six import BytesIO |
5 | 5 | |
6 | 6 |
from wcs.qommon.http_request import HTTPRequest |
7 |
from wcs.qommon.misc import indent_xml as indent |
|
7 | 8 |
from wcs.qommon.template import Template |
9 |
from wcs.categories import CardDefCategory |
|
8 | 10 |
from wcs.carddef import CardDef |
9 | 11 |
from wcs.fields import ItemField |
10 | 12 |
from wcs.fields import StringField |
... | ... | |
31 | 33 |
clean_temporary_pub() |
32 | 34 | |
33 | 35 | |
36 |
def export_to_indented_xml(carddef, include_id=False): |
|
37 |
carddef_xml = ET.fromstring(ET.tostring(carddef.export_to_xml(include_id=include_id))) |
|
38 |
indent(carddef_xml) |
|
39 |
return carddef_xml |
|
40 | ||
41 | ||
42 |
def assert_compare_carddef(carddef1, carddef2, include_id=False): |
|
43 |
assert ( |
|
44 |
ET.tostring(export_to_indented_xml(carddef1, include_id=include_id)) == |
|
45 |
ET.tostring(export_to_indented_xml(carddef2, include_id=include_id))) |
|
46 |
assert ( |
|
47 |
carddef1.export_to_json(include_id=include_id, indent=2) == |
|
48 |
carddef2.export_to_json(include_id=include_id, indent=2)) |
|
49 | ||
50 | ||
51 |
def assert_xml_import_export_works(carddef, include_id=False): |
|
52 |
carddef_xml = carddef.export_to_xml(include_id=include_id) |
|
53 |
carddef2 = CardDef.import_from_xml_tree(carddef_xml, include_id=include_id) |
|
54 |
assert_compare_carddef(carddef, carddef2, include_id=include_id) |
|
55 |
return carddef2 |
|
56 | ||
57 | ||
34 | 58 |
def test_basics(pub): |
35 | 59 |
carddef = CardDef() |
36 | 60 |
carddef.name = 'foo' |
... | ... | |
159 | 183 |
assert custom_views[1].formdef_type == 'carddef' |
160 | 184 | |
161 | 185 | |
186 |
def test_xml_export_import_category_reference(pub): |
|
187 |
CardDefCategory.wipe() |
|
188 |
CardDef.wipe() |
|
189 | ||
190 |
cat = CardDefCategory() |
|
191 |
cat.name = 'test category' |
|
192 |
cat.store() |
|
193 | ||
194 |
carddef = CardDef() |
|
195 |
carddef.name = 'foo' |
|
196 |
carddef.category_id = cat.id |
|
197 |
f2 = assert_xml_import_export_works(carddef) |
|
198 |
assert f2.category_id == carddef.category_id |
|
199 | ||
200 |
f2 = assert_xml_import_export_works(carddef, include_id=True) |
|
201 |
assert f2.category_id == carddef.category_id |
|
202 | ||
203 |
carddef_xml_with_id = carddef.export_to_xml(include_id=True) |
|
204 | ||
205 |
# check there's no reference to a non-existing category |
|
206 |
CardDefCategory.wipe() |
|
207 |
assert CardDef.import_from_xml_tree(carddef_xml_with_id, include_id=False).category_id is None |
|
208 |
assert CardDef.import_from_xml_tree(carddef_xml_with_id, include_id=True).category_id is None |
|
209 | ||
210 |
# check an import that is not using id fields will find the category by its |
|
211 |
# name |
|
212 |
cat = CardDefCategory() |
|
213 |
cat.id = '2' |
|
214 |
cat.name = 'test category' |
|
215 |
cat.store() |
|
216 |
assert CardDef.import_from_xml_tree(carddef_xml_with_id, include_id=False).category_id == '2' |
|
217 |
assert CardDef.import_from_xml_tree(carddef_xml_with_id, include_id=True).category_id is None |
|
218 | ||
219 | ||
162 | 220 |
def test_template_access(pub): |
163 | 221 |
CardDef.wipe() |
164 | 222 |
carddef = CardDef() |
tests/test_categories.py | ||
---|---|---|
6 | 6 | |
7 | 7 |
from django.utils.six import BytesIO |
8 | 8 |
from quixote import cleanup |
9 |
from wcs import publisher |
|
10 | 9 | |
11 |
from wcs.categories import Category |
|
10 |
from wcs.categories import Category, CardDefCategory
|
|
12 | 11 | |
13 | 12 |
from utilities import create_temporary_pub |
14 | 13 | |
... | ... | |
25 | 24 |
shutil.rmtree(pub.APP_DIR) |
26 | 25 | |
27 | 26 | |
28 |
def test_store(): |
|
29 |
Category.wipe() |
|
30 |
test = Category() |
|
27 |
@pytest.mark.parametrize('category_class', [Category, CardDefCategory]) |
|
28 |
def test_store(category_class): |
|
29 |
category_class.wipe() |
|
30 |
test = category_class() |
|
31 | 31 |
test.name = 'Test' |
32 | 32 |
test.description = 'Hello world' |
33 | 33 |
test.store() |
34 |
test2 = Category.get(1)
|
|
34 |
test2 = category_class.get(1)
|
|
35 | 35 |
assert test.id == test2.id |
36 | 36 |
assert test.name == test2.name |
37 | 37 |
assert test.description == test2.description |
38 | 38 | |
39 | 39 | |
40 |
def test_urlname(): |
|
41 |
Category.wipe() |
|
42 |
test = Category() |
|
40 |
@pytest.mark.parametrize('category_class', [Category, CardDefCategory]) |
|
41 |
def test_urlname(category_class): |
|
42 |
category_class.wipe() |
|
43 |
test = category_class() |
|
43 | 44 |
test.name = 'Test' |
44 | 45 |
test.description = 'Hello world' |
45 | 46 |
test.store() |
46 |
test = Category.get(1)
|
|
47 |
test = category_class.get(1)
|
|
47 | 48 |
assert test.url_name == 'test' |
48 | 49 | |
49 | 50 | |
50 |
def test_duplicate_urlname(): |
|
51 |
Category.wipe() |
|
52 |
test = Category() |
|
51 |
@pytest.mark.parametrize('category_class', [Category, CardDefCategory]) |
|
52 |
def test_duplicate_urlname(category_class): |
|
53 |
category_class.wipe() |
|
54 |
test = category_class() |
|
53 | 55 |
test.name = 'Test' |
54 | 56 |
test.store() |
55 |
test = Category.get(1)
|
|
57 |
test = category_class.get(1)
|
|
56 | 58 |
assert test.url_name == 'test' |
57 | 59 | |
58 |
test2 = Category()
|
|
60 |
test2 = category_class()
|
|
59 | 61 |
test2.name = 'Test' |
60 | 62 |
test2.store() |
61 |
test2 = Category.get(2)
|
|
63 |
test2 = category_class.get(2)
|
|
62 | 64 |
assert test2.url_name == 'test-2' |
63 | 65 | |
64 | 66 | |
65 |
def test_sort_positions(): |
|
66 |
Category.wipe() |
|
67 |
@pytest.mark.parametrize('category_class', [Category, CardDefCategory]) |
|
68 |
def test_sort_positions(category_class): |
|
69 |
category_class.wipe() |
|
67 | 70 | |
68 | 71 |
categories = [] |
69 | 72 |
for i in range(10): |
70 |
test = Category()
|
|
73 |
test = category_class()
|
|
71 | 74 |
test.name = 'Test %s' % i |
72 | 75 |
test.position = 10-i |
73 | 76 |
categories.append(test) |
... | ... | |
76 | 79 |
for i in range(8, 10): |
77 | 80 |
categories[i].position = None |
78 | 81 | |
79 |
Category.sort_by_position(categories)
|
|
82 |
category_class.sort_by_position(categories)
|
|
80 | 83 |
assert categories[0].name == 'Test 7' |
81 | 84 |
assert categories[-1].name in ('Test 8', 'Test 9') |
82 | 85 | |
83 | 86 | |
84 |
def test_xml_export(): |
|
85 |
Category.wipe() |
|
86 |
test = Category() |
|
87 |
@pytest.mark.parametrize('category_class', [Category, CardDefCategory]) |
|
88 |
def test_xml_export(category_class): |
|
89 |
category_class.wipe() |
|
90 |
test = category_class() |
|
87 | 91 |
test.id = 1 |
88 | 92 |
test.name = 'Test' |
89 | 93 |
test.description = 'Hello world' |
90 | 94 |
test.store() |
91 |
test = Category.get(1)
|
|
95 |
test = category_class.get(1)
|
|
92 | 96 | |
93 | 97 |
assert b'<name>Test</name>' in test.export_to_xml_string(include_id=True) |
94 | 98 |
assert b' id="1"' in test.export_to_xml_string(include_id=True) |
95 | 99 |
assert b' id="1"' not in test.export_to_xml_string(include_id=False) |
96 | 100 | |
97 | 101 | |
98 |
def test_xml_import(): |
|
99 |
Category.wipe() |
|
100 |
test = Category() |
|
102 |
@pytest.mark.parametrize('category_class', [Category, CardDefCategory]) |
|
103 |
def test_xml_import(category_class): |
|
104 |
category_class.wipe() |
|
105 |
test = category_class() |
|
101 | 106 |
test.name = 'Test' |
102 | 107 |
test.description = 'Hello world' |
103 | 108 |
test.store() |
104 |
test = Category.get(1)
|
|
109 |
test = category_class.get(1)
|
|
105 | 110 | |
106 | 111 |
fd = BytesIO(test.export_to_xml_string(include_id=True)) |
107 |
test2 = Category.import_from_xml(fd, include_id=True)
|
|
112 |
test2 = category_class.import_from_xml(fd, include_id=True)
|
|
108 | 113 |
assert test.id == test2.id |
109 | 114 |
assert test.name == test2.name |
110 | 115 |
assert test.description == test2.description |
... | ... | |
129 | 134 |
assert test.description == test2.description |
130 | 135 | |
131 | 136 | |
132 |
def test_get_by_urlname(): |
|
133 |
Category.wipe() |
|
134 |
test = Category() |
|
137 |
@pytest.mark.parametrize('category_class', [Category, CardDefCategory]) |
|
138 |
def test_get_by_urlname(category_class): |
|
139 |
category_class.wipe() |
|
140 |
test = category_class() |
|
135 | 141 |
test.id = 1 |
136 | 142 |
test.name = 'Test' |
137 | 143 |
test.description = 'Hello world' |
138 | 144 |
test.store() |
139 |
test = Category.get(1)
|
|
140 |
test2 = Category.get_by_urlname('test')
|
|
145 |
test = category_class.get(1)
|
|
146 |
test2 = category_class.get_by_urlname('test')
|
|
141 | 147 |
assert test.id == test2.id |
142 | 148 | |
143 | 149 | |
144 |
def test_has_urlname(): |
|
145 |
Category.wipe() |
|
146 |
test = Category() |
|
150 |
@pytest.mark.parametrize('category_class', [Category, CardDefCategory]) |
|
151 |
def test_has_urlname(category_class): |
|
152 |
category_class.wipe() |
|
153 |
test = category_class() |
|
147 | 154 |
test.id = 1 |
148 | 155 |
test.name = 'Test' |
149 | 156 |
test.description = 'Hello world' |
150 | 157 |
test.store() |
151 |
test = Category.get(1)
|
|
158 |
test = category_class.get(1)
|
|
152 | 159 | |
153 |
assert Category.has_urlname('test')
|
|
154 |
assert not Category.has_urlname('foobar')
|
|
160 |
assert category_class.has_urlname('test')
|
|
161 |
assert not category_class.has_urlname('foobar')
|
|
155 | 162 | |
156 | 163 | |
157 |
def test_remove_self(): |
|
158 |
Category.wipe() |
|
159 |
test = Category() |
|
164 |
@pytest.mark.parametrize('category_class', [Category, CardDefCategory]) |
|
165 |
def test_remove_self(category_class): |
|
166 |
category_class.wipe() |
|
167 |
test = category_class() |
|
160 | 168 |
test.id = 1 |
161 | 169 |
test.name = 'Test' |
162 | 170 |
test.description = 'Hello world' |
163 | 171 |
test.store() |
164 |
test = Category.get(1)
|
|
172 |
test = category_class.get(1)
|
|
165 | 173 |
test.remove_self() |
166 | 174 | |
167 | 175 |
with pytest.raises(KeyError): |
168 |
Category.get(1) |
|
176 |
category_class.get(1) |
wcs/admin/categories.py | ||
---|---|---|
18 | 18 |
from quixote.directory import Directory |
19 | 19 |
from quixote.html import TemplateIO, htmltext |
20 | 20 | |
21 |
from wcs.qommon import _ |
|
22 |
from wcs.categories import Category |
|
21 |
from wcs.qommon import _, N_
|
|
22 |
from wcs.categories import Category, CardDefCategory
|
|
23 | 23 |
from wcs.qommon.form import * |
24 | 24 |
from wcs.qommon.backoffice.menu import html_top |
25 | 25 | |
26 |
from wcs.carddef import CardDef |
|
26 | 27 |
from wcs.formdef import FormDef |
27 | 28 | |
28 | 29 | |
29 | 30 |
class CategoryUI(object): |
31 |
category_class = Category |
|
32 | ||
30 | 33 |
def __init__(self, category): |
31 | 34 |
self.category = category |
32 | 35 |
if self.category is None: |
33 |
self.category = Category()
|
|
36 |
self.category = self.category_class()
|
|
34 | 37 | |
35 | 38 |
def get_form(self): |
36 | 39 |
form = Form(enctype='multipart/form-data') |
... | ... | |
39 | 42 |
form.add(WysiwygTextWidget, 'description', title=_('Description'), |
40 | 43 |
cols=80, rows=10, |
41 | 44 |
value=self.category.description) |
42 |
form.add(StringWidget, 'redirect_url', size=32, |
|
43 |
title=_('URL Redirection'), |
|
44 |
hint=_('If set, redirect the site category page to the given URL.'), |
|
45 |
value=self.category.redirect_url) |
|
45 |
if self.category_class == Category: |
|
46 |
form.add(StringWidget, 'redirect_url', size=32, |
|
47 |
title=_('URL Redirection'), |
|
48 |
hint=_('If set, redirect the site category page to the given URL.'), |
|
49 |
value=self.category.redirect_url) |
|
46 | 50 |
form.add_submit('submit', _('Submit')) |
47 | 51 |
form.add_submit('cancel', _('Cancel')) |
48 | 52 |
return form |
... | ... | |
51 | 55 |
self.category.name = form.get_widget('name').parse() |
52 | 56 | |
53 | 57 |
name = form.get_widget('name').parse() |
54 |
category_names = [x.name for x in Category.select() if x.id != self.category.id]
|
|
58 |
category_names = [x.name for x in self.category_class.select() if x.id != self.category.id]
|
|
55 | 59 |
if name in category_names: |
56 | 60 |
form.get_widget('name').set_error(_('This name is already used')) |
57 | 61 |
raise ValueError() |
58 | 62 | |
59 | 63 |
self.category.description = form.get_widget('description').parse() |
60 |
self.category.redirect_url = form.get_widget('redirect_url').parse() |
|
64 |
if form.get_widget('redirect_url'): |
|
65 |
self.category.redirect_url = form.get_widget('redirect_url').parse() |
|
61 | 66 |
self.category.store() |
62 | 67 | |
63 | 68 | |
69 |
class CardDefCategoryUI(CategoryUI): |
|
70 |
category_class = CardDefCategory |
|
71 | ||
72 | ||
64 | 73 |
class CategoryPage(Directory): |
74 |
category_class = Category |
|
75 |
category_ui_class = CategoryUI |
|
76 |
formdef_class = FormDef |
|
77 |
usage_title = N_('Forms in this category') |
|
78 |
empty_message = N_('no form associated to this category') |
|
65 | 79 |
_q_exports = ['', 'edit', 'delete', 'description'] |
66 | 80 | |
67 | 81 |
def __init__(self, component): |
68 |
self.category = Category.get(component)
|
|
69 |
self.category_ui = CategoryUI(self.category)
|
|
82 |
self.category = self.category_class.get(component)
|
|
83 |
self.category_ui = self.category_ui_class(self.category)
|
|
70 | 84 |
get_response().breadcrumb.append((component + '/', self.category.name)) |
71 | 85 | |
72 | 86 |
def _q_index(self): |
... | ... | |
86 | 100 |
r += self.category.get_description_html_text() |
87 | 101 |
r += htmltext('</div>') |
88 | 102 | |
89 |
formdefs = FormDef.select(order_by='name')
|
|
103 |
formdefs = self.formdef_class.select(order_by='name')
|
|
90 | 104 |
formdefs = [x for x in formdefs if x.category_id == self.category.id] |
91 | 105 |
r += htmltext('<div class="bo-block">') |
92 |
r += htmltext('<h3>%s</h3>') % _('Forms in this category')
|
|
106 |
r += htmltext('<h3>%s</h3>') % _(self.usage_title)
|
|
93 | 107 |
r += htmltext('<ul>') |
94 | 108 |
for formdef in formdefs: |
95 | 109 |
r += htmltext('<li><a href="../../%s/">') % str(formdef.id) |
96 | 110 |
r += formdef.name |
97 | 111 |
r += htmltext('</a></li>') |
98 | 112 |
if not formdefs: |
99 |
r += htmltext('<li>%s</li>') % _('no form associated to this category')
|
|
113 |
r += htmltext('<li>%s</li>') % _(self.empty_message)
|
|
100 | 114 |
r += htmltext('</ul>') |
101 | 115 |
r += htmltext('</div>') |
102 | 116 |
return r.getvalue() |
... | ... | |
165 | 179 |
return r.getvalue() |
166 | 180 | |
167 | 181 | |
182 |
class CardDefCategoryPage(CategoryPage): |
|
183 |
category_class = CardDefCategory |
|
184 |
category_ui_class = CardDefCategoryUI |
|
185 |
formdef_class = CardDef |
|
186 |
usage_title = N_('Card models in this category') |
|
187 |
empty_message = N_('no card model associated to this category') |
|
188 | ||
189 | ||
168 | 190 |
class CategoriesDirectory(Directory): |
169 | 191 |
_q_exports = ['', 'new', 'update_order'] |
192 |
category_class = Category |
|
193 |
category_ui_class = CategoryUI |
|
194 |
category_page_class = CategoryPage |
|
195 |
category_explanation = N_('Categories are used to sort the different forms.') |
|
170 | 196 | |
171 | 197 |
def _q_index(self): |
172 | 198 |
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js', |
... | ... | |
180 | 206 |
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Category') |
181 | 207 |
r += htmltext('</span>') |
182 | 208 |
r += htmltext('</div>') |
183 |
r += htmltext('<div class="explanation bo-block"><p>%s</p></div>') % \ |
|
184 |
_('Categories are used to sort the different forms.') |
|
185 |
categories = Category.select() |
|
209 |
r += htmltext('<div class="explanation bo-block"><p>%s</p></div>') % _(self.category_explanation) |
|
210 |
categories = self.category_class.select() |
|
186 | 211 |
r += htmltext('<ul class="biglist sortable" id="category-list">') |
187 |
Category.sort_by_position(categories)
|
|
212 |
self.category_class.sort_by_position(categories)
|
|
188 | 213 |
for category in categories: |
189 | 214 |
r += htmltext('<li class="biglistitem" id="itemId_%s">') % category.id |
190 | 215 |
r += htmltext('<strong class="label"><a href="%s/">%s</a></strong>') % ( |
... | ... | |
196 | 221 |
def update_order(self): |
197 | 222 |
request = get_request() |
198 | 223 |
new_order = request.form['order'].strip(';').split(';') |
199 |
categories = Category.select()
|
|
224 |
categories = self.category_class.select()
|
|
200 | 225 |
dict = {} |
201 | 226 |
for l in categories: |
202 | 227 |
dict[str(l.id)] = l |
... | ... | |
207 | 232 | |
208 | 233 |
def new(self): |
209 | 234 |
get_response().breadcrumb.append( ('new', _('New')) ) |
210 |
category_ui = CategoryUI(None)
|
|
235 |
category_ui = self.category_ui_class(None)
|
|
211 | 236 |
form = category_ui.get_form() |
212 | 237 |
if form.get_widget('cancel').parse(): |
213 | 238 |
return redirect('.') |
... | ... | |
227 | 252 |
return r.getvalue() |
228 | 253 | |
229 | 254 |
def _q_lookup(self, component): |
230 |
return CategoryPage(component)
|
|
255 |
return self.category_page_class(component)
|
|
231 | 256 | |
232 | 257 |
def _q_traverse(self, path): |
233 | 258 |
get_response().breadcrumb.append( ('categories/', _('Categories')) ) |
234 |
return super(CategoriesDirectory, self)._q_traverse(path) |
|
259 |
return super()._q_traverse(path) |
|
260 | ||
261 | ||
262 |
class CardDefCategoriesDirectory(CategoriesDirectory): |
|
263 |
category_class = CardDefCategory |
|
264 |
category_ui_class = CardDefCategoryUI |
|
265 |
category_page_class = CardDefCategoryPage |
|
266 |
category_explanation = N_('Categories are used to sort the different card models.') |
wcs/admin/forms.py | ||
---|---|---|
60 | 60 |
from wcs.backoffice.snapshots import SnapshotsDirectory |
61 | 61 | |
62 | 62 | |
63 |
def get_categories(): |
|
64 |
t = sorted([(misc.simplify(x.name), x.id, x.name, x.id) for x in Category.select()])
|
|
63 |
def get_categories(category_class):
|
|
64 |
t = sorted([(misc.simplify(x.name), x.id, x.name, x.id) for x in category_class.select()])
|
|
65 | 65 |
return [x[1:] for x in t] |
66 | 66 | |
67 | 67 | |
68 | 68 |
class FormDefUI(object): |
69 | 69 |
formdef_class = FormDef |
70 |
category_class = Category |
|
70 | 71 | |
71 | 72 |
def __init__(self, formdef): |
72 | 73 |
self.formdef = formdef |
73 | 74 | |
74 | 75 |
def get_categories(self): |
75 |
return get_categories() |
|
76 |
return get_categories(self.category_class)
|
|
76 | 77 | |
77 | 78 |
@classmethod |
78 | 79 |
def get_workflows(cls, condition=lambda x: True): |
... | ... | |
165 | 166 | |
166 | 167 | |
167 | 168 |
class OptionsDirectory(Directory): |
169 |
category_class = Category |
|
170 |
category_empty_choice = N_('Select a category for this form') |
|
168 | 171 |
_q_exports = ['confirmation', 'only_allow_one', |
169 | 172 |
'always_advertise', 'tracking_code', 'online_status', 'captcha', |
170 | 173 |
'description', 'keywords', 'category', 'management', |
... | ... | |
265 | 268 |
return self.handle(form, _('Keywords')) |
266 | 269 | |
267 | 270 |
def category(self): |
268 |
categories = get_categories() |
|
271 |
categories = get_categories(self.category_class)
|
|
269 | 272 |
form = Form(enctype='multipart/form-data') |
270 |
form.widgets.append(HtmlWidget('<p>%s</p>' % _('Select a category for this form')))
|
|
273 |
form.widgets.append(HtmlWidget('<p>%s</p>' % _(self.category_empty_choice)))
|
|
271 | 274 |
form.add(SingleSelectWidget, 'category_id', title=_('Category'), |
272 | 275 |
value=self.formdef.category_id, |
273 | 276 |
options=[(None, '---', '')] + categories) |
... | ... | |
421 | 424 |
formdef_export_prefix = 'form' |
422 | 425 |
formdef_ui_class = FormDefUI |
423 | 426 |
formdef_default_workflow = '_default' |
427 |
options_directory_class = OptionsDirectory |
|
424 | 428 | |
425 | 429 |
delete_message = N_('You are about to irrevocably delete this form.') |
426 | 430 |
delete_title = N_('Deleting Form:') |
... | ... | |
442 | 446 |
self.fields.html_top = self.html_top |
443 | 447 |
self.role = WorkflowRoleDirectory(self.formdef) |
444 | 448 |
self.role.html_top = self.html_top |
445 |
self.options = OptionsDirectory(self.formdef)
|
|
449 |
self.options = self.options_directory_class(self.formdef)
|
|
446 | 450 |
self.logged_errors_dir = LoggedErrorsDirectory(parent_dir=self, formdef_id=self.formdef.id) |
447 | 451 |
self.snapshots_dir = SnapshotsDirectory(self.formdef) |
448 | 452 | |
... | ... | |
714 | 718 |
r += htmltext('</div>') |
715 | 719 |
return r.getvalue() |
716 | 720 | |
717 |
def category(self): |
|
718 |
categories = get_categories() |
|
719 |
form = Form(enctype='multipart/form-data') |
|
720 |
form.add(SingleSelectWidget, 'category_id', |
|
721 |
value=self.formdef.category_id, |
|
722 |
options=[(None, '---', '')] + categories) |
|
723 |
if not self.formdef.is_readonly(): |
|
724 |
form.add_submit('submit', _('Submit')) |
|
725 |
form.add_submit('cancel', _('Cancel')) |
|
726 |
if form.get_widget('cancel').parse(): |
|
727 |
return redirect('.') |
|
728 | ||
729 |
if not form.is_submitted() or form.has_errors(): |
|
730 |
get_response().breadcrumb.append( ('category', _('Category')) ) |
|
731 |
self.html_top(title = self.formdef.name) |
|
732 |
r = TemplateIO(html=True) |
|
733 |
r += htmltext('<h2>%s</h2>') % _('Category') |
|
734 |
r += htmltext('<p>%s</p>') % _('Select a category for this form') |
|
735 |
r += form.render() |
|
736 |
return r.getvalue() |
|
737 |
else: |
|
738 |
self.formdef.category_id = form.get_widget('category_id').parse() |
|
739 |
self.formdef.store(comment=_('Change of category')) |
|
740 |
return redirect('.') |
|
741 | ||
742 | 721 |
def _roles_selection(self, title, attribute, description=None, |
743 | 722 |
include_logged_users_role=True): |
744 | 723 |
form = Form(enctype='multipart/form-data') |
... | ... | |
1564 | 1543 |
_q_exports = ['', 'new', ('import', 'p_import'), |
1565 | 1544 |
'blocks', 'categories', ('data-sources', 'data_sources')] |
1566 | 1545 | |
1546 |
category_class = Category |
|
1567 | 1547 |
categories = CategoriesDirectory() |
1568 | 1548 |
blocks = BlocksDirectory(section='forms') |
1569 | 1549 |
data_sources = NamedDataSourcesDirectoryInForms() |
... | ... | |
1571 | 1551 |
formdef_page_class = FormDefPage |
1572 | 1552 |
formdef_ui_class = FormDefUI |
1573 | 1553 | |
1554 |
top_title = N_('Forms') |
|
1574 | 1555 |
import_title = N_('Import Form') |
1575 | 1556 |
import_submit_label = N_('Import Form') |
1576 | 1557 |
import_paragraph = N_( |
... | ... | |
1593 | 1574 |
return super()._q_traverse(path) |
1594 | 1575 | |
1595 | 1576 |
def _q_index(self): |
1596 |
self.html_top(title = _('Forms'))
|
|
1577 |
self.html_top(title=_(self.top_title))
|
|
1597 | 1578 |
r = TemplateIO(html=True) |
1598 | 1579 |
get_response().add_javascript(['jquery.js', 'widget_list.js']) |
1599 |
has_roles = bool(Role.count())
|
|
1580 |
r += self.form_actions()
|
|
1600 | 1581 | |
1601 |
r += htmltext('<div id="appbar">') |
|
1602 |
r += htmltext('<h2>%s</h2>') % _('Forms') |
|
1603 |
if has_roles: |
|
1604 |
r += htmltext('<span class="actions">') |
|
1605 |
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources') |
|
1606 |
if get_publisher().has_site_option('fields-blocks'): |
|
1607 |
r += htmltext('<a href="blocks/">%s</a>') % _('Fields blocks') |
|
1608 |
if get_publisher().get_backoffice_root().is_accessible('categories'): |
|
1609 |
r += htmltext('<a href="categories/">%s</a>') % _('Categories') |
|
1610 |
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import') |
|
1611 |
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Form') |
|
1612 |
r += htmltext('</span>') |
|
1613 |
r += htmltext('</div>') |
|
1614 | ||
1615 |
if not has_roles: |
|
1616 |
r += htmltext('<p>%s</p>') % _('You first have to define roles.') |
|
1617 | ||
1618 |
cats = Category.select() |
|
1619 |
Category.sort_by_position(cats) |
|
1582 |
cats = self.category_class.select() |
|
1583 |
self.category_class.sort_by_position(cats) |
|
1620 | 1584 |
one = False |
1621 | 1585 |
formdefs = self.formdef_class.select(order_by='name', ignore_errors=True, lightweight=True) |
1622 | 1586 |
for c in cats: |
... | ... | |
1638 | 1602 |
r += self.form_list(l2, title = title) |
1639 | 1603 |
return r.getvalue() |
1640 | 1604 | |
1605 |
def form_actions(self): |
|
1606 |
r = TemplateIO(html=True) |
|
1607 |
has_roles = bool(Role.count()) |
|
1608 |
r += htmltext('<div id="appbar">') |
|
1609 |
r += htmltext('<h2>%s</h2>') % _('Forms') |
|
1610 |
if has_roles: |
|
1611 |
r += htmltext('<span class="actions">') |
|
1612 |
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources') |
|
1613 |
if get_publisher().has_site_option('fields-blocks'): |
|
1614 |
r += htmltext('<a href="blocks/">%s</a>') % _('Fields blocks') |
|
1615 |
if get_publisher().get_backoffice_root().is_accessible('categories'): |
|
1616 |
r += htmltext('<a href="categories/">%s</a>') % _('Categories') |
|
1617 |
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import') |
|
1618 |
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Form') |
|
1619 |
r += htmltext('</span>') |
|
1620 |
r += htmltext('</div>') |
|
1621 | ||
1622 |
if not has_roles: |
|
1623 |
r += htmltext('<p>%s</p>') % _('You first have to define roles.') |
|
1624 |
return r.getvalue() |
|
1625 | ||
1641 | 1626 |
def form_list(self, formdefs, title): |
1642 | 1627 |
r = TemplateIO(html=True) |
1643 | 1628 |
if title: |
wcs/admin/settings.py | ||
---|---|---|
867 | 867 |
if not get_cfg('sp', {}).get('idp-manage-roles'): |
868 | 868 |
form.add(CheckboxWidget, 'roles', title = _('Roles'), value = True) |
869 | 869 |
form.add(CheckboxWidget, 'categories', title = _('Categories'), value = True) |
870 |
form.add(CheckboxWidget, 'carddef_categories', title = _('Card Model Categories'), value = True) |
|
870 | 871 |
form.add(CheckboxWidget, 'settings', title = _('Settings'), value = False) |
871 | 872 |
form.add(CheckboxWidget, 'datasources', title=_('Data sources'), value=True) |
872 | 873 |
form.add(CheckboxWidget, 'mail-templates', title=_('Mail templates'), value=True) |
... | ... | |
894 | 895 |
c = BytesIO() |
895 | 896 |
z = zipfile.ZipFile(c, 'w') |
896 | 897 |
for d in self.dirs: |
897 |
if d not in ('roles', 'categories', 'datasources', 'wscalls', 'mail-templates'): |
|
898 |
if d not in ('roles', 'categories', 'carddef_categories', 'datasources', 'wscalls', 'mail-templates'):
|
|
898 | 899 |
continue |
899 | 900 |
path = os.path.join(self.app_dir, d) |
900 | 901 |
if not os.path.exists(path): |
... | ... | |
942 | 943 |
job.store() |
943 | 944 | |
944 | 945 |
dirs = [] |
945 |
for w in ('formdefs', 'carddefs', 'workflows', 'roles', 'categories', |
|
946 |
'datasources', 'wscalls', 'mail-templates', 'blockdefs'): |
|
946 |
for w in ('formdefs', 'carddefs', 'workflows', 'roles', 'categories', 'carddef_categories',
|
|
947 |
'datasources', 'wscalls', 'mail-templates', 'blockdefs'):
|
|
947 | 948 |
if form.get_widget(w) and form.get_widget(w).parse(): |
948 | 949 |
dirs.append(w) |
949 | 950 |
if not dirs and not form.get_widget('settings').parse(): |
wcs/api.py | ||
---|---|---|
342 | 342 |
'title': x.name, |
343 | 343 |
'slug': x.url_name, |
344 | 344 |
'url': x.get_url(), |
345 |
'category_slug': x.category.url_name if x.category else None, |
|
346 |
'category_name': x.category.name if x.category else None, |
|
345 | 347 |
'description': x.description or '', |
346 | 348 |
'keywords': x.keywords_list, |
347 | 349 |
'custom_views': get_custom_views(x), |
wcs/backoffice/cards.py | ||
---|---|---|
16 | 16 |
# You should have received a copy of the GNU General Public License |
17 | 17 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
18 | 18 | |
19 |
import time |
|
20 | ||
21 |
from quixote import get_publisher, get_request, get_response, get_session, redirect |
|
19 |
from quixote import get_publisher, get_response, get_session, redirect |
|
22 | 20 |
from quixote.directory import Directory |
23 | 21 |
from quixote.html import TemplateIO, htmltext |
24 | 22 | |
25 |
from ..qommon import _, N_, misc
|
|
23 |
from ..qommon import _, N_ |
|
26 | 24 |
from ..qommon.misc import C_ |
27 | 25 |
from ..qommon.storage import NotEqual, Null |
28 | 26 | |
29 | 27 |
from wcs.carddef import CardDef |
28 |
from wcs.categories import CardDefCategory |
|
30 | 29 |
from wcs.roles import Role |
31 | 30 |
from wcs.workflows import Workflow |
32 |
from wcs.admin.forms import FormsDirectory, FormDefPage, FormDefUI, html_top |
|
31 |
from wcs.admin.categories import CardDefCategoriesDirectory |
|
32 |
from wcs.admin.forms import FormsDirectory, FormDefPage, FormDefUI, html_top, OptionsDirectory |
|
33 | 33 |
from wcs.admin.logged_errors import LoggedErrorsDirectory |
34 | 34 |
from wcs.admin import utils |
35 | 35 | |
36 | 36 | |
37 | 37 |
class CardDefUI(FormDefUI): |
38 | 38 |
formdef_class = CardDef |
39 |
category_class = CardDefCategory |
|
40 | ||
39 | 41 | |
40 |
def get_categories(self): |
|
41 |
return [] |
|
42 |
class CardDefOptionsDirectory(OptionsDirectory): |
|
43 |
category_class = CardDefCategory |
|
44 |
category_empty_choice = N_('Select a category for this card model') |
|
42 | 45 | |
43 | 46 | |
44 | 47 |
class CardDefPage(FormDefPage): |
... | ... | |
47 | 50 |
formdef_ui_class = CardDefUI |
48 | 51 |
formdef_default_workflow = '_carddef_default' |
49 | 52 | |
53 |
options_directory_class = CardDefOptionsDirectory |
|
54 | ||
50 | 55 |
delete_message = N_('You are about to irrevocably delete this card model.') |
51 | 56 |
delete_title = N_('Deleting Card Model:') |
52 | 57 |
overwrite_message = N_( |
... | ... | |
86 | 91 |
'label': label, |
87 | 92 |
'current_value': current_value}) |
88 | 93 | |
94 |
r += htmltext('<div class="bo-block">') |
|
95 |
r += htmltext('<h3>%s</h3>') % _('Information') |
|
96 |
r += htmltext('<ul class="biglist optionslist">') |
|
97 | ||
98 |
r += add_option_line( |
|
99 |
'options/category', _('Category'), |
|
100 |
self.formdef.category_id and self.formdef.category and |
|
101 |
self.formdef.category.name or C_('category|None')) |
|
102 |
r += htmltext('</ul>') |
|
103 |
r += htmltext('</div>') |
|
104 | ||
89 | 105 |
r += htmltext('<div class="splitcontent-left">') |
90 | 106 |
r += htmltext('<div class="bo-block">') |
91 | 107 |
r += htmltext('<h3>%s</h3>') % _('Workflow') |
... | ... | |
214 | 230 | |
215 | 231 | |
216 | 232 |
class CardsDirectory(FormsDirectory): |
217 |
_q_exports = ['', 'new', ('import', 'p_import')] |
|
233 |
_q_exports = ['', 'new', ('import', 'p_import'), 'categories'] |
|
234 | ||
235 |
category_class = CardDefCategory |
|
236 |
categories = CardDefCategoriesDirectory() |
|
218 | 237 |
formdef_class = CardDef |
219 | 238 |
formdef_page_class = CardDefPage |
220 | 239 |
formdef_ui_class = CardDefUI |
221 | 240 | |
241 |
top_title = N_('Card Models') |
|
222 | 242 |
import_title = N_('Import Card Model') |
223 | 243 |
import_submit_label = N_('Import Card Model') |
224 | 244 |
import_paragraph = N_( |
... | ... | |
238 | 258 |
get_response().breadcrumb.append(('cards/', _('Card Models'))) |
239 | 259 |
return Directory._q_traverse(self, path) |
240 | 260 | |
241 |
def _q_index(self): |
|
242 |
self.html_top(title=_('Card Models')) |
|
261 |
def form_actions(self): |
|
243 | 262 |
r = TemplateIO(html=True) |
244 | ||
245 | 263 |
r += htmltext('<div id="appbar">') |
246 | 264 |
r += htmltext('<h2>%s</h2>') % _('Card Models') |
247 | 265 |
r += htmltext('<span class="actions">') |
266 |
r += htmltext('<a href="categories/">%s</a>') % _('Categories') |
|
248 | 267 |
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import') |
249 | 268 |
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Card Model') |
250 | 269 |
r += htmltext('</span>') |
251 | 270 |
r += htmltext('</div>') |
252 | ||
253 |
formdefs = self.formdef_class.select(order_by='name', ignore_errors=True, lightweight=True) |
|
254 |
r += self.form_list(formdefs, title='') |
|
255 | 271 |
return r.getvalue() |
256 | 272 | |
257 | 273 |
def new(self): |
wcs/backoffice/data_management.py | ||
---|---|---|
33 | 33 |
from ..qommon.afterjobs import AfterJob |
34 | 34 | |
35 | 35 |
from wcs.carddef import CardDef |
36 |
from wcs.categories import CardDefCategory |
|
36 | 37 |
from wcs import fields |
37 | 38 | |
38 | 39 |
from .management import ManagementDirectory, FormPage, FormFillPage, FormBackOfficeStatusPage |
... | ... | |
60 | 61 | |
61 | 62 |
def get_carddefs(self): |
62 | 63 |
user = get_request().user |
63 |
if user: |
|
64 |
for formdef in CardDef.select(order_by='name', ignore_errors=True, lightweight=True): |
|
65 |
if user.is_admin or formdef.is_of_concern_for_user(user): |
|
66 |
yield formdef |
|
64 |
if not user: |
|
65 |
return |
|
66 |
carddefs = CardDef.select(order_by='name', ignore_errors=True, lightweight=True) |
|
67 |
carddefs = [c for c in carddefs if user.is_admin or c.is_of_concern_for_user(user)] |
|
68 |
cats = CardDefCategory.select(order_by='name') |
|
69 |
for c in cats + [None]: |
|
70 |
for carddef in carddefs: |
|
71 |
if c is None and not carddef.category_id: |
|
72 |
yield carddef |
|
73 |
if c is not None and carddef.category_id == c.id: |
|
74 |
yield carddef |
|
67 | 75 | |
68 | 76 |
def _q_index(self): |
69 | 77 |
html_top('data_management', _('Cards')) |
70 | 78 |
if CardDef.count() == 0: |
71 | 79 |
return self.empty_site_message(_('Cards')) |
72 | ||
73 | 80 |
return template.QommonTemplateResponse( |
74 | 81 |
templates=['wcs/backoffice/data-management.html'], |
75 | 82 |
context={'view': self}) |
wcs/carddef.py | ||
---|---|---|
23 | 23 |
from .qommon.template import Template |
24 | 24 | |
25 | 25 |
from wcs.carddata import CardData |
26 |
from wcs.categories import CardDefCategory |
|
26 | 27 |
from wcs.formdef import FormDef, get_formdefs_of_all_kinds |
27 | 28 | |
28 | 29 |
if not hasattr(types, 'ClassType'): |
... | ... | |
39 | 40 | |
40 | 41 |
confirmation = False |
41 | 42 | |
43 |
category_class = CardDefCategory |
|
44 | ||
42 | 45 |
def data_class(self, mode=None): |
43 | 46 |
if not 'carddef' in sys.modules: |
44 | 47 |
sys.modules['carddef'] = sys.modules[__name__] |
wcs/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_publisher, get_response
|
|
17 |
from quixote import get_publisher |
|
18 | 18 |
from quixote.html import htmltext |
19 | 19 | |
20 | 20 |
from .qommon import N_ |
... | ... | |
34 | 34 |
redirect_url = None |
35 | 35 | |
36 | 36 |
# declarations for serialization |
37 |
XML_NODES = [('name', 'str'), ('url_name', 'str'), ('description', 'str'), |
|
38 |
('redirect_url', 'str'), ('position', 'int')] |
|
37 |
XML_NODES = [ |
|
38 |
('name', 'str'), ('url_name', 'str'), ('description', 'str'), |
|
39 |
('redirect_url', 'str'), ('position', 'int')] |
|
39 | 40 | |
40 |
def __init__(self, name = None):
|
|
41 |
def __init__(self, name=None):
|
|
41 | 42 |
StorableObject.__init__(self) |
42 | 43 |
self.name = name |
43 | 44 | |
... | ... | |
110 | 111 |
return htmltext(text) |
111 | 112 | |
112 | 113 | |
114 |
class CardDefCategory(Category): |
|
115 |
_names = 'carddef_categories' |
|
116 |
xml_root_node = 'carddef_category' |
|
117 | ||
118 |
# declarations for serialization |
|
119 |
XML_NODES = [('name', 'str'), ('url_name', 'str'), ('description', 'str'), ('position', 'int')] |
|
120 | ||
121 | ||
113 | 122 |
Substitutions.register('category_name', category=N_('General'), comment=N_('Category Name')) |
114 | 123 |
Substitutions.register('category_description', category=N_('General'), comment=N_('Category Description')) |
115 | 124 |
Substitutions.register('category_id', category=N_('General'), comment=N_('Category Identifier')) |
wcs/formdef.py | ||
---|---|---|
141 | 141 |
'always_advertise', 'include_download_all_button', |
142 | 142 |
'has_captcha', 'skip_from_360_view'] |
143 | 143 | |
144 |
category_class = Category |
|
145 | ||
144 | 146 |
def __init__(self, *args, **kwargs): |
145 | 147 |
super(FormDef, self).__init__(*args, **kwargs) |
146 | 148 |
self.fields = [] |
... | ... | |
415 | 417 |
def get_category(self): |
416 | 418 |
if self.category_id: |
417 | 419 |
try: |
418 |
return Category.get(self.category_id)
|
|
420 |
return self.category_class.get(self.category_id)
|
|
419 | 421 |
except KeyError: |
420 | 422 |
return None |
421 | 423 |
else: |
... | ... | |
1183 | 1185 |
category_node = tree.find('category') |
1184 | 1186 |
if include_id and category_node.attrib.get('category_id'): |
1185 | 1187 |
category_id = str(category_node.attrib.get('category_id')) |
1186 |
if Category.has_key(category_id):
|
|
1188 |
if cls.category_class.has_key(category_id):
|
|
1187 | 1189 |
formdef.category_id = category_id |
1188 | 1190 |
else: |
1189 | 1191 |
category = xml_node_text(category_node) |
1190 |
for c in Category.select():
|
|
1192 |
for c in cls.category_class.select():
|
|
1191 | 1193 |
if c.name == category: |
1192 | 1194 |
formdef.category_id = c.id |
1193 | 1195 |
break |
wcs/templates/wcs/backoffice/data-management.html | ||
---|---|---|
4 | 4 |
{% block appbar-title %}{% trans "Cards" %}{% endblock %} |
5 | 5 | |
6 | 6 |
{% block content %} |
7 |
<ul class="objects-list single-links"> |
|
8 |
{% for carddef in view.get_carddefs %} |
|
9 |
<li><a href="{{ carddef.url_name }}/">{{ carddef.name }}</a></li> |
|
7 |
{% regroup view.get_carddefs by category as category_list %} |
|
8 |
<ul class="biglist"> |
|
9 |
{% for category, object_list in category_list %} |
|
10 |
{% if not forloop.first or category %}<li><h3>{{ category.name|default:_('Misc') }}</h3></li>{% endif %} |
|
11 |
{% for carddef in object_list %} |
|
12 |
<li><a href="{{ carddef.url_name }}/">{{ carddef.name }}</a></li> |
|
13 |
{% endfor %} |
|
10 | 14 |
{% endfor %} |
11 | 15 |
</ul> |
12 | 16 |
{% endblock %} |
13 |
- |