0001-workflows-add-comment-template-management-39178.patch
tests/admin_pages/test_settings.py | ||
---|---|---|
15 | 15 | |
16 | 16 |
from wcs import fields |
17 | 17 |
from wcs.api_access import ApiAccess |
18 |
from wcs.blocks import BlockDef |
|
18 | 19 |
from wcs.carddef import CardDef |
19 | 20 |
from wcs.categories import ( |
20 | 21 |
BlockCategory, |
21 | 22 |
CardDefCategory, |
22 | 23 |
Category, |
24 |
CommentTemplateCategory, |
|
23 | 25 |
DataSourceCategory, |
24 | 26 |
MailTemplateCategory, |
25 | 27 |
WorkflowCategory, |
26 | 28 |
) |
29 |
from wcs.comment_templates import CommentTemplate |
|
27 | 30 |
from wcs.data_sources import NamedDataSource |
28 | 31 |
from wcs.formdef import FormDef |
32 |
from wcs.mail_templates import MailTemplate |
|
29 | 33 |
from wcs.qommon.form import UploadedFile |
30 | 34 |
from wcs.qommon.http_request import HTTPRequest |
31 | 35 |
from wcs.wf.export_to_model import ExportToModel |
... | ... | |
97 | 101 |
ApiAccess.wipe() |
98 | 102 |
BlockCategory.wipe() |
99 | 103 |
MailTemplateCategory.wipe() |
104 |
CommentTemplateCategory.wipe() |
|
100 | 105 |
DataSourceCategory.wipe() |
106 |
MailTemplate.wipe() |
|
107 |
CommentTemplate.wipe() |
|
108 |
BlockDef.wipe() |
|
101 | 109 | |
102 | 110 |
wipe() |
103 | 111 |
create_superuser(pub) |
... | ... | |
132 | 140 |
WorkflowCategory(name='foobaz').store() |
133 | 141 |
BlockCategory(name='category for blocks').store() |
134 | 142 |
MailTemplateCategory(name='category for mail templates').store() |
143 |
CommentTemplateCategory(name='category for mail templates').store() |
|
135 | 144 |
DataSourceCategory(name='category for data sources').store() |
145 |
MailTemplate(name='Mail templates').store() |
|
146 |
CommentTemplate(name='Comment templates').store() |
|
136 | 147 |
pub.role_class(name='qux').store() |
137 | 148 |
NamedDataSource(name='quux').store() |
149 |
BlockDef(name='blockdef').store() |
|
138 | 150 |
ds = NamedDataSource(name='agenda') |
139 | 151 |
ds.external = 'agenda' |
140 | 152 |
ds.store() |
... | ... | |
179 | 191 |
assert 'carddef_categories/1' in filelist |
180 | 192 |
assert 'workflow_categories/1' in filelist |
181 | 193 |
assert 'block_categories/1' in filelist |
182 |
assert 'data_source_categories/1' in filelist |
|
183 | 194 |
assert 'mail_template_categories/1' in filelist |
195 |
assert 'comment_template_categories/1' in filelist |
|
196 |
assert 'data_source_categories/1' in filelist |
|
184 | 197 |
assert 'datasources/1' in filelist |
185 | 198 |
assert 'datasources/2' not in filelist # agenda datasource, not exported |
199 |
assert 'mail-templates/1' in filelist |
|
200 |
assert 'comment-templates/1' in filelist |
|
186 | 201 |
assert 'wscalls/corge' in filelist |
187 | 202 |
assert 'apiaccess/1' in filelist |
188 | 203 |
for filename in filelist: |
... | ... | |
204 | 219 |
resp.form['file'] = Upload('export.wcs', zip_content.getvalue()) |
205 | 220 |
resp = resp.form.submit('submit') |
206 | 221 |
assert 'Imported successfully' in resp.text |
207 |
assert '1 form' in resp.text |
|
208 |
assert '1 card' in resp.text |
|
209 |
assert '2 categories' in resp.text |
|
210 |
assert '1 card category' in resp.text |
|
211 |
assert '1 workflow category' in resp.text |
|
222 |
assert '1 form</li>' in resp.text |
|
223 |
assert '1 card</li>' in resp.text |
|
224 |
assert '1 fields block</li>' in resp.text |
|
225 |
assert '1 workflow</li>' in resp.text |
|
226 |
assert '1 role</li>' in resp.text |
|
227 |
assert '2 categories</li>' in resp.text |
|
228 |
assert '1 card category</li>' in resp.text |
|
229 |
assert '1 workflow category</li>' in resp.text |
|
230 |
assert '1 block category</li>' in resp.text |
|
231 |
assert '1 mail template category</li>' in resp.text |
|
232 |
assert '1 comment template category</li>' in resp.text |
|
233 |
assert '1 data source category</li>' in resp.text |
|
234 |
assert '1 data source</li>' in resp.text |
|
235 |
assert '1 mail template</li>' in resp.text |
|
236 |
assert '1 comment template</li>' in resp.text |
|
237 |
assert '1 webservice call</li>' in resp.text |
|
238 |
assert '1 API access</li>' in resp.text |
|
212 | 239 |
assert FormDef.count() == 1 |
213 | 240 |
assert FormDef.select()[0].url_name == 'foo' |
214 | 241 |
assert CardDef.count() == 1 |
215 | 242 |
assert CardDef.select()[0].url_name == 'bar' |
243 |
assert BlockDef.count() == 1 |
|
244 |
assert Workflow.count() == 1 |
|
245 |
assert pub.role_class.count() == 1 |
|
246 |
assert Category.count() == 2 |
|
247 |
assert CardDefCategory.count() == 1 |
|
248 |
assert WorkflowCategory.count() == 1 |
|
216 | 249 |
assert BlockCategory.count() == 1 |
217 | 250 |
assert MailTemplateCategory.count() == 1 |
251 |
assert CommentTemplateCategory.count() == 1 |
|
218 | 252 |
assert DataSourceCategory.count() == 1 |
253 |
assert NamedDataSource.count() == 1 |
|
254 |
assert MailTemplate.count() == 1 |
|
255 |
assert CommentTemplate.count() == 1 |
|
256 |
assert NamedWsCall.count() == 1 |
|
219 | 257 |
assert ApiAccess.count() == 1 |
220 |
assert pub.role_class.count() == 1 |
|
221 | 258 | |
222 | 259 |
# check roles are found by name |
223 | 260 |
wipe() |
tests/admin_pages/test_studio.py | ||
---|---|---|
5 | 5 | |
6 | 6 |
from wcs.blocks import BlockDef |
7 | 7 |
from wcs.carddef import CardDef |
8 |
from wcs.comment_templates import CommentTemplate |
|
8 | 9 |
from wcs.data_sources import NamedDataSource |
9 | 10 |
from wcs.formdef import FormDef |
10 | 11 |
from wcs.mail_templates import MailTemplate |
... | ... | |
48 | 49 |
assert '../settings/data-sources/' not in resp.text |
49 | 50 |
assert '../forms/blocks/' in resp.text |
50 | 51 |
assert '../workflows/mail-templates/' in resp.text |
52 |
assert '../workflows/comment-templates/' in resp.text |
|
51 | 53 |
assert '../settings/wscalls/' in resp.text |
52 | 54 |
assert 'Recent errors' in resp.text |
53 | 55 | |
... | ... | |
136 | 138 |
NamedDataSource.wipe() |
137 | 139 |
FormDef.wipe() |
138 | 140 |
MailTemplate.wipe() |
141 |
CommentTemplate.wipe() |
|
139 | 142 |
Workflow.wipe() |
140 | 143 |
NamedWsCall.wipe() |
141 | 144 | |
142 | 145 |
objects = defaultdict(list) |
143 | 146 |
for i in range(6): |
144 |
for klass in [BlockDef, CardDef, NamedDataSource, FormDef, MailTemplate, Workflow, NamedWsCall]: |
|
147 |
for klass in [ |
|
148 |
BlockDef, |
|
149 |
CardDef, |
|
150 |
NamedDataSource, |
|
151 |
FormDef, |
|
152 |
MailTemplate, |
|
153 |
CommentTemplate, |
|
154 |
Workflow, |
|
155 |
NamedWsCall, |
|
156 |
]: |
|
145 | 157 |
obj = klass() |
146 | 158 |
obj.name = 'foo %s' % i |
147 | 159 |
obj.store() |
148 | 160 |
objects[klass.xml_root_node].append(obj) |
149 |
for klass in [BlockDef, CardDef, NamedDataSource, FormDef, MailTemplate, Workflow, NamedWsCall]: |
|
161 |
for klass in [ |
|
162 |
BlockDef, |
|
163 |
CardDef, |
|
164 |
NamedDataSource, |
|
165 |
FormDef, |
|
166 |
MailTemplate, |
|
167 |
CommentTemplate, |
|
168 |
Workflow, |
|
169 |
NamedWsCall, |
|
170 |
]: |
|
150 | 171 |
assert pub.snapshot_class.count(clause=[Equal('object_type', klass.xml_root_node)]) == 6 |
151 | 172 |
# 2 snapshots for this one, but will be displayed only once |
152 | 173 |
objects[klass.xml_root_node][-1].name += ' bar' |
... | ... | |
184 | 205 |
assert ( |
185 | 206 |
'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][i].id not in resp |
186 | 207 |
) |
208 |
assert ( |
|
209 |
'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][i].id |
|
210 |
not in resp |
|
211 |
) |
|
187 | 212 |
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][i].id not in resp |
188 | 213 |
assert 'backoffice/settings/wscalls/%s/' % objects[NamedWsCall.xml_root_node][i].id not in resp |
189 | 214 | |
190 | 215 |
# too old |
191 | 216 |
assert 'backoffice/forms/blocks/%s/' % objects[BlockDef.xml_root_node][5].id not in resp |
192 | 217 |
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][5].id not in resp |
218 |
assert 'backoffice/settings/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id not in resp |
|
193 | 219 |
# only 5 elements |
194 |
assert 'backoffice/settings/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id in resp |
|
195 | 220 |
assert ( |
196 | 221 |
'backoffice/forms/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id not in resp |
197 | 222 |
) # not this url |
... | ... | |
201 | 226 |
) |
202 | 227 |
assert 'backoffice/forms/%s/' % objects[FormDef.xml_root_node][5].id in resp |
203 | 228 |
assert 'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][5].id in resp |
229 |
assert 'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][5].id in resp |
|
204 | 230 |
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][5].id in resp |
205 | 231 |
assert 'backoffice/settings/wscalls/%s/' % objects[NamedWsCall.xml_root_node][5].id in resp |
206 | 232 | |
... | ... | |
227 | 253 |
assert ( |
228 | 254 |
'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][i].id not in resp |
229 | 255 |
) |
256 |
assert ( |
|
257 |
'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][i].id |
|
258 |
not in resp |
|
259 |
) |
|
230 | 260 |
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][i].id not in resp |
231 | 261 |
# too old |
232 | 262 |
assert 'backoffice/forms/blocks/%s/' % objects[BlockDef.xml_root_node][5].id not in resp |
263 |
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][5].id not in resp |
|
233 | 264 |
# only 5 elements |
234 |
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][5].id in resp |
|
235 | 265 |
assert 'backoffice/forms/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id in resp |
236 | 266 |
assert ( |
237 | 267 |
'backoffice/workflows/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id |
... | ... | |
239 | 269 |
) |
240 | 270 |
assert 'backoffice/forms/%s/' % objects[FormDef.xml_root_node][5].id in resp |
241 | 271 |
assert 'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][5].id in resp |
272 |
assert 'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][5].id in resp |
|
242 | 273 |
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][5].id in resp |
243 | 274 | |
244 | 275 |
pub.cfg['admin-permissions'] = {} |
... | ... | |
256 | 287 |
assert 'backoffice/forms/%s/' % objects[FormDef.xml_root_node][i].id not in resp |
257 | 288 |
assert 'backoffice/settings/wscalls/%s/' % objects[NamedWsCall.xml_root_node][i].id not in resp |
258 | 289 |
# too old |
259 |
for i in range(4):
|
|
290 |
for i in range(5):
|
|
260 | 291 |
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][i].id not in resp |
261 | 292 |
assert ( |
262 | 293 |
'backoffice/workflows/data-sources/%s/' % objects[NamedDataSource.xml_root_node][i].id not in resp |
... | ... | |
264 | 295 |
assert ( |
265 | 296 |
'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][i].id not in resp |
266 | 297 |
) |
298 |
assert ( |
|
299 |
'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][i].id |
|
300 |
not in resp |
|
301 |
) |
|
267 | 302 |
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][i].id not in resp |
268 |
# too old |
|
269 |
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][4].id not in resp |
|
270 |
assert 'backoffice/workflows/data-sources/%s/' % objects[NamedDataSource.xml_root_node][4].id not in resp |
|
271 |
assert 'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][4].id not in resp |
|
272 | 303 |
# only 5 elements |
273 |
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][4].id in resp |
|
274 | 304 |
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][5].id in resp |
275 | 305 |
assert 'backoffice/workflows/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id in resp |
276 | 306 |
assert 'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][5].id in resp |
307 |
assert 'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][5].id in resp |
|
277 | 308 |
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][5].id in resp |
278 | 309 | |
279 | 310 |
pub.cfg['admin-permissions'] = {} |
... | ... | |
296 | 327 |
assert ( |
297 | 328 |
'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][i].id not in resp |
298 | 329 |
) |
330 |
assert ( |
|
331 |
'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][i].id |
|
332 |
not in resp |
|
333 |
) |
|
299 | 334 |
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][i].id not in resp |
300 | 335 |
# too old |
301 | 336 |
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][0].id not in resp |
... | ... | |
324 | 359 |
pub.cfg['admin-permissions'] = {} |
325 | 360 |
pub.write_cfg() |
326 | 361 |
resp = app.get('/backoffice/studio/all-changes/') |
327 |
assert '(1-20/42)' in resp
|
|
362 |
assert '(1-20/48)' in resp
|
|
328 | 363 |
resp = resp.click('<!--Next Page-->') |
329 |
assert '21-40/42' in resp.text
|
|
364 |
assert '21-40/48' in resp.text
|
|
330 | 365 |
resp = resp.click('<!--Next Page-->') |
331 |
assert '41-42/42' in resp.text
|
|
366 |
assert '41-48/48' in resp.text
|
|
332 | 367 | |
333 | 368 |
user.is_admin = False |
334 | 369 |
user.store() |
tests/test_comment_template.py | ||
---|---|---|
1 |
import io |
|
2 |
import os |
|
3 |
import re |
|
4 |
import xml.etree.ElementTree as ET |
|
5 | ||
6 |
import pytest |
|
7 |
from quixote import cleanup |
|
8 |
from webtest import Upload |
|
9 | ||
10 |
from wcs.categories import CommentTemplateCategory |
|
11 |
from wcs.comment_templates import CommentTemplate |
|
12 |
from wcs.fields import FileField |
|
13 |
from wcs.formdef import FormDef |
|
14 |
from wcs.qommon.http_request import HTTPRequest |
|
15 |
from wcs.qommon.ident.password_accounts import PasswordAccount |
|
16 |
from wcs.qommon.misc import indent_xml as indent |
|
17 |
from wcs.qommon.upload_storage import PicklableUpload |
|
18 |
from wcs.workflows import Workflow |
|
19 | ||
20 |
from .utilities import clean_temporary_pub, create_temporary_pub, get_app, login |
|
21 | ||
22 | ||
23 |
def setup_module(module): |
|
24 |
cleanup() |
|
25 | ||
26 | ||
27 |
def teardown_module(module): |
|
28 |
clean_temporary_pub() |
|
29 | ||
30 | ||
31 |
@pytest.fixture |
|
32 |
def pub(request): |
|
33 |
pub = create_temporary_pub() |
|
34 |
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'}) |
|
35 |
pub.set_app_dir(req) |
|
36 |
pub._set_request(req) |
|
37 |
pub.cfg['identification'] = {'methods': ['password']} |
|
38 |
pub.write_cfg() |
|
39 |
return pub |
|
40 | ||
41 | ||
42 |
@pytest.fixture |
|
43 |
def superuser(pub): |
|
44 |
if pub.user_class.select(lambda x: x.name == 'admin'): |
|
45 |
user1 = pub.user_class.select(lambda x: x.name == 'admin')[0] |
|
46 |
user1.is_admin = True |
|
47 |
user1.store() |
|
48 |
return user1 |
|
49 | ||
50 |
user1 = pub.user_class(name='admin') |
|
51 |
user1.is_admin = True |
|
52 |
user1.store() |
|
53 | ||
54 |
account1 = PasswordAccount(id='admin') |
|
55 |
account1.set_password('admin') |
|
56 |
account1.user_id = user1.id |
|
57 |
account1.store() |
|
58 | ||
59 |
return user1 |
|
60 | ||
61 | ||
62 |
@pytest.fixture |
|
63 |
def comment_template(): |
|
64 |
CommentTemplate.wipe() |
|
65 |
comment_template = CommentTemplate(name='test CT') |
|
66 |
comment_template.comment = 'test comment' |
|
67 |
comment_template.store() |
|
68 |
return comment_template |
|
69 | ||
70 | ||
71 |
def test_comment_templates_basics(pub, superuser): |
|
72 |
CommentTemplateCategory.wipe() |
|
73 |
CommentTemplate.wipe() |
|
74 | ||
75 |
app = login(get_app(pub)) |
|
76 |
resp = app.get('/backoffice/workflows/') |
|
77 |
assert 'Comment Templates' in resp |
|
78 |
resp = resp.click('Comment Templates') |
|
79 |
assert 'There are no comment templates defined.' in resp |
|
80 | ||
81 |
resp = resp.click('New') |
|
82 |
resp.form['name'] = 'first comment template' |
|
83 |
resp = resp.form.submit('cancel').follow() |
|
84 |
assert 'There are no comment templates defined.' in resp |
|
85 | ||
86 |
resp = resp.click('New') |
|
87 |
resp.form['name'] = 'first comment template' |
|
88 |
resp = resp.form.submit('submit').follow() |
|
89 |
resp.form['comment'] = 'comment body' |
|
90 |
resp = resp.form.submit('submit').follow() |
|
91 | ||
92 |
resp = resp.click('Edit') |
|
93 |
resp.form['comment'] = 'edited comment body' |
|
94 |
resp.form['attachments$element0'] = 'plop' |
|
95 |
resp = resp.form.submit('submit').follow() |
|
96 | ||
97 |
resp = resp.click('Edit') |
|
98 |
assert resp.form['comment'].value == 'edited comment body' |
|
99 |
assert resp.form['attachments$element0'].value == 'plop' |
|
100 |
resp = resp.form.submit('submit').follow() |
|
101 | ||
102 |
resp = resp.click('Delete') |
|
103 |
resp = resp.form.submit('cancel').follow() |
|
104 |
assert 'first comment template' in resp |
|
105 | ||
106 |
resp = resp.click('Delete') |
|
107 |
resp = resp.form.submit('submit').follow() |
|
108 |
assert 'first comment template' not in resp |
|
109 |
assert 'There are no comment templates defined.' in resp |
|
110 | ||
111 |
resp = resp.click('New') |
|
112 |
resp.form['name'] = 'first comment template' |
|
113 |
resp = resp.form.submit('submit').follow() |
|
114 |
resp.form['comment'] = 'comment body' |
|
115 |
resp = resp.form.submit('submit').follow() |
|
116 | ||
117 |
resp = app.get('/backoffice/workflows/') |
|
118 |
resp = resp.click('Comment Templates') |
|
119 |
assert 'first comment template' in resp |
|
120 | ||
121 | ||
122 |
def test_comment_template_in_use(pub, superuser): |
|
123 |
Workflow.wipe() |
|
124 |
CommentTemplate.wipe() |
|
125 |
workflow = Workflow(name='test workflow') |
|
126 |
st1 = workflow.add_status('Status1') |
|
127 |
item = st1.add_action('register-comment') |
|
128 |
item.comment = 'Hello' |
|
129 |
workflow.store() |
|
130 | ||
131 |
comment_template = CommentTemplate(name='test comment template') |
|
132 |
comment_template.comment = 'test comment' |
|
133 |
comment_template.store() |
|
134 |
assert comment_template.is_in_use() is False |
|
135 | ||
136 |
item.comment_template = comment_template.slug |
|
137 |
workflow.store() |
|
138 |
assert comment_template.is_in_use() is True |
|
139 | ||
140 |
# check workflow usage is displayed |
|
141 |
app = login(get_app(pub)) |
|
142 |
resp = app.get('/backoffice/workflows/comment-templates/%s/' % comment_template.id) |
|
143 |
assert 'Usage in workflows' in resp.text |
|
144 |
assert 'test workflow' in resp.text |
|
145 |
resp.click('test workflow') # make sure the link is ok |
|
146 | ||
147 |
resp = resp.click('Delete') |
|
148 |
assert 'still used' in resp.text |
|
149 | ||
150 | ||
151 |
def test_admin_workflow_edit(pub, superuser): |
|
152 |
CommentTemplateCategory.wipe() |
|
153 |
Workflow.wipe() |
|
154 |
CommentTemplate.wipe() |
|
155 |
comment_template = CommentTemplate(name='test comment template') |
|
156 |
comment_template.comment = 'test comment' |
|
157 |
comment_template.store() |
|
158 | ||
159 |
workflow = Workflow(name='test comment template') |
|
160 |
st1 = workflow.add_status('Status1') |
|
161 |
item = st1.add_action('register-comment') |
|
162 |
item.comment = 'Hello' |
|
163 |
workflow.store() |
|
164 | ||
165 |
app = login(get_app(pub)) |
|
166 |
resp = app.get('/backoffice/workflows/%s/status/%s/items/1/' % (workflow.id, st1.id)) |
|
167 |
assert [o[0] for o in resp.form['comment_template'].options] == ['', 'test-comment-template'] |
|
168 | ||
169 |
cat_b = CommentTemplateCategory(name='Cat B') |
|
170 |
cat_b.store() |
|
171 |
comment_template = CommentTemplate(name='foo bar') |
|
172 |
comment_template.category_id = cat_b.id |
|
173 |
comment_template.store() |
|
174 |
comment_template = CommentTemplate(name='bar foo') |
|
175 |
comment_template.category_id = cat_b.id |
|
176 |
comment_template.store() |
|
177 |
cat_a = CommentTemplateCategory(name='Cat A') |
|
178 |
cat_a.store() |
|
179 |
comment_template = CommentTemplate(name='foo baz') |
|
180 |
comment_template.category_id = cat_a.id |
|
181 |
comment_template.store() |
|
182 | ||
183 |
resp = app.get('/backoffice/workflows/%s/status/%s/items/1/' % (workflow.id, st1.id)) |
|
184 |
assert [o[0] for o in resp.form['comment_template'].options] == [ |
|
185 |
'', |
|
186 |
'foo-baz', |
|
187 |
'bar-foo', |
|
188 |
'foo-bar', |
|
189 |
'test-comment-template', |
|
190 |
] |
|
191 |
resp.form['comment_template'] = 'test-comment-template' |
|
192 |
resp = resp.form.submit('submit') |
|
193 | ||
194 |
workflow = Workflow.get(workflow.id) |
|
195 |
assert workflow.possible_status[0].items[0].comment_template == 'test-comment-template' |
|
196 | ||
197 | ||
198 |
def test_comment_templates_category(pub, superuser): |
|
199 |
CommentTemplateCategory.wipe() |
|
200 |
CommentTemplate.wipe() |
|
201 | ||
202 |
app = login(get_app(pub)) |
|
203 |
resp = app.get('/backoffice/workflows/comment-templates/new') |
|
204 |
assert 'category_id' not in resp.form.fields |
|
205 | ||
206 |
comment_template = CommentTemplate(name='foo') |
|
207 |
comment_template.store() |
|
208 | ||
209 |
resp = app.get('/backoffice/workflows/comment-templates/categories/') |
|
210 |
resp = resp.click('New Category') |
|
211 |
resp.form['name'] = 'a new category' |
|
212 |
resp.form['description'] = 'description of the category' |
|
213 |
resp = resp.form.submit('submit') |
|
214 |
assert CommentTemplateCategory.count() == 1 |
|
215 |
category = CommentTemplateCategory.select()[0] |
|
216 |
assert category.name == 'a new category' |
|
217 | ||
218 |
resp = app.get('/backoffice/workflows/comment-templates/new') |
|
219 |
resp.form['name'] = 'template 2' |
|
220 |
resp = resp.form.submit('submit').follow() |
|
221 |
assert CommentTemplate.count() == 2 |
|
222 |
assert CommentTemplate.get(2).category_id is None |
|
223 |
resp = app.get('/backoffice/workflows/comment-templates/new') |
|
224 |
resp.form['name'] = 'template 3' |
|
225 |
resp.form['category_id'] = str(category.id) |
|
226 |
resp = resp.form.submit('submit').follow() |
|
227 |
assert CommentTemplate.count() == 3 |
|
228 |
assert CommentTemplate.get(3).category_id == str(category.id) |
|
229 | ||
230 |
resp = app.get('/backoffice/workflows/comment-templates/%s/' % comment_template.id) |
|
231 |
resp = resp.click(href=re.compile('^edit$')) |
|
232 |
resp.form['category_id'] = str(category.id) |
|
233 |
resp = resp.form.submit('cancel').follow() |
|
234 |
comment_template.refresh_from_storage() |
|
235 |
assert comment_template.category_id is None |
|
236 | ||
237 |
resp = app.get('/backoffice/workflows/comment-templates/%s/' % comment_template.id) |
|
238 |
resp = resp.click(href=re.compile('^edit$')) |
|
239 |
resp.form['category_id'] = str(category.id) |
|
240 |
resp.form['comment'] = 'comment body' |
|
241 |
resp = resp.form.submit('submit').follow() |
|
242 |
comment_template.refresh_from_storage() |
|
243 |
assert str(comment_template.category_id) == str(category.id) |
|
244 | ||
245 |
resp = app.get('/backoffice/workflows/comment-templates/%s/' % comment_template.id) |
|
246 |
resp = resp.click(href=re.compile('^edit$')) |
|
247 |
assert resp.form['category_id'].value == str(category.id) |
|
248 | ||
249 |
resp = app.get('/backoffice/workflows/comment-templates/categories/') |
|
250 |
resp = resp.click('New Category') |
|
251 |
resp.form['name'] = 'a second category' |
|
252 |
resp.form['description'] = 'description of the category' |
|
253 |
resp = resp.form.submit('submit') |
|
254 |
assert CommentTemplateCategory.count() == 2 |
|
255 |
category2 = [x for x in CommentTemplateCategory.select() if x.id != category.id][0] |
|
256 |
assert category2.name == 'a second category' |
|
257 | ||
258 |
app.get( |
|
259 |
'/backoffice/workflows/comment-templates/categories/update_order?order=%s;%s;' |
|
260 |
% (category2.id, category.id) |
|
261 |
) |
|
262 |
categories = CommentTemplateCategory.select() |
|
263 |
CommentTemplateCategory.sort_by_position(categories) |
|
264 |
assert [x.id for x in categories] == [str(category2.id), str(category.id)] |
|
265 | ||
266 |
app.get( |
|
267 |
'/backoffice/workflows/comment-templates/categories/update_order?order=%s;%s;0' |
|
268 |
% (category.id, category2.id) |
|
269 |
) |
|
270 |
categories = CommentTemplateCategory.select() |
|
271 |
CommentTemplateCategory.sort_by_position(categories) |
|
272 |
assert [x.id for x in categories] == [str(category.id), str(category2.id)] |
|
273 | ||
274 |
resp = app.get('/backoffice/workflows/comment-templates/categories/') |
|
275 |
resp = resp.click('a new category') |
|
276 |
resp = resp.click('Delete') |
|
277 |
resp = resp.form.submit() |
|
278 |
comment_template.refresh_from_storage() |
|
279 |
assert not comment_template.category_id |
|
280 | ||
281 | ||
282 |
def test_workflow_register_comment_template(pub): |
|
283 |
Workflow.wipe() |
|
284 |
CommentTemplate.wipe() |
|
285 | ||
286 |
comment_template = CommentTemplate(name='test comment template') |
|
287 |
comment_template.comment = 'test comment' |
|
288 |
comment_template.store() |
|
289 | ||
290 |
workflow = Workflow(name='test comment template') |
|
291 |
st1 = workflow.add_status('Status1') |
|
292 |
item = st1.add_action('register-comment') |
|
293 |
item.comment = 'Hello' |
|
294 |
item.comment_template = comment_template.slug |
|
295 |
workflow.store() |
|
296 | ||
297 |
formdef = FormDef() |
|
298 |
formdef.name = 'baz' |
|
299 |
formdef.fields = [] |
|
300 |
formdef.store() |
|
301 | ||
302 |
formdata = formdef.data_class()() |
|
303 |
formdata.just_created() |
|
304 |
formdata.store() |
|
305 | ||
306 |
item.perform(formdata) |
|
307 |
assert len(formdata.evolution) == 1 |
|
308 |
assert len(formdata.evolution[0].parts) == 2 |
|
309 |
assert formdata.evolution[-1].parts[1].content == 'test comment' |
|
310 | ||
311 |
# check nothing is registered and an error is logged if the comment template is missing |
|
312 |
CommentTemplate.wipe() |
|
313 |
item.perform(formdata) |
|
314 |
assert len(formdata.evolution) == 1 |
|
315 |
assert len(formdata.evolution[0].parts) == 2 |
|
316 |
assert pub.loggederror_class.count() == 1 |
|
317 |
logged_error = pub.loggederror_class.select()[0] |
|
318 |
assert ( |
|
319 |
logged_error.summary |
|
320 |
== 'reference to invalid comment template test-comment-template in status Status1' |
|
321 |
) |
|
322 | ||
323 | ||
324 |
def test_workflow_register_comment_template_attachments(pub): |
|
325 |
Workflow.wipe() |
|
326 |
CommentTemplate.wipe() |
|
327 | ||
328 |
comment_template = CommentTemplate(name='test comment template') |
|
329 |
comment_template.comment = 'test comment' |
|
330 |
comment_template.attachments = ['form_var_file1_raw'] |
|
331 |
comment_template.store() |
|
332 | ||
333 |
workflow = Workflow(name='test comment template') |
|
334 |
st1 = workflow.add_status('Status1') |
|
335 |
item = st1.add_action('register-comment') |
|
336 |
item.comment = 'Hello' |
|
337 |
item.comment_template = comment_template.slug |
|
338 |
workflow.store() |
|
339 | ||
340 |
formdef = FormDef() |
|
341 |
formdef.name = 'baz' |
|
342 |
formdef.fields = [ |
|
343 |
FileField(id='1', label='File', type='file', varname='file1'), |
|
344 |
] |
|
345 |
formdef.store() |
|
346 | ||
347 |
upload = PicklableUpload('test.jpeg', 'image/jpeg') |
|
348 |
with open(os.path.join(os.path.dirname(__file__), 'image-with-gps-data.jpeg'), 'rb') as fd: |
|
349 |
upload.receive([fd.read()]) |
|
350 |
formdata = formdef.data_class()() |
|
351 |
formdata.data = {'1': upload} |
|
352 |
formdata.just_created() |
|
353 |
formdata.store() |
|
354 |
pub.substitutions.feed(formdata) |
|
355 | ||
356 |
item.perform(formdata) |
|
357 |
assert len(formdata.evolution) == 1 |
|
358 |
assert len(formdata.evolution[0].parts) == 3 |
|
359 |
assert formdata.evolution[-1].parts[2].content == 'test comment' |
|
360 |
assert formdata.evolution[-1].parts[1].base_filename == 'test.jpeg' |
|
361 | ||
362 |
# check two files are sent if attachments are also defined on the action itself. |
|
363 |
item.attachments = ['form_var_file1_raw'] |
|
364 |
item.perform(formdata) |
|
365 |
assert len(formdata.evolution) == 1 |
|
366 |
assert len(formdata.evolution[0].parts) == 6 |
|
367 |
assert formdata.evolution[-1].parts[5].content == 'test comment' |
|
368 |
assert formdata.evolution[-1].parts[4].base_filename == 'test.jpeg' |
|
369 |
assert formdata.evolution[-1].parts[3].base_filename == 'test.jpeg' |
|
370 | ||
371 | ||
372 |
def test_workflow_register_comment_template_empty(pub): |
|
373 |
Workflow.wipe() |
|
374 |
CommentTemplate.wipe() |
|
375 | ||
376 |
comment_template = CommentTemplate(name='test comment template') |
|
377 |
comment_template.comment = None |
|
378 |
comment_template.store() |
|
379 | ||
380 |
workflow = Workflow(name='test comment template') |
|
381 |
st1 = workflow.add_status('Status1') |
|
382 |
item = st1.add_action('register-comment') |
|
383 |
item.comment = 'Hello' |
|
384 |
item.comment_template = comment_template.slug |
|
385 |
workflow.store() |
|
386 | ||
387 |
formdef = FormDef() |
|
388 |
formdef.name = 'baz' |
|
389 |
formdef.store() |
|
390 | ||
391 |
formdata = formdef.data_class()() |
|
392 |
formdata.data = {} |
|
393 |
formdata.just_created() |
|
394 |
formdata.store() |
|
395 |
pub.substitutions.feed(formdata) |
|
396 | ||
397 |
item.perform(formdata) |
|
398 |
assert len(formdata.evolution) == 1 |
|
399 |
assert len(formdata.evolution[0].parts) == 1 |
|
400 | ||
401 | ||
402 |
def test_comment_templates_export(pub, superuser, comment_template): |
|
403 |
app = login(get_app(pub)) |
|
404 |
resp = app.get('/backoffice/workflows/comment-templates/1/') |
|
405 | ||
406 |
resp = resp.click(href='export') |
|
407 |
xml_export = resp.text |
|
408 | ||
409 |
ds = io.StringIO(xml_export) |
|
410 |
comment_template2 = CommentTemplate.import_from_xml(ds) |
|
411 |
assert comment_template2.name == 'test CT' |
|
412 | ||
413 | ||
414 |
def test_comment_templates_import(pub, superuser, comment_template): |
|
415 |
comment_template.slug = 'foobar' |
|
416 |
comment_template.store() |
|
417 |
comment_template_xml = ET.tostring(comment_template.export_to_xml(include_id=True)) |
|
418 |
CommentTemplate.wipe() |
|
419 |
assert CommentTemplate.count() == 0 |
|
420 | ||
421 |
app = login(get_app(pub)) |
|
422 |
resp = app.get('/backoffice/workflows/comment-templates/') |
|
423 |
resp = resp.click(href='import') |
|
424 |
resp.forms[0]['file'] = Upload('comment_template.wcs', comment_template_xml) |
|
425 |
resp = resp.forms[0].submit() |
|
426 |
assert CommentTemplate.count() == 1 |
|
427 |
assert {wc.slug for wc in CommentTemplate.select()} == {'foobar'} |
|
428 | ||
429 |
# check slug |
|
430 |
resp = app.get('/backoffice/workflows/comment-templates/') |
|
431 |
resp = resp.click(href='import') |
|
432 |
resp.forms[0]['file'] = Upload('comment_template.wcs', comment_template_xml) |
|
433 |
resp = resp.forms[0].submit() |
|
434 |
assert CommentTemplate.count() == 2 |
|
435 |
assert {wc.slug for wc in CommentTemplate.select()} == {'foobar', 'test-ct'} |
|
436 |
resp = app.get('/backoffice/workflows/comment-templates/') |
|
437 |
resp = resp.click(href='import') |
|
438 |
resp.forms[0]['file'] = Upload('comment_template.wcs', comment_template_xml) |
|
439 |
resp = resp.forms[0].submit() |
|
440 |
assert CommentTemplate.count() == 3 |
|
441 |
assert {wc.slug for wc in CommentTemplate.select()} == {'foobar', 'test-ct', 'test-ct-1'} |
|
442 | ||
443 |
# import an invalid file |
|
444 |
resp = app.get('/backoffice/workflows/comment-templates/') |
|
445 |
resp = resp.click(href='import') |
|
446 |
resp.form['file'] = Upload('comment_template.wcs', b'garbage') |
|
447 |
resp = resp.form.submit() |
|
448 |
assert 'Invalid File' in resp.text |
|
449 | ||
450 | ||
451 |
def test_comment_templates_duplicate(pub, superuser, comment_template): |
|
452 |
app = login(get_app(pub)) |
|
453 |
resp = app.get('/backoffice/workflows/comment-templates/1/') |
|
454 | ||
455 |
resp = resp.click(href='duplicate') |
|
456 |
assert resp.form['name'].value == 'test CT (copy)' |
|
457 |
resp = resp.form.submit('cancel').follow() |
|
458 |
assert CommentTemplate.count() == 1 |
|
459 | ||
460 |
resp = resp.click(href='duplicate') |
|
461 |
assert resp.form['name'].value == 'test CT (copy)' |
|
462 |
resp = resp.form.submit('submit').follow() |
|
463 |
assert CommentTemplate.count() == 2 |
|
464 | ||
465 |
resp = app.get('/backoffice/workflows/comment-templates/1/') |
|
466 |
resp = resp.click(href='duplicate') |
|
467 |
assert resp.form['name'].value == 'test CT (copy 2)' |
|
468 |
resp.form['name'].value = 'other copy' |
|
469 |
resp = resp.form.submit('submit').follow() |
|
470 |
assert CommentTemplate.count() == 3 |
|
471 |
assert {x.name for x in CommentTemplate.select()} == {'test CT', 'test CT (copy)', 'other copy'} |
|
472 |
assert {x.slug for x in CommentTemplate.select()} == {'test-ct', 'test-ct-copy', 'other-copy'} |
|
473 | ||
474 | ||
475 |
def export_to_indented_xml(comment_template, include_id=False): |
|
476 |
comment_template_xml = comment_template.export_to_xml(include_id=include_id) |
|
477 |
indent(comment_template_xml) |
|
478 |
return comment_template_xml |
|
479 | ||
480 | ||
481 |
def assert_import_export_works(comment_template, include_id=False): |
|
482 |
comment_template2 = CommentTemplate.import_from_xml_tree( |
|
483 |
ET.fromstring(ET.tostring(comment_template.export_to_xml(include_id))), include_id |
|
484 |
) |
|
485 |
assert ET.tostring(export_to_indented_xml(comment_template)) == ET.tostring( |
|
486 |
export_to_indented_xml(comment_template2) |
|
487 |
) |
|
488 |
return comment_template2 |
|
489 | ||
490 | ||
491 |
def test_comment_template(pub): |
|
492 |
comment_template = CommentTemplate(name='test') |
|
493 |
assert_import_export_works(comment_template, include_id=True) |
|
494 | ||
495 | ||
496 |
def test_comment_template_with_category(pub): |
|
497 |
category = CommentTemplateCategory(name='test category') |
|
498 |
category.store() |
|
499 | ||
500 |
comment_template = CommentTemplate(name='test category') |
|
501 |
comment_template.category_id = category.id |
|
502 |
comment_template.store() |
|
503 |
comment_template2 = assert_import_export_works(comment_template, include_id=True) |
|
504 |
assert comment_template2.category_id == comment_template.category_id |
|
505 | ||
506 |
# import with non existing category |
|
507 |
CommentTemplateCategory.wipe() |
|
508 |
export = ET.tostring(comment_template.export_to_xml(include_id=True)) |
|
509 |
comment_template3 = CommentTemplate.import_from_xml_tree(ET.fromstring(export), include_id=True) |
|
510 |
assert comment_template3.category_id is None |
tests/test_snapshots.py | ||
---|---|---|
9 | 9 |
from wcs.blocks import BlockDef |
10 | 10 |
from wcs.carddef import CardDef |
11 | 11 |
from wcs.categories import Category |
12 |
from wcs.comment_templates import CommentTemplate |
|
12 | 13 |
from wcs.data_sources import NamedDataSource |
13 | 14 |
from wcs.fields import CommentField, ItemField, PageField, StringField |
14 | 15 |
from wcs.formdef import FormDef |
... | ... | |
1133 | 1134 |
resp = resp.click('Edit') |
1134 | 1135 | |
1135 | 1136 | |
1137 |
def test_comment_template_snapshot_browse(pub): |
|
1138 |
create_superuser(pub) |
|
1139 |
create_role(pub) |
|
1140 | ||
1141 |
CommentTemplate.wipe() |
|
1142 |
comment_template = CommentTemplate(name='test') |
|
1143 |
comment_template.store() |
|
1144 |
assert pub.snapshot_class.count() == 1 |
|
1145 |
# check calling .store() without changes doesn't create snapshots |
|
1146 |
comment_template.store() |
|
1147 |
assert pub.snapshot_class.count() == 1 |
|
1148 | ||
1149 |
app = login(get_app(pub)) |
|
1150 | ||
1151 |
resp = app.get('/backoffice/workflows/comment-templates/%s/history/' % comment_template.id) |
|
1152 |
snapshot = pub.snapshot_class.select_object_history(comment_template)[0] |
|
1153 |
resp = resp.click(href='%s/view/' % snapshot.id) |
|
1154 |
assert 'This comment template is readonly' in resp.text |
|
1155 |
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text |
|
1156 |
with pytest.raises(IndexError): |
|
1157 |
resp = resp.click('Edit') |
|
1158 | ||
1159 | ||
1136 | 1160 |
def test_category_snapshot_browse(pub): |
1137 | 1161 |
create_superuser(pub) |
1138 | 1162 |
create_role(pub) |
wcs/admin/categories.py | ||
---|---|---|
26 | 26 |
BlockCategory, |
27 | 27 |
CardDefCategory, |
28 | 28 |
Category, |
29 |
CommentTemplateCategory, |
|
29 | 30 |
DataSourceCategory, |
30 | 31 |
MailTemplateCategory, |
31 | 32 |
WorkflowCategory, |
32 | 33 |
) |
34 |
from wcs.comment_templates import CommentTemplate |
|
33 | 35 |
from wcs.data_sources import NamedDataSource |
34 | 36 |
from wcs.formdef import FormDef |
35 | 37 |
from wcs.mail_templates import MailTemplate |
... | ... | |
177 | 179 |
management_roles_hint_text = None |
178 | 180 | |
179 | 181 | |
182 |
class CommentTemplateCategoryUI(CategoryUI): |
|
183 |
category_class = CommentTemplateCategory |
|
184 |
management_roles_hint_text = None |
|
185 | ||
186 | ||
180 | 187 |
class DataSourceCategoryUI(CategoryUI): |
181 | 188 |
category_class = DataSourceCategory |
182 | 189 |
management_roles_hint_text = None |
... | ... | |
334 | 341 |
empty_message = _('No mail template associated to this category.') |
335 | 342 | |
336 | 343 | |
344 |
class CommentTemplateCategoryPage(CategoryPage): |
|
345 |
category_class = CommentTemplateCategory |
|
346 |
category_ui_class = CommentTemplateCategoryUI |
|
347 |
object_class = CommentTemplate |
|
348 |
usage_title = _('Comment templates in this category') |
|
349 |
empty_message = _('No comment template associated to this category.') |
|
350 | ||
351 | ||
337 | 352 |
class DataSourceCategoryPage(CategoryPage): |
338 | 353 |
category_class = DataSourceCategory |
339 | 354 |
category_ui_class = DataSourceCategoryUI |
... | ... | |
452 | 467 |
category_explanation = _('Categories are used to sort the different mail templates.') |
453 | 468 | |
454 | 469 | |
470 |
class CommentTemplateCategoriesDirectory(CategoriesDirectory): |
|
471 |
base_section = 'workflows' |
|
472 |
category_class = CommentTemplateCategory |
|
473 |
category_ui_class = CommentTemplateCategoryUI |
|
474 |
category_page_class = CommentTemplateCategoryPage |
|
475 |
category_explanation = _('Categories are used to sort the different comment templates.') |
|
476 | ||
477 | ||
455 | 478 |
class DataSourceCategoriesDirectory(CategoriesDirectory): |
456 | 479 |
base_section = 'workflows' |
457 | 480 |
category_class = DataSourceCategory |
wcs/admin/comment_templates.py | ||
---|---|---|
1 |
# w.c.s. - web application for online forms |
|
2 |
# Copyright (C) 2005-2022 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or modify |
|
5 |
# it under the terms of the GNU General Public License as published by |
|
6 |
# the Free Software Foundation; either version 2 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU General Public License |
|
15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from quixote import get_publisher, get_response, redirect |
|
18 |
from quixote.directory import Directory |
|
19 |
from quixote.html import TemplateIO, htmltext |
|
20 | ||
21 |
from wcs.admin import utils |
|
22 |
from wcs.admin.categories import CommentTemplateCategoriesDirectory, get_categories |
|
23 |
from wcs.backoffice.snapshots import SnapshotsDirectory |
|
24 |
from wcs.categories import CommentTemplateCategory |
|
25 |
from wcs.comment_templates import CommentTemplate |
|
26 |
from wcs.qommon import _, errors, misc, template |
|
27 |
from wcs.qommon.backoffice.menu import html_top |
|
28 |
from wcs.qommon.form import ( |
|
29 |
ComputedExpressionWidget, |
|
30 |
FileWidget, |
|
31 |
Form, |
|
32 |
HtmlWidget, |
|
33 |
SingleSelectWidget, |
|
34 |
SlugWidget, |
|
35 |
StringWidget, |
|
36 |
TextWidget, |
|
37 |
WidgetList, |
|
38 |
get_session, |
|
39 |
) |
|
40 | ||
41 | ||
42 |
class CommentTemplatesDirectory(Directory): |
|
43 |
_q_exports = ['', 'new', 'categories', ('import', 'p_import')] |
|
44 |
do_not_call_in_templates = True |
|
45 |
categories = CommentTemplateCategoriesDirectory() |
|
46 | ||
47 |
def _q_traverse(self, path): |
|
48 |
if not get_publisher().get_backoffice_root().is_global_accessible('workflows'): |
|
49 |
raise errors.AccessForbiddenError() |
|
50 |
get_response().breadcrumb.append(('comment-templates/', _('Comment Templates'))) |
|
51 |
return super()._q_traverse(path) |
|
52 | ||
53 |
def _q_lookup(self, component): |
|
54 |
return CommentTemplatePage(component) |
|
55 | ||
56 |
def _q_index(self): |
|
57 |
html_top('comment_templates', title=_('Comment Templates')) |
|
58 |
categories = CommentTemplateCategory.select() |
|
59 |
CommentTemplateCategory.sort_by_position(categories) |
|
60 |
comment_templates = CommentTemplate.select(order_by='name') |
|
61 |
if categories: |
|
62 |
categories.append(CommentTemplateCategory(_('Misc'))) |
|
63 |
for category in categories: |
|
64 |
category.comment_templates = [x for x in comment_templates if x.category_id == category.id] |
|
65 |
return template.QommonTemplateResponse( |
|
66 |
templates=['wcs/backoffice/comment-templates.html'], |
|
67 |
context={'view': self, 'comment_templates': comment_templates, 'categories': categories}, |
|
68 |
) |
|
69 | ||
70 |
def new(self): |
|
71 |
form = Form(enctype='multipart/form-data') |
|
72 |
form.add(StringWidget, 'name', title=_('Name'), required=True, size=50) |
|
73 |
category_options = get_categories(CommentTemplateCategory) |
|
74 |
if category_options: |
|
75 |
category_options = [(None, '---', '')] + list(category_options) |
|
76 |
form.add( |
|
77 |
SingleSelectWidget, |
|
78 |
'category_id', |
|
79 |
title=_('Category'), |
|
80 |
options=category_options, |
|
81 |
) |
|
82 |
form.add_submit('submit', _('Add')) |
|
83 |
form.add_submit('cancel', _('Cancel')) |
|
84 |
if form.get_widget('cancel').parse(): |
|
85 |
return redirect('.') |
|
86 | ||
87 |
if form.is_submitted() and not form.has_errors(): |
|
88 |
comment_template = CommentTemplate(name=form.get_widget('name').parse()) |
|
89 |
if form.get_widget('category_id'): |
|
90 |
comment_template.category_id = form.get_widget('category_id').parse() |
|
91 |
comment_template.store() |
|
92 |
return redirect('%s/edit' % comment_template.id) |
|
93 | ||
94 |
get_response().breadcrumb.append(('new', _('New Comment Template'))) |
|
95 |
html_top('comment_templates', title=_('New Comment Template')) |
|
96 |
r = TemplateIO(html=True) |
|
97 |
r += htmltext('<h2>%s</h2>') % _('New Comment Template') |
|
98 |
r += form.render() |
|
99 |
return r.getvalue() |
|
100 | ||
101 |
def p_import(self): |
|
102 |
form = Form(enctype='multipart/form-data') |
|
103 |
import_title = _('Import Comment Template') |
|
104 | ||
105 |
form.add(FileWidget, 'file', title=_('File'), required=True) |
|
106 |
form.add_submit('submit', import_title) |
|
107 |
form.add_submit('cancel', _('Cancel')) |
|
108 | ||
109 |
if form.get_submit() == 'cancel': |
|
110 |
return redirect('.') |
|
111 | ||
112 |
if form.is_submitted() and not form.has_errors(): |
|
113 |
try: |
|
114 |
return self.import_submit(form) |
|
115 |
except ValueError: |
|
116 |
pass |
|
117 | ||
118 |
get_response().breadcrumb.append(('import', _('Import'))) |
|
119 |
html_top('comment_templates', title=import_title) |
|
120 |
r = TemplateIO(html=True) |
|
121 |
r += htmltext('<h2>%s</h2>') % import_title |
|
122 |
r += htmltext('<p>%s</p>') % _('You can install a new comment template by uploading a file.') |
|
123 |
r += form.render() |
|
124 |
return r.getvalue() |
|
125 | ||
126 |
def import_submit(self, form): |
|
127 |
fp = form.get_widget('file').parse().fp |
|
128 | ||
129 |
error = False |
|
130 |
try: |
|
131 |
comment_template = CommentTemplate.import_from_xml(fp) |
|
132 |
get_session().message = ('info', _('This comment template has been successfully imported.')) |
|
133 |
except ValueError: |
|
134 |
error = True |
|
135 | ||
136 |
if error: |
|
137 |
form.set_error('file', _('Invalid File')) |
|
138 |
raise ValueError() |
|
139 | ||
140 |
# check slug unicity |
|
141 |
known_slugs = { |
|
142 |
x.slug: x.id for x in CommentTemplate.select(ignore_migration=True, ignore_errors=True) |
|
143 |
} |
|
144 |
if comment_template.slug in known_slugs: |
|
145 |
comment_template.slug = None # a new one will be set in .store() |
|
146 |
comment_template.store() |
|
147 |
return redirect('%s/' % comment_template.id) |
|
148 | ||
149 | ||
150 |
class CommentTemplatePage(Directory): |
|
151 |
_q_exports = [ |
|
152 |
'', |
|
153 |
'edit', |
|
154 |
'delete', |
|
155 |
'duplicate', |
|
156 |
'export', |
|
157 |
('history', 'snapshots_dir'), |
|
158 |
] |
|
159 |
do_not_call_in_templates = True |
|
160 | ||
161 |
def __init__(self, component, instance=None): |
|
162 |
try: |
|
163 |
self.comment_template = instance or CommentTemplate.get(component) |
|
164 |
except KeyError: |
|
165 |
raise errors.TraversalError() |
|
166 |
get_response().breadcrumb.append((component + '/', self.comment_template.name)) |
|
167 |
self.snapshots_dir = SnapshotsDirectory(self.comment_template) |
|
168 | ||
169 |
def get_sidebar(self): |
|
170 |
r = TemplateIO(html=True) |
|
171 |
if self.comment_template.is_readonly(): |
|
172 |
r += htmltext('<div class="infonotice"><p>%s</p></div>') % _('This comment template is readonly.') |
|
173 |
r += utils.snapshot_info_block(snapshot=self.comment_template.snapshot_object) |
|
174 |
r += htmltext('<ul id="sidebar-actions">') |
|
175 |
if not self.comment_template.is_readonly(): |
|
176 |
r += htmltext('<li><a href="export">%s</a></li>') % _('Export') |
|
177 |
r += htmltext('<li><a href="duplicate" rel="popup">%s</a></li>') % _('Duplicate') |
|
178 |
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete') |
|
179 |
if get_publisher().snapshot_class: |
|
180 |
r += htmltext('<li><a rel="popup" href="history/save">%s</a></li>') % _('Save snapshot') |
|
181 |
r += htmltext('<li><a href="history/">%s</a></li>') % _('History') |
|
182 |
r += htmltext('</ul>') |
|
183 |
return r.getvalue() |
|
184 | ||
185 |
def _q_index(self): |
|
186 |
html_top('comment_templates', title=self.comment_template.name) |
|
187 |
get_response().filter['sidebar'] = self.get_sidebar() |
|
188 |
return template.QommonTemplateResponse( |
|
189 |
templates=['wcs/backoffice/comment-template.html'], |
|
190 |
context={'view': self, 'comment_template': self.comment_template}, |
|
191 |
) |
|
192 | ||
193 |
def get_form(self): |
|
194 |
form = Form(enctype='multipart/form-data', use_tabs=True) |
|
195 |
form.add( |
|
196 |
StringWidget, 'name', title=_('Name'), required=True, size=30, value=self.comment_template.name |
|
197 |
) |
|
198 |
category_options = get_categories(CommentTemplateCategory) |
|
199 |
if category_options: |
|
200 |
category_options = [(None, '---', '')] + list(category_options) |
|
201 |
form.add( |
|
202 |
SingleSelectWidget, |
|
203 |
'category_id', |
|
204 |
title=_('Category'), |
|
205 |
options=category_options, |
|
206 |
value=self.comment_template.category_id, |
|
207 |
) |
|
208 | ||
209 |
form.add( |
|
210 |
TextWidget, |
|
211 |
'description', |
|
212 |
title=_('Description'), |
|
213 |
cols=80, |
|
214 |
rows=3, |
|
215 |
value=self.comment_template.description, |
|
216 |
) |
|
217 |
form.add( |
|
218 |
TextWidget, |
|
219 |
'comment', |
|
220 |
title=_('Comment'), |
|
221 |
value=self.comment_template.comment, |
|
222 |
cols=80, |
|
223 |
rows=15, |
|
224 |
require=True, |
|
225 |
validation_function=ComputedExpressionWidget.validate_template, |
|
226 |
) |
|
227 | ||
228 |
if self.comment_template.slug and not self.comment_template.is_in_use(): |
|
229 |
form.add( |
|
230 |
SlugWidget, |
|
231 |
'slug', |
|
232 |
value=self.comment_template.slug, |
|
233 |
advanced=True, |
|
234 |
) |
|
235 | ||
236 |
form.add( |
|
237 |
WidgetList, |
|
238 |
'attachments', |
|
239 |
title=_('Attachments (templates or Python expressions)'), |
|
240 |
element_type=StringWidget, |
|
241 |
value=self.comment_template.attachments, |
|
242 |
add_element_label=_('Add attachment'), |
|
243 |
element_kwargs={'render_br': False, 'size': 50}, |
|
244 |
advanced=True, |
|
245 |
) |
|
246 | ||
247 |
if not self.comment_template.is_readonly(): |
|
248 |
form.add_submit('submit', _('Submit')) |
|
249 |
form.add_submit('cancel', _('Cancel')) |
|
250 |
return form |
|
251 | ||
252 |
def submit_form(self, form): |
|
253 |
name = form.get_widget('name').parse() |
|
254 |
slug_widget = form.get_widget('slug') |
|
255 |
if slug_widget: |
|
256 |
slug = form.get_widget('slug').parse() |
|
257 | ||
258 |
for comment_template in CommentTemplate.select(): |
|
259 |
if comment_template.id == self.comment_template.id: |
|
260 |
continue |
|
261 |
if slug_widget and slug == comment_template.slug: |
|
262 |
slug_widget.set_error(_('This value is already used.')) |
|
263 |
if form.has_errors(): |
|
264 |
raise ValueError() |
|
265 | ||
266 |
self.comment_template.name = name |
|
267 |
if form.get_widget('category_id'): |
|
268 |
self.comment_template.category_id = form.get_widget('category_id').parse() |
|
269 |
self.comment_template.description = form.get_widget('description').parse() |
|
270 |
self.comment_template.comment = form.get_widget('comment').parse() |
|
271 |
self.comment_template.attachments = form.get_widget('attachments').parse() |
|
272 |
if slug_widget: |
|
273 |
self.comment_template.slug = slug |
|
274 |
self.comment_template.store() |
|
275 | ||
276 |
def edit(self): |
|
277 |
form = self.get_form() |
|
278 |
if form.get_submit() == 'cancel': |
|
279 |
return redirect('.') |
|
280 | ||
281 |
if form.get_submit() == 'submit' and not form.has_errors(): |
|
282 |
try: |
|
283 |
self.submit_form(form) |
|
284 |
except ValueError: |
|
285 |
pass |
|
286 |
else: |
|
287 |
return redirect('.') |
|
288 | ||
289 |
get_response().breadcrumb.append(('edit', _('Edit'))) |
|
290 |
html_top('comment_templates', title=_('Edit Comment Template')) |
|
291 |
r = TemplateIO(html=True) |
|
292 |
r += htmltext('<h2>%s</h2>') % _('Edit Comment Template') |
|
293 |
r += form.render() |
|
294 |
r += get_publisher().substitutions.get_substitution_html_table() |
|
295 | ||
296 |
return r.getvalue() |
|
297 | ||
298 |
def delete(self): |
|
299 |
form = Form(enctype='multipart/form-data') |
|
300 |
if not self.comment_template.is_in_use(): |
|
301 |
form.widgets.append( |
|
302 |
HtmlWidget('<p>%s</p>' % _('You are about to irrevocably delete this comment template.')) |
|
303 |
) |
|
304 |
form.add_submit('delete', _('Submit')) |
|
305 |
else: |
|
306 |
form.widgets.append( |
|
307 |
HtmlWidget('<p>%s</p>' % _('This comment template is still used, it cannot be deleted.')) |
|
308 |
) |
|
309 |
form.add_submit('cancel', _('Cancel')) |
|
310 |
if form.get_widget('cancel').parse(): |
|
311 |
return redirect('.') |
|
312 |
if not form.is_submitted() or form.has_errors(): |
|
313 |
get_response().breadcrumb.append(('delete', _('Delete'))) |
|
314 |
html_top('comment_templates', title=_('Delete Comment Template')) |
|
315 |
r = TemplateIO(html=True) |
|
316 |
r += htmltext('<h2>%s %s</h2>') % (_('Deleting Comment Template:'), self.comment_template.name) |
|
317 |
r += form.render() |
|
318 |
return r.getvalue() |
|
319 |
else: |
|
320 |
self.comment_template.remove_self() |
|
321 |
return redirect('..') |
|
322 | ||
323 |
def export(self): |
|
324 |
return misc.xml_response( |
|
325 |
self.comment_template, |
|
326 |
filename='comment-template-%s.wcs' % self.comment_template.slug, |
|
327 |
content_type='application/x-wcs-comment-template', |
|
328 |
) |
|
329 | ||
330 |
def duplicate(self): |
|
331 |
form = Form(enctype='multipart/form-data') |
|
332 |
name_widget = form.add(StringWidget, 'name', title=_('Name'), required=True, size=30) |
|
333 |
form.add_submit('submit', _('Submit')) |
|
334 |
form.add_submit('cancel', _('Cancel')) |
|
335 |
if form.get_widget('cancel').parse(): |
|
336 |
return redirect('.') |
|
337 | ||
338 |
if not form.is_submitted(): |
|
339 |
original_name = self.comment_template.name |
|
340 |
new_name = '%s %s' % (original_name, _('(copy)')) |
|
341 |
names = [x.name for x in CommentTemplate.select()] |
|
342 |
no = 2 |
|
343 |
while new_name in names: |
|
344 |
new_name = _('%(name)s (copy %(no)d)') % {'name': original_name, 'no': no} |
|
345 |
no += 1 |
|
346 |
name_widget.set_value(new_name) |
|
347 | ||
348 |
if not form.is_submitted() or form.has_errors(): |
|
349 |
html_top('comment_templates', title=_('Duplicate Comment Template')) |
|
350 |
r = TemplateIO(html=True) |
|
351 |
get_response().breadcrumb.append(('duplicate', _('Duplicate'))) |
|
352 |
r += htmltext('<h2>%s</h2>') % _('Duplicate Comment Template') |
|
353 |
r += form.render() |
|
354 |
return r.getvalue() |
|
355 | ||
356 |
self.comment_template.id = None |
|
357 |
self.comment_template.slug = None |
|
358 |
self.comment_template.name = form.get_widget('name').parse() |
|
359 |
self.comment_template.store() |
|
360 |
return redirect('../%s/' % self.comment_template.id) |
wcs/admin/settings.py | ||
---|---|---|
731 | 731 |
form.add(CheckboxWidget, 'workflow_categories', title=_('Workflow Categories'), value=True) |
732 | 732 |
form.add(CheckboxWidget, 'block_categories', title=_('Fields Blocks Categories'), value=True) |
733 | 733 |
form.add(CheckboxWidget, 'mail_template_categories', title=_('Mail Templates Categories'), value=True) |
734 |
form.add( |
|
735 |
CheckboxWidget, 'comment_template_categories', title=_('Comment Templates Categories'), value=True |
|
736 |
) |
|
734 | 737 |
form.add(CheckboxWidget, 'data_source_categories', title=_('Data Sources Categories'), value=True) |
735 | 738 |
form.add(CheckboxWidget, 'settings', title=_('Settings'), value=False) |
736 | 739 |
form.add(CheckboxWidget, 'datasources', title=_('Data sources'), value=True) |
737 | 740 |
form.add(CheckboxWidget, 'mail-templates', title=_('Mail templates'), value=True) |
741 |
form.add(CheckboxWidget, 'comment-templates', title=_('Comment templates'), value=True) |
|
738 | 742 |
form.add(CheckboxWidget, 'wscalls', title=_('Webservice calls'), value=True) |
739 | 743 |
form.add(CheckboxWidget, 'apiaccess', title=_('API access'), value=True) |
740 | 744 |
form.add_submit('submit', _('Submit')) |
... | ... | |
766 | 770 |
'workflow_categories', |
767 | 771 |
'block_categories', |
768 | 772 |
'mail_template_categories', |
773 |
'comment_template_categories', |
|
769 | 774 |
'data_source_categories', |
770 | 775 |
'wscalls', |
771 | 776 |
'mail-templates', |
777 |
'comment-templates', |
|
772 | 778 |
'apiaccess', |
773 | 779 |
): |
774 | 780 |
continue |
... | ... | |
864 | 870 |
'workflow_categories', |
865 | 871 |
'block_categories', |
866 | 872 |
'mail_template_categories', |
873 |
'comment_template_categories', |
|
867 | 874 |
'data_source_categories', |
868 | 875 |
'datasources', |
869 | 876 |
'wscalls', |
870 | 877 |
'mail-templates', |
878 |
'comment-templates', |
|
871 | 879 |
'blockdefs', |
872 | 880 |
'apiaccess', |
873 | 881 |
): |
... | ... | |
927 | 935 |
try: |
928 | 936 |
results = self.import_submit(form) |
929 | 937 |
results['mail_templates'] = results['mail-templates'] |
938 |
results['comment_templates'] = results['comment-templates'] |
|
930 | 939 |
except zipfile.BadZipfile: |
931 | 940 |
results = None |
932 | 941 |
reason = _('Not a valid export file') |
wcs/admin/workflows.py | ||
---|---|---|
63 | 63 |
) |
64 | 64 | |
65 | 65 |
from . import utils |
66 |
from .comment_templates import CommentTemplatesDirectory |
|
66 | 67 |
from .data_sources import NamedDataSourcesDirectory |
67 | 68 |
from .fields import FieldDefPage, FieldsDirectory |
68 | 69 |
from .logged_errors import LoggedErrorsDirectory |
... | ... | |
1944 | 1945 |
('import', 'p_import'), |
1945 | 1946 |
('data-sources', 'data_sources'), |
1946 | 1947 |
('mail-templates', 'mail_templates'), |
1948 |
('comment-templates', 'comment_templates'), |
|
1947 | 1949 |
] |
1948 | 1950 | |
1949 | 1951 |
data_sources = NamedDataSourcesDirectoryInWorkflows() |
1950 | 1952 |
mail_templates = MailTemplatesDirectory() |
1953 |
comment_templates = CommentTemplatesDirectory() |
|
1951 | 1954 |
category_class = WorkflowCategory |
1952 | 1955 |
categories = WorkflowCategoriesDirectory() |
1953 | 1956 | |
... | ... | |
1979 | 1982 |
r += htmltext('<h2>%s</h2>') % _('Workflows') |
1980 | 1983 |
r += htmltext('<span class="actions">') |
1981 | 1984 |
if is_global_accessible(): |
1985 |
r += htmltext('<a href="comment-templates/">%s</a>') % _('Comment Templates') |
|
1982 | 1986 |
r += htmltext('<a href="mail-templates/">%s</a>') % _('Mail Templates') |
1983 | 1987 |
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources') |
1984 | 1988 |
r += htmltext('<a href="categories/">%s</a>') % _('Categories') |
wcs/backoffice/studio.py | ||
---|---|---|
21 | 21 |
from wcs.backoffice.deprecations import DeprecationsDirectory |
22 | 22 |
from wcs.blocks import BlockDef |
23 | 23 |
from wcs.carddef import CardDef |
24 |
from wcs.comment_templates import CommentTemplate |
|
24 | 25 |
from wcs.data_sources import NamedDataSource |
25 | 26 |
from wcs.formdef import FormDef |
26 | 27 |
from wcs.mail_templates import MailTemplate |
... | ... | |
46 | 47 |
backoffice_root = get_publisher().get_backoffice_root() |
47 | 48 |
object_types = [] |
48 | 49 |
if backoffice_root.is_accessible('workflows'): |
49 |
object_types += [Workflow, MailTemplate] |
|
50 |
object_types += [Workflow, MailTemplate, CommentTemplate]
|
|
50 | 51 |
if backoffice_root.is_accessible('forms'): |
51 | 52 |
object_types += [NamedDataSource, BlockDef, FormDef] |
52 | 53 |
if backoffice_root.is_accessible('workflows'): |
... | ... | |
103 | 104 |
extra_links.append(('../forms/blocks/', pgettext('studio', 'Field blocks'))) |
104 | 105 |
if backoffice_root.is_accessible('workflows'): |
105 | 106 |
extra_links.append(('../workflows/mail-templates/', pgettext('studio', 'Mail templates'))) |
106 |
object_types += [Workflow, MailTemplate] |
|
107 |
extra_links.append(('../workflows/comment-templates/', pgettext('studio', 'Comment templates'))) |
|
108 |
object_types += [Workflow, MailTemplate, CommentTemplate] |
|
107 | 109 |
if backoffice_root.is_accessible('forms'): |
108 | 110 |
extra_links.append(('../forms/data-sources/', pgettext('studio', 'Data sources'))) |
109 | 111 |
object_types += [NamedDataSource, BlockDef, FormDef] |
wcs/categories.py | ||
---|---|---|
299 | 299 |
return MailTemplate |
300 | 300 | |
301 | 301 | |
302 |
class CommentTemplateCategory(Category): |
|
303 |
_names = 'comment_template_categories' |
|
304 |
xml_root_node = 'comment_template_category' |
|
305 |
backoffice_class = 'wcs.admin.categories.CommentTemplateCategoryPage' |
|
306 |
backoffice_base_url = 'workflows/comment-templates/categories/' |
|
307 | ||
308 |
# declarations for serialization |
|
309 |
XML_NODES = [ |
|
310 |
('name', 'str'), |
|
311 |
('url_name', 'str'), |
|
312 |
('description', 'str'), |
|
313 |
('position', 'int'), |
|
314 |
] |
|
315 | ||
316 |
@classmethod |
|
317 |
def get_object_class(cls): |
|
318 |
from .comment_templates import CommentTemplate |
|
319 | ||
320 |
return CommentTemplate |
|
321 | ||
322 | ||
302 | 323 |
class DataSourceCategory(Category): |
303 | 324 |
_names = 'data_source_categories' |
304 | 325 |
xml_root_node = 'data_source_category' |
wcs/comment_templates.py | ||
---|---|---|
1 |
# w.c.s. - web application for online forms |
|
2 |
# Copyright (C) 2005-2022 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software; you can redistribute it and/or modify |
|
5 |
# it under the terms of the GNU General Public License as published by |
|
6 |
# the Free Software Foundation; either version 2 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU General Public License |
|
15 |
# along with this program; if not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from collections import defaultdict |
|
18 | ||
19 |
from quixote import get_publisher |
|
20 | ||
21 |
from wcs.categories import CommentTemplateCategory |
|
22 |
from wcs.qommon import _, get_logger |
|
23 |
from wcs.qommon.form import OptGroup |
|
24 |
from wcs.qommon.xml_storage import XmlStorableObject |
|
25 | ||
26 | ||
27 |
class CommentTemplate(XmlStorableObject): |
|
28 |
_names = 'comment-templates' |
|
29 |
xml_root_node = 'comment-template' |
|
30 |
backoffice_class = 'wcs.admin.comment_templates.CommentTemplatePage' |
|
31 |
verbose_name = _('Comment template') |
|
32 |
verbose_name_plural = _('Comment templates') |
|
33 | ||
34 |
name = None |
|
35 |
slug = None |
|
36 |
description = None |
|
37 |
comment = None |
|
38 |
attachments = [] |
|
39 |
category_id = None |
|
40 | ||
41 |
# declarations for serialization |
|
42 |
XML_NODES = [ |
|
43 |
('name', 'str'), |
|
44 |
('slug', 'str'), |
|
45 |
('description', 'str'), |
|
46 |
('comment', 'str'), |
|
47 |
('attachments', 'str_list'), |
|
48 |
] |
|
49 | ||
50 |
def __init__(self, name=None): |
|
51 |
XmlStorableObject.__init__(self) |
|
52 |
self.name = name |
|
53 | ||
54 |
@property |
|
55 |
def category(self): |
|
56 |
return CommentTemplateCategory.get(self.category_id, ignore_errors=True) |
|
57 | ||
58 |
@category.setter |
|
59 |
def category(self, category): |
|
60 |
if category: |
|
61 |
self.category_id = category.id |
|
62 |
elif self.category_id: |
|
63 |
self.category_id = None |
|
64 | ||
65 |
def get_admin_url(self): |
|
66 |
base_url = get_publisher().get_backoffice_url() |
|
67 |
return '%s/workflows/comment-templates/%s/' % (base_url, self.id) |
|
68 | ||
69 |
def store(self, comment=None, *args, **kwargs): |
|
70 |
assert not self.is_readonly() |
|
71 |
if self.slug is None: |
|
72 |
# set slug if it's not yet there |
|
73 |
self.slug = self.get_new_slug() |
|
74 |
super().store(*args, **kwargs) |
|
75 |
if get_publisher().snapshot_class: |
|
76 |
get_publisher().snapshot_class.snap(instance=self, comment=comment) |
|
77 | ||
78 |
def get_places_of_use(self): |
|
79 |
from wcs.workflows import Workflow |
|
80 | ||
81 |
for workflow in Workflow.select(ignore_errors=True, ignore_migration=True): |
|
82 |
for item in workflow.get_all_items(): |
|
83 |
if item.key != 'register-comment': |
|
84 |
continue |
|
85 |
if item.comment_template == self.slug: |
|
86 |
yield workflow |
|
87 |
break |
|
88 | ||
89 |
def is_in_use(self): |
|
90 |
return any(self.get_places_of_use()) |
|
91 | ||
92 |
@classmethod |
|
93 |
def get_as_options_list(cls): |
|
94 |
def get_option(mt): |
|
95 |
option = [mt.slug, mt.name, mt.slug] |
|
96 |
if get_publisher().get_backoffice_root().is_accessible('workflows'): |
|
97 |
option.append({'data-goto-url': mt.get_admin_url()}) |
|
98 |
return option |
|
99 | ||
100 |
comment_templates_by_category_names = defaultdict(list) |
|
101 |
for comment_template in cls.select(order_by='name'): |
|
102 |
name = '' |
|
103 |
if comment_template.category: |
|
104 |
name = comment_template.category.name |
|
105 |
comment_templates_by_category_names[name].append(comment_template) |
|
106 |
category_names = list(comment_templates_by_category_names.keys()) |
|
107 |
if len(category_names) == 1 and category_names[0] == '': |
|
108 |
# no category found |
|
109 |
return [get_option(mt) for mt in comment_templates_by_category_names['']] |
|
110 |
options = [] |
|
111 |
# sort categories |
|
112 |
category_names = sorted(category_names) |
|
113 |
# comment template without categories at the end |
|
114 |
if category_names[0] == '': |
|
115 |
category_names = category_names[1:] + [''] |
|
116 |
# group by category name |
|
117 |
for name in category_names: |
|
118 |
options.append(OptGroup(name or _('Without category'))) |
|
119 |
options.extend([get_option(mt) for mt in comment_templates_by_category_names[name]]) |
|
120 |
return options |
|
121 | ||
122 |
@classmethod |
|
123 |
def get_by_slug(cls, slug, ignore_errors=True): |
|
124 |
comment_template = super().get_by_slug(slug, ignore_errors=ignore_errors) |
|
125 |
if comment_template is None: |
|
126 |
get_logger().warning("comment template '%s' does not exist" % slug) |
|
127 |
return comment_template |
|
128 | ||
129 |
def export_to_xml(self, include_id=False): |
|
130 |
root = super().export_to_xml(include_id=include_id) |
|
131 |
CommentTemplateCategory.object_category_xml_export(self, root, include_id=include_id) |
|
132 |
return root |
|
133 | ||
134 |
@classmethod |
|
135 |
def import_from_xml_tree(cls, tree, include_id=False, **kwargs): |
|
136 |
comment_template = super().import_from_xml_tree(tree, include_id=include_id, **kwargs) |
|
137 |
CommentTemplateCategory.object_category_xml_import(comment_template, tree, include_id=include_id) |
|
138 |
return comment_template |
wcs/publisher.py | ||
---|---|---|
193 | 193 |
'workflow_categories': 0, |
194 | 194 |
'block_categories': 0, |
195 | 195 |
'mail_template_categories': 0, |
196 |
'comment_template_categories': 0, |
|
196 | 197 |
'data_source_categories': 0, |
197 | 198 |
'roles': 0, |
198 | 199 |
'settings': 0, |
199 | 200 |
'datasources': 0, |
200 | 201 |
'wscalls': 0, |
201 | 202 |
'mail-templates': 0, |
203 |
'comment-templates': 0, |
|
202 | 204 |
'blockdefs': 0, |
203 | 205 |
'apiaccess': 0, |
204 | 206 |
} |
wcs/snapshots.py | ||
---|---|---|
203 | 203 |
from wcs.blocks import BlockDef |
204 | 204 |
from wcs.carddef import CardDef |
205 | 205 |
from wcs.categories import BlockCategory, CardDefCategory, Category, WorkflowCategory |
206 |
from wcs.comment_templates import CommentTemplate |
|
206 | 207 |
from wcs.data_sources import NamedDataSource |
207 | 208 |
from wcs.formdef import FormDef |
208 | 209 |
from wcs.mail_templates import MailTemplate |
... | ... | |
217 | 218 |
Workflow, |
218 | 219 |
NamedWsCall, |
219 | 220 |
MailTemplate, |
221 |
CommentTemplate, |
|
220 | 222 |
Category, |
221 | 223 |
CardDefCategory, |
222 | 224 |
WorkflowCategory, |
wcs/templates/wcs/backoffice/comment-template.html | ||
---|---|---|
1 |
{% extends "wcs/backoffice/base.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block appbar-title %}{% trans "Comment Template" %} - {{ comment_template.name }}{% endblock %} |
|
5 | ||
6 |
{% block appbar-actions %} |
|
7 |
{% if not comment_template.is_readonly %} |
|
8 |
<a href="edit">{% trans "Edit" %}</a> |
|
9 |
{% endif %} |
|
10 |
{% endblock %} |
|
11 | ||
12 |
{% block content %} |
|
13 |
{% if comment_template.description %} |
|
14 |
<div class="bo-block">{{ comment_template.description }}</div> |
|
15 |
{% endif %} |
|
16 | ||
17 |
{% if comment_template.comment %} |
|
18 |
<div class="section"> |
|
19 |
<div class="comment-comment">{{ comment_template.comment }}</div> |
|
20 |
</div> |
|
21 | ||
22 |
{% for workflow in comment_template.get_places_of_use %} |
|
23 |
{% if forloop.first %} |
|
24 |
<div class="section"> |
|
25 |
<h3>{% trans "Usage in workflows" %}</h3> |
|
26 |
<ul class="objects-list single-links"> |
|
27 |
{% endif %} |
|
28 |
<li><a href="{{ workflow.get_admin_url }}">{{ workflow.name }}</a></li> |
|
29 |
{% if forloop.last %} |
|
30 |
</ul> |
|
31 |
</div> |
|
32 |
{% endif %} |
|
33 |
{% endfor %} |
|
34 | ||
35 |
{% else %} |
|
36 |
<div class="infonotice">{% trans "This comment template still needs to be configured." %}</div> |
|
37 |
{% endif %} |
|
38 | ||
39 |
{% endblock %} |
wcs/templates/wcs/backoffice/comment-templates.html | ||
---|---|---|
1 |
{% extends "wcs/backoffice/base.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block appbar-title %}{% trans "Comment Templates" %}{% endblock %} |
|
5 | ||
6 |
{% block appbar-actions %} |
|
7 |
<a href="categories/">{% trans "Categories" %}</a> |
|
8 |
<a rel="popup" href="import">{% trans "Import" %}</a> |
|
9 |
<a rel="popup" href="new">{% trans "New comment template" %}</a> |
|
10 |
{% endblock %} |
|
11 | ||
12 |
{% block content %} |
|
13 |
{% if categories %} |
|
14 |
{% for category in categories %} |
|
15 |
{% if category.comment_templates %} |
|
16 |
<div class="section"> |
|
17 |
<h2>{{ category.name }}</h2> |
|
18 |
<ul class="objects-list single-links"> |
|
19 |
{% for comment_template in category.comment_templates %} |
|
20 |
<li><a href="{{ comment_template.id }}/">{{ comment_template.name }}</a></li> |
|
21 |
{% endfor %} |
|
22 |
</ul> |
|
23 |
</div> |
|
24 |
{% endif %} |
|
25 |
{% endfor %} |
|
26 |
{% elif comment_templates %} |
|
27 |
<ul class="objects-list single-links"> |
|
28 |
{% for comment_template in comment_templates %} |
|
29 |
<li><a href="{{ comment_template.id }}/">{{ comment_template.name }}</a></li> |
|
30 |
{% endfor %} |
|
31 |
</ul> |
|
32 |
{% else %} |
|
33 |
<div class="infonotice"> |
|
34 |
{% trans "There are no comment templates defined." %} |
|
35 |
</div> |
|
36 |
{% endif %} |
|
37 |
{% endblock %} |
wcs/templates/wcs/backoffice/settings/import.html | ||
---|---|---|
33 | 33 |
{% if results.workflow_categories %} |
34 | 34 |
<li>{% blocktrans count counter=results.workflow_categories %}1 workflow category{% plural %}{{ counter }} workflow categories{% endblocktrans %}</li> |
35 | 35 |
{% endif %} |
36 |
{% if results.block_categories %} |
|
37 |
<li>{% blocktrans count counter=results.block_categories %}1 block category{% plural %}{{ counter }} block categories{% endblocktrans %}</li> |
|
38 |
{% endif %} |
|
39 |
{% if results.mail_template_categories %} |
|
40 |
<li>{% blocktrans count counter=results.mail_template_categories %}1 mail template category{% plural %}{{ counter }} mail template categories{% endblocktrans %}</li> |
|
41 |
{% endif %} |
|
42 |
{% if results.comment_template_categories %} |
|
43 |
<li>{% blocktrans count counter=results.comment_template_categories %}1 comment template category{% plural %}{{ counter }} comment template categories{% endblocktrans %}</li> |
|
44 |
{% endif %} |
|
45 |
{% if results.data_source_categories %} |
|
46 |
<li>{% blocktrans count counter=results.data_source_categories %}1 data source category{% plural %}{{ counter }} data source categories{% endblocktrans %}</li> |
|
47 |
{% endif %} |
|
36 | 48 |
{% if results.datasources %} |
37 | 49 |
<li>{% blocktrans count counter=results.datasources %}1 data source{% plural %}{{ counter }} data sources{% endblocktrans %}</li> |
38 | 50 |
{% endif %} |
39 | 51 |
{% if results.mail_templates %} |
40 | 52 |
<li>{% blocktrans count counter=results.mail_templates %}1 mail template{% plural %}{{ counter }} mail templates{% endblocktrans %}</li> |
41 | 53 |
{% endif %} |
54 |
{% if results.comment_templates %} |
|
55 |
<li>{% blocktrans count counter=results.comment_templates %}1 comment template{% plural %}{{ counter }} comment templates{% endblocktrans %}</li> |
|
56 |
{% endif %} |
|
42 | 57 |
{% if results.wscalls %} |
43 | 58 |
<li>{% blocktrans count counter=results.wscalls %}1 webservice call{% plural %}{{ counter }} webservice calls{% endblocktrans %}</li> |
44 | 59 |
{% endif %} |
wcs/wf/register_comment.py | ||
---|---|---|
17 | 17 |
from quixote import get_publisher |
18 | 18 |
from quixote.html import htmltext |
19 | 19 | |
20 |
from wcs.comment_templates import CommentTemplate |
|
20 | 21 |
from wcs.workflows import ( |
21 | 22 |
AttachmentEvolutionPart, |
22 | 23 |
EvolutionPart, |
... | ... | |
26 | 27 |
) |
27 | 28 | |
28 | 29 |
from ..qommon import _, ezt |
29 |
from ..qommon.form import TextWidget, WidgetListOfRoles |
|
30 |
from ..qommon.form import SingleSelectWidget, TextWidget, WidgetListOfRoles
|
|
30 | 31 |
from ..qommon.template import TemplateError |
31 | 32 | |
32 | 33 | |
... | ... | |
84 | 85 |
category = 'interaction' |
85 | 86 | |
86 | 87 |
comment = None |
88 |
comment_template = None |
|
87 | 89 |
to = None |
88 | 90 |
attachments = None |
89 | 91 | |
90 | 92 |
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None, **kwargs): |
91 | 93 |
super().add_parameters_widgets(form, parameters, prefix=prefix, formdef=formdef, **kwargs) |
94 |
subject_body_attrs = {} |
|
92 | 95 |
if 'comment' in parameters: |
96 |
if CommentTemplate.count(): |
|
97 |
subject_body_attrs = { |
|
98 |
'data-dynamic-display-value': '', |
|
99 |
'data-dynamic-display-child-of': '%scomment_template' % prefix, |
|
100 |
} |
|
101 |
if 'comment' in parameters: |
|
102 |
form.add( |
|
103 |
TextWidget, |
|
104 |
'%scomment' % prefix, |
|
105 |
title=_('Message'), |
|
106 |
value=self.comment, |
|
107 |
cols=80, |
|
108 |
rows=10, |
|
109 |
attrs=subject_body_attrs, |
|
110 |
) |
|
111 |
if 'comment_template' in parameters and CommentTemplate.count(): |
|
93 | 112 |
form.add( |
94 |
TextWidget, '%scomment' % prefix, title=_('Message'), value=self.comment, cols=80, rows=10 |
|
113 |
SingleSelectWidget, |
|
114 |
'%scomment_template' % prefix, |
|
115 |
title=_('Comment Template'), |
|
116 |
value=self.comment_template, |
|
117 |
options=[(None, '', '')] + CommentTemplate.get_as_options_list(), |
|
118 |
attrs={'data-dynamic-display-parent': 'true'}, |
|
95 | 119 |
) |
96 | 120 |
if 'to' in parameters: |
97 | 121 |
form.add( |
... | ... | |
105 | 129 |
) |
106 | 130 | |
107 | 131 |
def get_parameters(self): |
108 |
return ('comment', 'to', 'attachments', 'condition') |
|
132 |
return ('comment_template', 'comment', 'to', 'attachments', 'condition')
|
|
109 | 133 | |
110 | 134 |
def attach_uploads_to_formdata(self, formdata, uploads, to): |
111 | 135 |
if not formdata.evolution[-1].parts: |
... | ... | |
124 | 148 |
if not formdata.evolution: |
125 | 149 |
return |
126 | 150 | |
151 |
if self.comment_template: |
|
152 |
comment_template = CommentTemplate.get_by_slug(self.comment_template) |
|
153 |
if comment_template: |
|
154 |
comment = comment_template.comment |
|
155 |
extra_attachments = comment_template.attachments |
|
156 |
else: |
|
157 |
message = _( |
|
158 |
'reference to invalid comment template %(comment_template)s in status %(status)s' |
|
159 |
) % { |
|
160 |
'status': self.parent.name, |
|
161 |
'comment_template': self.comment_template, |
|
162 |
} |
|
163 |
get_publisher().record_error(message, formdata=formdata, status_item=self) |
|
164 |
return |
|
165 |
else: |
|
166 |
comment = self.comment |
|
167 |
extra_attachments = None |
|
168 | ||
127 | 169 |
# process attachments first, they might be used in the comment |
128 | 170 |
# (with substitution vars) |
129 |
if self.attachments: |
|
130 |
uploads = self.convert_attachments_to_uploads() |
|
171 |
if self.attachments or extra_attachments:
|
|
172 |
uploads = self.convert_attachments_to_uploads(extra_attachments)
|
|
131 | 173 |
self.attach_uploads_to_formdata(formdata, uploads, self.to) |
132 | 174 |
formdata.store() # store and invalidate cache, so references can be used in the comment message. |
133 | 175 | |
134 | 176 |
# the comment can use attachments done above |
135 |
if self.comment:
|
|
177 |
if comment: |
|
136 | 178 |
try: |
137 | 179 |
formdata.evolution[-1].add_part( |
138 |
JournalEvolutionPart(formdata, get_publisher().translate(self.comment), self.to)
|
|
180 |
JournalEvolutionPart(formdata, get_publisher().translate(comment), self.to) |
|
139 | 181 |
) |
140 | 182 |
formdata.store() |
141 | 183 |
except TemplateError as e: |
142 |
- |