workflows: add comment template management (#39178)
This commit is contained in:
parent
c68eb965c7
commit
2152ddbe67
|
@ -15,17 +15,21 @@ from webtest import Upload
|
|||
|
||||
from wcs import fields
|
||||
from wcs.api_access import ApiAccess
|
||||
from wcs.blocks import BlockDef
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.categories import (
|
||||
BlockCategory,
|
||||
CardDefCategory,
|
||||
Category,
|
||||
CommentTemplateCategory,
|
||||
DataSourceCategory,
|
||||
MailTemplateCategory,
|
||||
WorkflowCategory,
|
||||
)
|
||||
from wcs.comment_templates import CommentTemplate
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.mail_templates import MailTemplate
|
||||
from wcs.qommon.form import UploadedFile
|
||||
from wcs.qommon.http_request import HTTPRequest
|
||||
from wcs.wf.export_to_model import ExportToModel
|
||||
|
@ -97,7 +101,11 @@ def test_settings_export_import(pub):
|
|||
ApiAccess.wipe()
|
||||
BlockCategory.wipe()
|
||||
MailTemplateCategory.wipe()
|
||||
CommentTemplateCategory.wipe()
|
||||
DataSourceCategory.wipe()
|
||||
MailTemplate.wipe()
|
||||
CommentTemplate.wipe()
|
||||
BlockDef.wipe()
|
||||
|
||||
wipe()
|
||||
create_superuser(pub)
|
||||
|
@ -132,9 +140,13 @@ def test_settings_export_import(pub):
|
|||
WorkflowCategory(name='foobaz').store()
|
||||
BlockCategory(name='category for blocks').store()
|
||||
MailTemplateCategory(name='category for mail templates').store()
|
||||
CommentTemplateCategory(name='category for mail templates').store()
|
||||
DataSourceCategory(name='category for data sources').store()
|
||||
MailTemplate(name='Mail templates').store()
|
||||
CommentTemplate(name='Comment templates').store()
|
||||
pub.role_class(name='qux').store()
|
||||
NamedDataSource(name='quux').store()
|
||||
BlockDef(name='blockdef').store()
|
||||
ds = NamedDataSource(name='agenda')
|
||||
ds.external = 'agenda'
|
||||
ds.store()
|
||||
|
@ -179,10 +191,13 @@ def test_settings_export_import(pub):
|
|||
assert 'carddef_categories/1' in filelist
|
||||
assert 'workflow_categories/1' in filelist
|
||||
assert 'block_categories/1' in filelist
|
||||
assert 'data_source_categories/1' in filelist
|
||||
assert 'mail_template_categories/1' in filelist
|
||||
assert 'comment_template_categories/1' in filelist
|
||||
assert 'data_source_categories/1' in filelist
|
||||
assert 'datasources/1' in filelist
|
||||
assert 'datasources/2' not in filelist # agenda datasource, not exported
|
||||
assert 'mail-templates/1' in filelist
|
||||
assert 'comment-templates/1' in filelist
|
||||
assert 'wscalls/corge' in filelist
|
||||
assert 'apiaccess/1' in filelist
|
||||
for filename in filelist:
|
||||
|
@ -204,20 +219,42 @@ def test_settings_export_import(pub):
|
|||
resp.form['file'] = Upload('export.wcs', zip_content.getvalue())
|
||||
resp = resp.form.submit('submit')
|
||||
assert 'Imported successfully' in resp.text
|
||||
assert '1 form' in resp.text
|
||||
assert '1 card' in resp.text
|
||||
assert '2 categories' in resp.text
|
||||
assert '1 card category' in resp.text
|
||||
assert '1 workflow category' in resp.text
|
||||
assert '1 form</li>' in resp.text
|
||||
assert '1 card</li>' in resp.text
|
||||
assert '1 fields block</li>' in resp.text
|
||||
assert '1 workflow</li>' in resp.text
|
||||
assert '1 role</li>' in resp.text
|
||||
assert '2 categories</li>' in resp.text
|
||||
assert '1 card category</li>' in resp.text
|
||||
assert '1 workflow category</li>' in resp.text
|
||||
assert '1 block category</li>' in resp.text
|
||||
assert '1 mail template category</li>' in resp.text
|
||||
assert '1 comment template category</li>' in resp.text
|
||||
assert '1 data source category</li>' in resp.text
|
||||
assert '1 data source</li>' in resp.text
|
||||
assert '1 mail template</li>' in resp.text
|
||||
assert '1 comment template</li>' in resp.text
|
||||
assert '1 webservice call</li>' in resp.text
|
||||
assert '1 API access</li>' in resp.text
|
||||
assert FormDef.count() == 1
|
||||
assert FormDef.select()[0].url_name == 'foo'
|
||||
assert CardDef.count() == 1
|
||||
assert CardDef.select()[0].url_name == 'bar'
|
||||
assert BlockDef.count() == 1
|
||||
assert Workflow.count() == 1
|
||||
assert pub.role_class.count() == 1
|
||||
assert Category.count() == 2
|
||||
assert CardDefCategory.count() == 1
|
||||
assert WorkflowCategory.count() == 1
|
||||
assert BlockCategory.count() == 1
|
||||
assert MailTemplateCategory.count() == 1
|
||||
assert CommentTemplateCategory.count() == 1
|
||||
assert DataSourceCategory.count() == 1
|
||||
assert NamedDataSource.count() == 1
|
||||
assert MailTemplate.count() == 1
|
||||
assert CommentTemplate.count() == 1
|
||||
assert NamedWsCall.count() == 1
|
||||
assert ApiAccess.count() == 1
|
||||
assert pub.role_class.count() == 1
|
||||
|
||||
# check roles are found by name
|
||||
wipe()
|
||||
|
|
|
@ -5,6 +5,7 @@ import pytest
|
|||
|
||||
from wcs.blocks import BlockDef
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.comment_templates import CommentTemplate
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.mail_templates import MailTemplate
|
||||
|
@ -48,6 +49,7 @@ def test_studio_home(pub):
|
|||
assert '../settings/data-sources/' not in resp.text
|
||||
assert '../forms/blocks/' in resp.text
|
||||
assert '../workflows/mail-templates/' in resp.text
|
||||
assert '../workflows/comment-templates/' in resp.text
|
||||
assert '../settings/wscalls/' in resp.text
|
||||
assert 'Recent errors' in resp.text
|
||||
|
||||
|
@ -136,17 +138,36 @@ def test_studio_home_recent_changes(pub):
|
|||
NamedDataSource.wipe()
|
||||
FormDef.wipe()
|
||||
MailTemplate.wipe()
|
||||
CommentTemplate.wipe()
|
||||
Workflow.wipe()
|
||||
NamedWsCall.wipe()
|
||||
|
||||
objects = defaultdict(list)
|
||||
for i in range(6):
|
||||
for klass in [BlockDef, CardDef, NamedDataSource, FormDef, MailTemplate, Workflow, NamedWsCall]:
|
||||
for klass in [
|
||||
BlockDef,
|
||||
CardDef,
|
||||
NamedDataSource,
|
||||
FormDef,
|
||||
MailTemplate,
|
||||
CommentTemplate,
|
||||
Workflow,
|
||||
NamedWsCall,
|
||||
]:
|
||||
obj = klass()
|
||||
obj.name = 'foo %s' % i
|
||||
obj.store()
|
||||
objects[klass.xml_root_node].append(obj)
|
||||
for klass in [BlockDef, CardDef, NamedDataSource, FormDef, MailTemplate, Workflow, NamedWsCall]:
|
||||
for klass in [
|
||||
BlockDef,
|
||||
CardDef,
|
||||
NamedDataSource,
|
||||
FormDef,
|
||||
MailTemplate,
|
||||
CommentTemplate,
|
||||
Workflow,
|
||||
NamedWsCall,
|
||||
]:
|
||||
assert pub.snapshot_class.count(clause=[Equal('object_type', klass.xml_root_node)]) == 6
|
||||
# 2 snapshots for this one, but will be displayed only once
|
||||
objects[klass.xml_root_node][-1].name += ' bar'
|
||||
|
@ -184,14 +205,18 @@ def test_studio_home_recent_changes(pub):
|
|||
assert (
|
||||
'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][i].id not in resp
|
||||
)
|
||||
assert (
|
||||
'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][i].id
|
||||
not in resp
|
||||
)
|
||||
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][i].id not in resp
|
||||
assert 'backoffice/settings/wscalls/%s/' % objects[NamedWsCall.xml_root_node][i].id not in resp
|
||||
|
||||
# too old
|
||||
assert 'backoffice/forms/blocks/%s/' % objects[BlockDef.xml_root_node][5].id not in resp
|
||||
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][5].id not in resp
|
||||
assert 'backoffice/settings/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id not in resp
|
||||
# only 5 elements
|
||||
assert 'backoffice/settings/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id in resp
|
||||
assert (
|
||||
'backoffice/forms/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id not in resp
|
||||
) # not this url
|
||||
|
@ -201,6 +226,7 @@ def test_studio_home_recent_changes(pub):
|
|||
)
|
||||
assert 'backoffice/forms/%s/' % objects[FormDef.xml_root_node][5].id in resp
|
||||
assert 'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][5].id in resp
|
||||
assert 'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][5].id in resp
|
||||
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][5].id in resp
|
||||
assert 'backoffice/settings/wscalls/%s/' % objects[NamedWsCall.xml_root_node][5].id in resp
|
||||
|
||||
|
@ -227,11 +253,15 @@ def test_studio_home_recent_changes(pub):
|
|||
assert (
|
||||
'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][i].id not in resp
|
||||
)
|
||||
assert (
|
||||
'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][i].id
|
||||
not in resp
|
||||
)
|
||||
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][i].id not in resp
|
||||
# too old
|
||||
assert 'backoffice/forms/blocks/%s/' % objects[BlockDef.xml_root_node][5].id not in resp
|
||||
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][5].id not in resp
|
||||
# only 5 elements
|
||||
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][5].id in resp
|
||||
assert 'backoffice/forms/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id in resp
|
||||
assert (
|
||||
'backoffice/workflows/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id
|
||||
|
@ -239,6 +269,7 @@ def test_studio_home_recent_changes(pub):
|
|||
)
|
||||
assert 'backoffice/forms/%s/' % objects[FormDef.xml_root_node][5].id in resp
|
||||
assert 'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][5].id in resp
|
||||
assert 'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][5].id in resp
|
||||
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][5].id in resp
|
||||
|
||||
pub.cfg['admin-permissions'] = {}
|
||||
|
@ -256,7 +287,7 @@ def test_studio_home_recent_changes(pub):
|
|||
assert 'backoffice/forms/%s/' % objects[FormDef.xml_root_node][i].id not in resp
|
||||
assert 'backoffice/settings/wscalls/%s/' % objects[NamedWsCall.xml_root_node][i].id not in resp
|
||||
# too old
|
||||
for i in range(4):
|
||||
for i in range(5):
|
||||
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][i].id not in resp
|
||||
assert (
|
||||
'backoffice/workflows/data-sources/%s/' % objects[NamedDataSource.xml_root_node][i].id not in resp
|
||||
|
@ -264,16 +295,16 @@ def test_studio_home_recent_changes(pub):
|
|||
assert (
|
||||
'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][i].id not in resp
|
||||
)
|
||||
assert (
|
||||
'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][i].id
|
||||
not in resp
|
||||
)
|
||||
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][i].id not in resp
|
||||
# too old
|
||||
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][4].id not in resp
|
||||
assert 'backoffice/workflows/data-sources/%s/' % objects[NamedDataSource.xml_root_node][4].id not in resp
|
||||
assert 'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][4].id not in resp
|
||||
# only 5 elements
|
||||
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][4].id in resp
|
||||
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][5].id in resp
|
||||
assert 'backoffice/workflows/data-sources/%s/' % objects[NamedDataSource.xml_root_node][5].id in resp
|
||||
assert 'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][5].id in resp
|
||||
assert 'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][5].id in resp
|
||||
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][5].id in resp
|
||||
|
||||
pub.cfg['admin-permissions'] = {}
|
||||
|
@ -296,6 +327,10 @@ def test_studio_home_recent_changes(pub):
|
|||
assert (
|
||||
'backoffice/workflows/mail-templates/%s/' % objects[MailTemplate.xml_root_node][i].id not in resp
|
||||
)
|
||||
assert (
|
||||
'backoffice/workflows/comment-templates/%s/' % objects[CommentTemplate.xml_root_node][i].id
|
||||
not in resp
|
||||
)
|
||||
assert 'backoffice/workflows/%s/' % objects[Workflow.xml_root_node][i].id not in resp
|
||||
# too old
|
||||
assert 'backoffice/cards/%s/' % objects[CardDef.xml_root_node][0].id not in resp
|
||||
|
@ -324,11 +359,11 @@ def test_studio_home_recent_changes(pub):
|
|||
pub.cfg['admin-permissions'] = {}
|
||||
pub.write_cfg()
|
||||
resp = app.get('/backoffice/studio/all-changes/')
|
||||
assert '(1-20/42)' in resp
|
||||
assert '(1-20/48)' in resp
|
||||
resp = resp.click('<!--Next Page-->')
|
||||
assert '21-40/42' in resp.text
|
||||
assert '21-40/48' in resp.text
|
||||
resp = resp.click('<!--Next Page-->')
|
||||
assert '41-42/42' in resp.text
|
||||
assert '41-48/48' in resp.text
|
||||
|
||||
user.is_admin = False
|
||||
user.store()
|
||||
|
|
|
@ -0,0 +1,510 @@
|
|||
import io
|
||||
import os
|
||||
import re
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import pytest
|
||||
from quixote import cleanup
|
||||
from webtest import Upload
|
||||
|
||||
from wcs.categories import CommentTemplateCategory
|
||||
from wcs.comment_templates import CommentTemplate
|
||||
from wcs.fields import FileField
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.qommon.http_request import HTTPRequest
|
||||
from wcs.qommon.ident.password_accounts import PasswordAccount
|
||||
from wcs.qommon.misc import indent_xml as indent
|
||||
from wcs.qommon.upload_storage import PicklableUpload
|
||||
from wcs.workflows import Workflow
|
||||
|
||||
from .utilities import clean_temporary_pub, create_temporary_pub, get_app, login
|
||||
|
||||
|
||||
def setup_module(module):
|
||||
cleanup()
|
||||
|
||||
|
||||
def teardown_module(module):
|
||||
clean_temporary_pub()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pub(request):
|
||||
pub = create_temporary_pub()
|
||||
req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
|
||||
pub.set_app_dir(req)
|
||||
pub._set_request(req)
|
||||
pub.cfg['identification'] = {'methods': ['password']}
|
||||
pub.write_cfg()
|
||||
return pub
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def superuser(pub):
|
||||
if pub.user_class.select(lambda x: x.name == 'admin'):
|
||||
user1 = pub.user_class.select(lambda x: x.name == 'admin')[0]
|
||||
user1.is_admin = True
|
||||
user1.store()
|
||||
return user1
|
||||
|
||||
user1 = pub.user_class(name='admin')
|
||||
user1.is_admin = True
|
||||
user1.store()
|
||||
|
||||
account1 = PasswordAccount(id='admin')
|
||||
account1.set_password('admin')
|
||||
account1.user_id = user1.id
|
||||
account1.store()
|
||||
|
||||
return user1
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def comment_template():
|
||||
CommentTemplate.wipe()
|
||||
comment_template = CommentTemplate(name='test CT')
|
||||
comment_template.comment = 'test comment'
|
||||
comment_template.store()
|
||||
return comment_template
|
||||
|
||||
|
||||
def test_comment_templates_basics(pub, superuser):
|
||||
CommentTemplateCategory.wipe()
|
||||
CommentTemplate.wipe()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/')
|
||||
assert 'Comment Templates' in resp
|
||||
resp = resp.click('Comment Templates')
|
||||
assert 'There are no comment templates defined.' in resp
|
||||
|
||||
resp = resp.click('New')
|
||||
resp.form['name'] = 'first comment template'
|
||||
resp = resp.form.submit('cancel').follow()
|
||||
assert 'There are no comment templates defined.' in resp
|
||||
|
||||
resp = resp.click('New')
|
||||
resp.form['name'] = 'first comment template'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
resp.form['comment'] = 'comment body'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
|
||||
resp = resp.click('Edit')
|
||||
resp.form['comment'] = 'edited comment body'
|
||||
resp.form['attachments$element0'] = 'plop'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
|
||||
resp = resp.click('Edit')
|
||||
assert resp.form['comment'].value == 'edited comment body'
|
||||
assert resp.form['attachments$element0'].value == 'plop'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
|
||||
resp = resp.click('Delete')
|
||||
resp = resp.form.submit('cancel').follow()
|
||||
assert 'first comment template' in resp
|
||||
|
||||
resp = resp.click('Delete')
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert 'first comment template' not in resp
|
||||
assert 'There are no comment templates defined.' in resp
|
||||
|
||||
resp = resp.click('New')
|
||||
resp.form['name'] = 'first comment template'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
resp.form['comment'] = 'comment body'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
|
||||
resp = app.get('/backoffice/workflows/')
|
||||
resp = resp.click('Comment Templates')
|
||||
assert 'first comment template' in resp
|
||||
|
||||
|
||||
def test_comment_template_in_use(pub, superuser):
|
||||
Workflow.wipe()
|
||||
CommentTemplate.wipe()
|
||||
workflow = Workflow(name='test workflow')
|
||||
st1 = workflow.add_status('Status1')
|
||||
item = st1.add_action('register-comment')
|
||||
item.comment = 'Hello'
|
||||
workflow.store()
|
||||
|
||||
comment_template = CommentTemplate(name='test comment template')
|
||||
comment_template.comment = 'test comment'
|
||||
comment_template.store()
|
||||
assert comment_template.is_in_use() is False
|
||||
|
||||
item.comment_template = comment_template.slug
|
||||
workflow.store()
|
||||
assert comment_template.is_in_use() is True
|
||||
|
||||
# check workflow usage is displayed
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/comment-templates/%s/' % comment_template.id)
|
||||
assert 'Usage in workflows' in resp.text
|
||||
assert 'test workflow' in resp.text
|
||||
resp.click('test workflow') # make sure the link is ok
|
||||
|
||||
resp = resp.click('Delete')
|
||||
assert 'still used' in resp.text
|
||||
|
||||
|
||||
def test_admin_workflow_edit(pub, superuser):
|
||||
CommentTemplateCategory.wipe()
|
||||
Workflow.wipe()
|
||||
CommentTemplate.wipe()
|
||||
comment_template = CommentTemplate(name='test comment template')
|
||||
comment_template.comment = 'test comment'
|
||||
comment_template.store()
|
||||
|
||||
workflow = Workflow(name='test comment template')
|
||||
st1 = workflow.add_status('Status1')
|
||||
item = st1.add_action('register-comment')
|
||||
item.comment = 'Hello'
|
||||
workflow.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/%s/status/%s/items/1/' % (workflow.id, st1.id))
|
||||
assert [o[0] for o in resp.form['comment_template'].options] == ['', 'test-comment-template']
|
||||
|
||||
cat_b = CommentTemplateCategory(name='Cat B')
|
||||
cat_b.store()
|
||||
comment_template = CommentTemplate(name='foo bar')
|
||||
comment_template.category_id = cat_b.id
|
||||
comment_template.store()
|
||||
comment_template = CommentTemplate(name='bar foo')
|
||||
comment_template.category_id = cat_b.id
|
||||
comment_template.store()
|
||||
cat_a = CommentTemplateCategory(name='Cat A')
|
||||
cat_a.store()
|
||||
comment_template = CommentTemplate(name='foo baz')
|
||||
comment_template.category_id = cat_a.id
|
||||
comment_template.store()
|
||||
|
||||
resp = app.get('/backoffice/workflows/%s/status/%s/items/1/' % (workflow.id, st1.id))
|
||||
assert [o[0] for o in resp.form['comment_template'].options] == [
|
||||
'',
|
||||
'foo-baz',
|
||||
'bar-foo',
|
||||
'foo-bar',
|
||||
'test-comment-template',
|
||||
]
|
||||
resp.form['comment_template'] = 'test-comment-template'
|
||||
resp = resp.form.submit('submit')
|
||||
|
||||
workflow = Workflow.get(workflow.id)
|
||||
assert workflow.possible_status[0].items[0].comment_template == 'test-comment-template'
|
||||
|
||||
|
||||
def test_comment_templates_category(pub, superuser):
|
||||
CommentTemplateCategory.wipe()
|
||||
CommentTemplate.wipe()
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/comment-templates/new')
|
||||
assert 'category_id' not in resp.form.fields
|
||||
|
||||
comment_template = CommentTemplate(name='foo')
|
||||
comment_template.store()
|
||||
|
||||
resp = app.get('/backoffice/workflows/comment-templates/categories/')
|
||||
resp = resp.click('New Category')
|
||||
resp.form['name'] = 'a new category'
|
||||
resp.form['description'] = 'description of the category'
|
||||
resp = resp.form.submit('submit')
|
||||
assert CommentTemplateCategory.count() == 1
|
||||
category = CommentTemplateCategory.select()[0]
|
||||
assert category.name == 'a new category'
|
||||
|
||||
resp = app.get('/backoffice/workflows/comment-templates/new')
|
||||
resp.form['name'] = 'template 2'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert CommentTemplate.count() == 2
|
||||
assert CommentTemplate.get(2).category_id is None
|
||||
resp = app.get('/backoffice/workflows/comment-templates/new')
|
||||
resp.form['name'] = 'template 3'
|
||||
resp.form['category_id'] = str(category.id)
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert CommentTemplate.count() == 3
|
||||
assert CommentTemplate.get(3).category_id == str(category.id)
|
||||
|
||||
resp = app.get('/backoffice/workflows/comment-templates/%s/' % comment_template.id)
|
||||
resp = resp.click(href=re.compile('^edit$'))
|
||||
resp.form['category_id'] = str(category.id)
|
||||
resp = resp.form.submit('cancel').follow()
|
||||
comment_template.refresh_from_storage()
|
||||
assert comment_template.category_id is None
|
||||
|
||||
resp = app.get('/backoffice/workflows/comment-templates/%s/' % comment_template.id)
|
||||
resp = resp.click(href=re.compile('^edit$'))
|
||||
resp.form['category_id'] = str(category.id)
|
||||
resp.form['comment'] = 'comment body'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
comment_template.refresh_from_storage()
|
||||
assert str(comment_template.category_id) == str(category.id)
|
||||
|
||||
resp = app.get('/backoffice/workflows/comment-templates/%s/' % comment_template.id)
|
||||
resp = resp.click(href=re.compile('^edit$'))
|
||||
assert resp.form['category_id'].value == str(category.id)
|
||||
|
||||
resp = app.get('/backoffice/workflows/comment-templates/categories/')
|
||||
resp = resp.click('New Category')
|
||||
resp.form['name'] = 'a second category'
|
||||
resp.form['description'] = 'description of the category'
|
||||
resp = resp.form.submit('submit')
|
||||
assert CommentTemplateCategory.count() == 2
|
||||
category2 = [x for x in CommentTemplateCategory.select() if x.id != category.id][0]
|
||||
assert category2.name == 'a second category'
|
||||
|
||||
app.get(
|
||||
'/backoffice/workflows/comment-templates/categories/update_order?order=%s;%s;'
|
||||
% (category2.id, category.id)
|
||||
)
|
||||
categories = CommentTemplateCategory.select()
|
||||
CommentTemplateCategory.sort_by_position(categories)
|
||||
assert [x.id for x in categories] == [str(category2.id), str(category.id)]
|
||||
|
||||
app.get(
|
||||
'/backoffice/workflows/comment-templates/categories/update_order?order=%s;%s;0'
|
||||
% (category.id, category2.id)
|
||||
)
|
||||
categories = CommentTemplateCategory.select()
|
||||
CommentTemplateCategory.sort_by_position(categories)
|
||||
assert [x.id for x in categories] == [str(category.id), str(category2.id)]
|
||||
|
||||
resp = app.get('/backoffice/workflows/comment-templates/categories/')
|
||||
resp = resp.click('a new category')
|
||||
resp = resp.click('Delete')
|
||||
resp = resp.form.submit()
|
||||
comment_template.refresh_from_storage()
|
||||
assert not comment_template.category_id
|
||||
|
||||
|
||||
def test_workflow_register_comment_template(pub):
|
||||
Workflow.wipe()
|
||||
CommentTemplate.wipe()
|
||||
|
||||
comment_template = CommentTemplate(name='test comment template')
|
||||
comment_template.comment = 'test comment'
|
||||
comment_template.store()
|
||||
|
||||
workflow = Workflow(name='test comment template')
|
||||
st1 = workflow.add_status('Status1')
|
||||
item = st1.add_action('register-comment')
|
||||
item.comment = 'Hello'
|
||||
item.comment_template = comment_template.slug
|
||||
workflow.store()
|
||||
|
||||
formdef = FormDef()
|
||||
formdef.name = 'baz'
|
||||
formdef.fields = []
|
||||
formdef.store()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
|
||||
item.perform(formdata)
|
||||
assert len(formdata.evolution) == 1
|
||||
assert len(formdata.evolution[0].parts) == 2
|
||||
assert formdata.evolution[-1].parts[1].content == 'test comment'
|
||||
|
||||
# check nothing is registered and an error is logged if the comment template is missing
|
||||
CommentTemplate.wipe()
|
||||
item.perform(formdata)
|
||||
assert len(formdata.evolution) == 1
|
||||
assert len(formdata.evolution[0].parts) == 2
|
||||
assert pub.loggederror_class.count() == 1
|
||||
logged_error = pub.loggederror_class.select()[0]
|
||||
assert (
|
||||
logged_error.summary
|
||||
== 'reference to invalid comment template test-comment-template in status Status1'
|
||||
)
|
||||
|
||||
|
||||
def test_workflow_register_comment_template_attachments(pub):
|
||||
Workflow.wipe()
|
||||
CommentTemplate.wipe()
|
||||
|
||||
comment_template = CommentTemplate(name='test comment template')
|
||||
comment_template.comment = 'test comment'
|
||||
comment_template.attachments = ['form_var_file1_raw']
|
||||
comment_template.store()
|
||||
|
||||
workflow = Workflow(name='test comment template')
|
||||
st1 = workflow.add_status('Status1')
|
||||
item = st1.add_action('register-comment')
|
||||
item.comment = 'Hello'
|
||||
item.comment_template = comment_template.slug
|
||||
workflow.store()
|
||||
|
||||
formdef = FormDef()
|
||||
formdef.name = 'baz'
|
||||
formdef.fields = [
|
||||
FileField(id='1', label='File', type='file', varname='file1'),
|
||||
]
|
||||
formdef.store()
|
||||
|
||||
upload = PicklableUpload('test.jpeg', 'image/jpeg')
|
||||
with open(os.path.join(os.path.dirname(__file__), 'image-with-gps-data.jpeg'), 'rb') as fd:
|
||||
upload.receive([fd.read()])
|
||||
formdata = formdef.data_class()()
|
||||
formdata.data = {'1': upload}
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
pub.substitutions.feed(formdata)
|
||||
|
||||
item.perform(formdata)
|
||||
assert len(formdata.evolution) == 1
|
||||
assert len(formdata.evolution[0].parts) == 3
|
||||
assert formdata.evolution[-1].parts[2].content == 'test comment'
|
||||
assert formdata.evolution[-1].parts[1].base_filename == 'test.jpeg'
|
||||
|
||||
# check two files are sent if attachments are also defined on the action itself.
|
||||
item.attachments = ['form_var_file1_raw']
|
||||
item.perform(formdata)
|
||||
assert len(formdata.evolution) == 1
|
||||
assert len(formdata.evolution[0].parts) == 6
|
||||
assert formdata.evolution[-1].parts[5].content == 'test comment'
|
||||
assert formdata.evolution[-1].parts[4].base_filename == 'test.jpeg'
|
||||
assert formdata.evolution[-1].parts[3].base_filename == 'test.jpeg'
|
||||
|
||||
|
||||
def test_workflow_register_comment_template_empty(pub):
|
||||
Workflow.wipe()
|
||||
CommentTemplate.wipe()
|
||||
|
||||
comment_template = CommentTemplate(name='test comment template')
|
||||
comment_template.comment = None
|
||||
comment_template.store()
|
||||
|
||||
workflow = Workflow(name='test comment template')
|
||||
st1 = workflow.add_status('Status1')
|
||||
item = st1.add_action('register-comment')
|
||||
item.comment = 'Hello'
|
||||
item.comment_template = comment_template.slug
|
||||
workflow.store()
|
||||
|
||||
formdef = FormDef()
|
||||
formdef.name = 'baz'
|
||||
formdef.store()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.data = {}
|
||||
formdata.just_created()
|
||||
formdata.store()
|
||||
pub.substitutions.feed(formdata)
|
||||
|
||||
item.perform(formdata)
|
||||
assert len(formdata.evolution) == 1
|
||||
assert len(formdata.evolution[0].parts) == 1
|
||||
|
||||
|
||||
def test_comment_templates_export(pub, superuser, comment_template):
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/comment-templates/1/')
|
||||
|
||||
resp = resp.click(href='export')
|
||||
xml_export = resp.text
|
||||
|
||||
ds = io.StringIO(xml_export)
|
||||
comment_template2 = CommentTemplate.import_from_xml(ds)
|
||||
assert comment_template2.name == 'test CT'
|
||||
|
||||
|
||||
def test_comment_templates_import(pub, superuser, comment_template):
|
||||
comment_template.slug = 'foobar'
|
||||
comment_template.store()
|
||||
comment_template_xml = ET.tostring(comment_template.export_to_xml(include_id=True))
|
||||
CommentTemplate.wipe()
|
||||
assert CommentTemplate.count() == 0
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/comment-templates/')
|
||||
resp = resp.click(href='import')
|
||||
resp.forms[0]['file'] = Upload('comment_template.wcs', comment_template_xml)
|
||||
resp = resp.forms[0].submit()
|
||||
assert CommentTemplate.count() == 1
|
||||
assert {wc.slug for wc in CommentTemplate.select()} == {'foobar'}
|
||||
|
||||
# check slug
|
||||
resp = app.get('/backoffice/workflows/comment-templates/')
|
||||
resp = resp.click(href='import')
|
||||
resp.forms[0]['file'] = Upload('comment_template.wcs', comment_template_xml)
|
||||
resp = resp.forms[0].submit()
|
||||
assert CommentTemplate.count() == 2
|
||||
assert {wc.slug for wc in CommentTemplate.select()} == {'foobar', 'test-ct'}
|
||||
resp = app.get('/backoffice/workflows/comment-templates/')
|
||||
resp = resp.click(href='import')
|
||||
resp.forms[0]['file'] = Upload('comment_template.wcs', comment_template_xml)
|
||||
resp = resp.forms[0].submit()
|
||||
assert CommentTemplate.count() == 3
|
||||
assert {wc.slug for wc in CommentTemplate.select()} == {'foobar', 'test-ct', 'test-ct-1'}
|
||||
|
||||
# import an invalid file
|
||||
resp = app.get('/backoffice/workflows/comment-templates/')
|
||||
resp = resp.click(href='import')
|
||||
resp.form['file'] = Upload('comment_template.wcs', b'garbage')
|
||||
resp = resp.form.submit()
|
||||
assert 'Invalid File' in resp.text
|
||||
|
||||
|
||||
def test_comment_templates_duplicate(pub, superuser, comment_template):
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/comment-templates/1/')
|
||||
|
||||
resp = resp.click(href='duplicate')
|
||||
assert resp.form['name'].value == 'test CT (copy)'
|
||||
resp = resp.form.submit('cancel').follow()
|
||||
assert CommentTemplate.count() == 1
|
||||
|
||||
resp = resp.click(href='duplicate')
|
||||
assert resp.form['name'].value == 'test CT (copy)'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert CommentTemplate.count() == 2
|
||||
|
||||
resp = app.get('/backoffice/workflows/comment-templates/1/')
|
||||
resp = resp.click(href='duplicate')
|
||||
assert resp.form['name'].value == 'test CT (copy 2)'
|
||||
resp.form['name'].value = 'other copy'
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert CommentTemplate.count() == 3
|
||||
assert {x.name for x in CommentTemplate.select()} == {'test CT', 'test CT (copy)', 'other copy'}
|
||||
assert {x.slug for x in CommentTemplate.select()} == {'test-ct', 'test-ct-copy', 'other-copy'}
|
||||
|
||||
|
||||
def export_to_indented_xml(comment_template, include_id=False):
|
||||
comment_template_xml = comment_template.export_to_xml(include_id=include_id)
|
||||
indent(comment_template_xml)
|
||||
return comment_template_xml
|
||||
|
||||
|
||||
def assert_import_export_works(comment_template, include_id=False):
|
||||
comment_template2 = CommentTemplate.import_from_xml_tree(
|
||||
ET.fromstring(ET.tostring(comment_template.export_to_xml(include_id))), include_id
|
||||
)
|
||||
assert ET.tostring(export_to_indented_xml(comment_template)) == ET.tostring(
|
||||
export_to_indented_xml(comment_template2)
|
||||
)
|
||||
return comment_template2
|
||||
|
||||
|
||||
def test_comment_template(pub):
|
||||
comment_template = CommentTemplate(name='test')
|
||||
assert_import_export_works(comment_template, include_id=True)
|
||||
|
||||
|
||||
def test_comment_template_with_category(pub):
|
||||
category = CommentTemplateCategory(name='test category')
|
||||
category.store()
|
||||
|
||||
comment_template = CommentTemplate(name='test category')
|
||||
comment_template.category_id = category.id
|
||||
comment_template.store()
|
||||
comment_template2 = assert_import_export_works(comment_template, include_id=True)
|
||||
assert comment_template2.category_id == comment_template.category_id
|
||||
|
||||
# import with non existing category
|
||||
CommentTemplateCategory.wipe()
|
||||
export = ET.tostring(comment_template.export_to_xml(include_id=True))
|
||||
comment_template3 = CommentTemplate.import_from_xml_tree(ET.fromstring(export), include_id=True)
|
||||
assert comment_template3.category_id is None
|
|
@ -9,6 +9,7 @@ from quixote.http_request import Upload
|
|||
from wcs.blocks import BlockDef
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.categories import Category
|
||||
from wcs.comment_templates import CommentTemplate
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.fields import CommentField, ItemField, PageField, StringField
|
||||
from wcs.formdef import FormDef
|
||||
|
@ -1151,6 +1152,29 @@ def test_mail_template_snapshot_browse(pub):
|
|||
resp = resp.click('Edit')
|
||||
|
||||
|
||||
def test_comment_template_snapshot_browse(pub):
|
||||
create_superuser(pub)
|
||||
create_role(pub)
|
||||
|
||||
CommentTemplate.wipe()
|
||||
comment_template = CommentTemplate(name='test')
|
||||
comment_template.store()
|
||||
assert pub.snapshot_class.count() == 1
|
||||
# check calling .store() without changes doesn't create snapshots
|
||||
comment_template.store()
|
||||
assert pub.snapshot_class.count() == 1
|
||||
|
||||
app = login(get_app(pub))
|
||||
|
||||
resp = app.get('/backoffice/workflows/comment-templates/%s/history/' % comment_template.id)
|
||||
snapshot = pub.snapshot_class.select_object_history(comment_template)[0]
|
||||
resp = resp.click(href='%s/view/' % snapshot.id)
|
||||
assert 'This comment template is readonly' in resp.text
|
||||
assert '<p>%s</p>' % localstrftime(snapshot.timestamp) in resp.text
|
||||
with pytest.raises(IndexError):
|
||||
resp = resp.click('Edit')
|
||||
|
||||
|
||||
def test_category_snapshot_browse(pub):
|
||||
create_superuser(pub)
|
||||
create_role(pub)
|
||||
|
|
|
@ -26,10 +26,12 @@ from wcs.categories import (
|
|||
BlockCategory,
|
||||
CardDefCategory,
|
||||
Category,
|
||||
CommentTemplateCategory,
|
||||
DataSourceCategory,
|
||||
MailTemplateCategory,
|
||||
WorkflowCategory,
|
||||
)
|
||||
from wcs.comment_templates import CommentTemplate
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.mail_templates import MailTemplate
|
||||
|
@ -177,6 +179,11 @@ class MailTemplateCategoryUI(CategoryUI):
|
|||
management_roles_hint_text = None
|
||||
|
||||
|
||||
class CommentTemplateCategoryUI(CategoryUI):
|
||||
category_class = CommentTemplateCategory
|
||||
management_roles_hint_text = None
|
||||
|
||||
|
||||
class DataSourceCategoryUI(CategoryUI):
|
||||
category_class = DataSourceCategory
|
||||
management_roles_hint_text = None
|
||||
|
@ -334,6 +341,14 @@ class MailTemplateCategoryPage(CategoryPage):
|
|||
empty_message = _('No mail template associated to this category.')
|
||||
|
||||
|
||||
class CommentTemplateCategoryPage(CategoryPage):
|
||||
category_class = CommentTemplateCategory
|
||||
category_ui_class = CommentTemplateCategoryUI
|
||||
object_class = CommentTemplate
|
||||
usage_title = _('Comment templates in this category')
|
||||
empty_message = _('No comment template associated to this category.')
|
||||
|
||||
|
||||
class DataSourceCategoryPage(CategoryPage):
|
||||
category_class = DataSourceCategory
|
||||
category_ui_class = DataSourceCategoryUI
|
||||
|
@ -452,6 +467,14 @@ class MailTemplateCategoriesDirectory(CategoriesDirectory):
|
|||
category_explanation = _('Categories are used to sort the different mail templates.')
|
||||
|
||||
|
||||
class CommentTemplateCategoriesDirectory(CategoriesDirectory):
|
||||
base_section = 'workflows'
|
||||
category_class = CommentTemplateCategory
|
||||
category_ui_class = CommentTemplateCategoryUI
|
||||
category_page_class = CommentTemplateCategoryPage
|
||||
category_explanation = _('Categories are used to sort the different comment templates.')
|
||||
|
||||
|
||||
class DataSourceCategoriesDirectory(CategoriesDirectory):
|
||||
base_section = 'workflows'
|
||||
category_class = DataSourceCategory
|
||||
|
|
|
@ -0,0 +1,360 @@
|
|||
# w.c.s. - web application for online forms
|
||||
# Copyright (C) 2005-2022 Entr'ouvert
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from quixote import get_publisher, get_response, redirect
|
||||
from quixote.directory import Directory
|
||||
from quixote.html import TemplateIO, htmltext
|
||||
|
||||
from wcs.admin import utils
|
||||
from wcs.admin.categories import CommentTemplateCategoriesDirectory, get_categories
|
||||
from wcs.backoffice.snapshots import SnapshotsDirectory
|
||||
from wcs.categories import CommentTemplateCategory
|
||||
from wcs.comment_templates import CommentTemplate
|
||||
from wcs.qommon import _, errors, misc, template
|
||||
from wcs.qommon.backoffice.menu import html_top
|
||||
from wcs.qommon.form import (
|
||||
ComputedExpressionWidget,
|
||||
FileWidget,
|
||||
Form,
|
||||
HtmlWidget,
|
||||
SingleSelectWidget,
|
||||
SlugWidget,
|
||||
StringWidget,
|
||||
TextWidget,
|
||||
WidgetList,
|
||||
get_session,
|
||||
)
|
||||
|
||||
|
||||
class CommentTemplatesDirectory(Directory):
|
||||
_q_exports = ['', 'new', 'categories', ('import', 'p_import')]
|
||||
do_not_call_in_templates = True
|
||||
categories = CommentTemplateCategoriesDirectory()
|
||||
|
||||
def _q_traverse(self, path):
|
||||
if not get_publisher().get_backoffice_root().is_global_accessible('workflows'):
|
||||
raise errors.AccessForbiddenError()
|
||||
get_response().breadcrumb.append(('comment-templates/', _('Comment Templates')))
|
||||
return super()._q_traverse(path)
|
||||
|
||||
def _q_lookup(self, component):
|
||||
return CommentTemplatePage(component)
|
||||
|
||||
def _q_index(self):
|
||||
html_top('comment_templates', title=_('Comment Templates'))
|
||||
categories = CommentTemplateCategory.select()
|
||||
CommentTemplateCategory.sort_by_position(categories)
|
||||
comment_templates = CommentTemplate.select(order_by='name')
|
||||
if categories:
|
||||
categories.append(CommentTemplateCategory(_('Misc')))
|
||||
for category in categories:
|
||||
category.comment_templates = [x for x in comment_templates if x.category_id == category.id]
|
||||
return template.QommonTemplateResponse(
|
||||
templates=['wcs/backoffice/comment-templates.html'],
|
||||
context={'view': self, 'comment_templates': comment_templates, 'categories': categories},
|
||||
)
|
||||
|
||||
def new(self):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
form.add(StringWidget, 'name', title=_('Name'), required=True, size=50)
|
||||
category_options = get_categories(CommentTemplateCategory)
|
||||
if category_options:
|
||||
category_options = [(None, '---', '')] + list(category_options)
|
||||
form.add(
|
||||
SingleSelectWidget,
|
||||
'category_id',
|
||||
title=_('Category'),
|
||||
options=category_options,
|
||||
)
|
||||
form.add_submit('submit', _('Add'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
if form.get_widget('cancel').parse():
|
||||
return redirect('.')
|
||||
|
||||
if form.is_submitted() and not form.has_errors():
|
||||
comment_template = CommentTemplate(name=form.get_widget('name').parse())
|
||||
if form.get_widget('category_id'):
|
||||
comment_template.category_id = form.get_widget('category_id').parse()
|
||||
comment_template.store()
|
||||
return redirect('%s/edit' % comment_template.id)
|
||||
|
||||
get_response().breadcrumb.append(('new', _('New Comment Template')))
|
||||
html_top('comment_templates', title=_('New Comment Template'))
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s</h2>') % _('New Comment Template')
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
|
||||
def p_import(self):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
import_title = _('Import Comment Template')
|
||||
|
||||
form.add(FileWidget, 'file', title=_('File'), required=True)
|
||||
form.add_submit('submit', import_title)
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
|
||||
if form.get_submit() == 'cancel':
|
||||
return redirect('.')
|
||||
|
||||
if form.is_submitted() and not form.has_errors():
|
||||
try:
|
||||
return self.import_submit(form)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
get_response().breadcrumb.append(('import', _('Import')))
|
||||
html_top('comment_templates', title=import_title)
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s</h2>') % import_title
|
||||
r += htmltext('<p>%s</p>') % _('You can install a new comment template by uploading a file.')
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
|
||||
def import_submit(self, form):
|
||||
fp = form.get_widget('file').parse().fp
|
||||
|
||||
error = False
|
||||
try:
|
||||
comment_template = CommentTemplate.import_from_xml(fp)
|
||||
get_session().message = ('info', _('This comment template has been successfully imported.'))
|
||||
except ValueError:
|
||||
error = True
|
||||
|
||||
if error:
|
||||
form.set_error('file', _('Invalid File'))
|
||||
raise ValueError()
|
||||
|
||||
# check slug unicity
|
||||
known_slugs = {
|
||||
x.slug: x.id for x in CommentTemplate.select(ignore_migration=True, ignore_errors=True)
|
||||
}
|
||||
if comment_template.slug in known_slugs:
|
||||
comment_template.slug = None # a new one will be set in .store()
|
||||
comment_template.store()
|
||||
return redirect('%s/' % comment_template.id)
|
||||
|
||||
|
||||
class CommentTemplatePage(Directory):
|
||||
_q_exports = [
|
||||
'',
|
||||
'edit',
|
||||
'delete',
|
||||
'duplicate',
|
||||
'export',
|
||||
('history', 'snapshots_dir'),
|
||||
]
|
||||
do_not_call_in_templates = True
|
||||
|
||||
def __init__(self, component, instance=None):
|
||||
try:
|
||||
self.comment_template = instance or CommentTemplate.get(component)
|
||||
except KeyError:
|
||||
raise errors.TraversalError()
|
||||
get_response().breadcrumb.append((component + '/', self.comment_template.name))
|
||||
self.snapshots_dir = SnapshotsDirectory(self.comment_template)
|
||||
|
||||
def get_sidebar(self):
|
||||
r = TemplateIO(html=True)
|
||||
if self.comment_template.is_readonly():
|
||||
r += htmltext('<div class="infonotice"><p>%s</p></div>') % _('This comment template is readonly.')
|
||||
r += utils.snapshot_info_block(snapshot=self.comment_template.snapshot_object)
|
||||
r += htmltext('<ul id="sidebar-actions">')
|
||||
if not self.comment_template.is_readonly():
|
||||
r += htmltext('<li><a href="export">%s</a></li>') % _('Export')
|
||||
r += htmltext('<li><a href="duplicate" rel="popup">%s</a></li>') % _('Duplicate')
|
||||
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete')
|
||||
if get_publisher().snapshot_class:
|
||||
r += htmltext('<li><a rel="popup" href="history/save">%s</a></li>') % _('Save snapshot')
|
||||
r += htmltext('<li><a href="history/">%s</a></li>') % _('History')
|
||||
r += htmltext('</ul>')
|
||||
return r.getvalue()
|
||||
|
||||
def _q_index(self):
|
||||
html_top('comment_templates', title=self.comment_template.name)
|
||||
get_response().filter['sidebar'] = self.get_sidebar()
|
||||
return template.QommonTemplateResponse(
|
||||
templates=['wcs/backoffice/comment-template.html'],
|
||||
context={'view': self, 'comment_template': self.comment_template},
|
||||
)
|
||||
|
||||
def get_form(self):
|
||||
form = Form(enctype='multipart/form-data', use_tabs=True)
|
||||
form.add(
|
||||
StringWidget, 'name', title=_('Name'), required=True, size=30, value=self.comment_template.name
|
||||
)
|
||||
category_options = get_categories(CommentTemplateCategory)
|
||||
if category_options:
|
||||
category_options = [(None, '---', '')] + list(category_options)
|
||||
form.add(
|
||||
SingleSelectWidget,
|
||||
'category_id',
|
||||
title=_('Category'),
|
||||
options=category_options,
|
||||
value=self.comment_template.category_id,
|
||||
)
|
||||
|
||||
form.add(
|
||||
TextWidget,
|
||||
'description',
|
||||
title=_('Description'),
|
||||
cols=80,
|
||||
rows=3,
|
||||
value=self.comment_template.description,
|
||||
)
|
||||
form.add(
|
||||
TextWidget,
|
||||
'comment',
|
||||
title=_('Comment'),
|
||||
value=self.comment_template.comment,
|
||||
cols=80,
|
||||
rows=15,
|
||||
require=True,
|
||||
validation_function=ComputedExpressionWidget.validate_template,
|
||||
)
|
||||
|
||||
if self.comment_template.slug and not self.comment_template.is_in_use():
|
||||
form.add(
|
||||
SlugWidget,
|
||||
'slug',
|
||||
value=self.comment_template.slug,
|
||||
advanced=True,
|
||||
)
|
||||
|
||||
form.add(
|
||||
WidgetList,
|
||||
'attachments',
|
||||
title=_('Attachments (templates or Python expressions)'),
|
||||
element_type=StringWidget,
|
||||
value=self.comment_template.attachments,
|
||||
add_element_label=_('Add attachment'),
|
||||
element_kwargs={'render_br': False, 'size': 50},
|
||||
advanced=True,
|
||||
)
|
||||
|
||||
if not self.comment_template.is_readonly():
|
||||
form.add_submit('submit', _('Submit'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
return form
|
||||
|
||||
def submit_form(self, form):
|
||||
name = form.get_widget('name').parse()
|
||||
slug_widget = form.get_widget('slug')
|
||||
if slug_widget:
|
||||
slug = form.get_widget('slug').parse()
|
||||
|
||||
for comment_template in CommentTemplate.select():
|
||||
if comment_template.id == self.comment_template.id:
|
||||
continue
|
||||
if slug_widget and slug == comment_template.slug:
|
||||
slug_widget.set_error(_('This value is already used.'))
|
||||
if form.has_errors():
|
||||
raise ValueError()
|
||||
|
||||
self.comment_template.name = name
|
||||
if form.get_widget('category_id'):
|
||||
self.comment_template.category_id = form.get_widget('category_id').parse()
|
||||
self.comment_template.description = form.get_widget('description').parse()
|
||||
self.comment_template.comment = form.get_widget('comment').parse()
|
||||
self.comment_template.attachments = form.get_widget('attachments').parse()
|
||||
if slug_widget:
|
||||
self.comment_template.slug = slug
|
||||
self.comment_template.store()
|
||||
|
||||
def edit(self):
|
||||
form = self.get_form()
|
||||
if form.get_submit() == 'cancel':
|
||||
return redirect('.')
|
||||
|
||||
if form.get_submit() == 'submit' and not form.has_errors():
|
||||
try:
|
||||
self.submit_form(form)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
return redirect('.')
|
||||
|
||||
get_response().breadcrumb.append(('edit', _('Edit')))
|
||||
html_top('comment_templates', title=_('Edit Comment Template'))
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s</h2>') % _('Edit Comment Template')
|
||||
r += form.render()
|
||||
r += get_publisher().substitutions.get_substitution_html_table()
|
||||
|
||||
return r.getvalue()
|
||||
|
||||
def delete(self):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
if not self.comment_template.is_in_use():
|
||||
form.widgets.append(
|
||||
HtmlWidget('<p>%s</p>' % _('You are about to irrevocably delete this comment template.'))
|
||||
)
|
||||
form.add_submit('delete', _('Submit'))
|
||||
else:
|
||||
form.widgets.append(
|
||||
HtmlWidget('<p>%s</p>' % _('This comment template is still used, it cannot be deleted.'))
|
||||
)
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
if form.get_widget('cancel').parse():
|
||||
return redirect('.')
|
||||
if not form.is_submitted() or form.has_errors():
|
||||
get_response().breadcrumb.append(('delete', _('Delete')))
|
||||
html_top('comment_templates', title=_('Delete Comment Template'))
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s %s</h2>') % (_('Deleting Comment Template:'), self.comment_template.name)
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
else:
|
||||
self.comment_template.remove_self()
|
||||
return redirect('..')
|
||||
|
||||
def export(self):
|
||||
return misc.xml_response(
|
||||
self.comment_template,
|
||||
filename='comment-template-%s.wcs' % self.comment_template.slug,
|
||||
content_type='application/x-wcs-comment-template',
|
||||
)
|
||||
|
||||
def duplicate(self):
|
||||
form = Form(enctype='multipart/form-data')
|
||||
name_widget = form.add(StringWidget, 'name', title=_('Name'), required=True, size=30)
|
||||
form.add_submit('submit', _('Submit'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
if form.get_widget('cancel').parse():
|
||||
return redirect('.')
|
||||
|
||||
if not form.is_submitted():
|
||||
original_name = self.comment_template.name
|
||||
new_name = '%s %s' % (original_name, _('(copy)'))
|
||||
names = [x.name for x in CommentTemplate.select()]
|
||||
no = 2
|
||||
while new_name in names:
|
||||
new_name = _('%(name)s (copy %(no)d)') % {'name': original_name, 'no': no}
|
||||
no += 1
|
||||
name_widget.set_value(new_name)
|
||||
|
||||
if not form.is_submitted() or form.has_errors():
|
||||
html_top('comment_templates', title=_('Duplicate Comment Template'))
|
||||
r = TemplateIO(html=True)
|
||||
get_response().breadcrumb.append(('duplicate', _('Duplicate')))
|
||||
r += htmltext('<h2>%s</h2>') % _('Duplicate Comment Template')
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
|
||||
self.comment_template.id = None
|
||||
self.comment_template.slug = None
|
||||
self.comment_template.name = form.get_widget('name').parse()
|
||||
self.comment_template.store()
|
||||
return redirect('../%s/' % self.comment_template.id)
|
|
@ -738,10 +738,14 @@ class SettingsDirectory(AccessControlled, Directory):
|
|||
form.add(CheckboxWidget, 'workflow_categories', title=_('Workflow Categories'), value=True)
|
||||
form.add(CheckboxWidget, 'block_categories', title=_('Fields Blocks Categories'), value=True)
|
||||
form.add(CheckboxWidget, 'mail_template_categories', title=_('Mail Templates Categories'), value=True)
|
||||
form.add(
|
||||
CheckboxWidget, 'comment_template_categories', title=_('Comment Templates Categories'), value=True
|
||||
)
|
||||
form.add(CheckboxWidget, 'data_source_categories', title=_('Data Sources Categories'), value=True)
|
||||
form.add(CheckboxWidget, 'settings', title=_('Settings'), value=False)
|
||||
form.add(CheckboxWidget, 'datasources', title=_('Data sources'), value=True)
|
||||
form.add(CheckboxWidget, 'mail-templates', title=_('Mail templates'), value=True)
|
||||
form.add(CheckboxWidget, 'comment-templates', title=_('Comment templates'), value=True)
|
||||
form.add(CheckboxWidget, 'wscalls', title=_('Webservice calls'), value=True)
|
||||
form.add(CheckboxWidget, 'apiaccess', title=_('API access'), value=True)
|
||||
form.add_submit('submit', _('Submit'))
|
||||
|
@ -773,9 +777,11 @@ class SettingsDirectory(AccessControlled, Directory):
|
|||
'workflow_categories',
|
||||
'block_categories',
|
||||
'mail_template_categories',
|
||||
'comment_template_categories',
|
||||
'data_source_categories',
|
||||
'wscalls',
|
||||
'mail-templates',
|
||||
'comment-templates',
|
||||
'apiaccess',
|
||||
):
|
||||
continue
|
||||
|
@ -871,10 +877,12 @@ class SettingsDirectory(AccessControlled, Directory):
|
|||
'workflow_categories',
|
||||
'block_categories',
|
||||
'mail_template_categories',
|
||||
'comment_template_categories',
|
||||
'data_source_categories',
|
||||
'datasources',
|
||||
'wscalls',
|
||||
'mail-templates',
|
||||
'comment-templates',
|
||||
'blockdefs',
|
||||
'apiaccess',
|
||||
):
|
||||
|
@ -934,6 +942,7 @@ class SettingsDirectory(AccessControlled, Directory):
|
|||
try:
|
||||
results = self.import_submit(form)
|
||||
results['mail_templates'] = results['mail-templates']
|
||||
results['comment_templates'] = results['comment-templates']
|
||||
except zipfile.BadZipfile:
|
||||
results = None
|
||||
reason = _('Not a valid export file')
|
||||
|
|
|
@ -63,6 +63,7 @@ from wcs.workflows import (
|
|||
)
|
||||
|
||||
from . import utils
|
||||
from .comment_templates import CommentTemplatesDirectory
|
||||
from .data_sources import NamedDataSourcesDirectory
|
||||
from .fields import FieldDefPage, FieldsDirectory
|
||||
from .logged_errors import LoggedErrorsDirectory
|
||||
|
@ -1944,10 +1945,12 @@ class WorkflowsDirectory(Directory):
|
|||
('import', 'p_import'),
|
||||
('data-sources', 'data_sources'),
|
||||
('mail-templates', 'mail_templates'),
|
||||
('comment-templates', 'comment_templates'),
|
||||
]
|
||||
|
||||
data_sources = NamedDataSourcesDirectoryInWorkflows()
|
||||
mail_templates = MailTemplatesDirectory()
|
||||
comment_templates = CommentTemplatesDirectory()
|
||||
category_class = WorkflowCategory
|
||||
categories = WorkflowCategoriesDirectory()
|
||||
|
||||
|
@ -1979,6 +1982,7 @@ class WorkflowsDirectory(Directory):
|
|||
r += htmltext('<h2>%s</h2>') % _('Workflows')
|
||||
r += htmltext('<span class="actions">')
|
||||
if is_global_accessible():
|
||||
r += htmltext('<a href="comment-templates/">%s</a>') % _('Comment Templates')
|
||||
r += htmltext('<a href="mail-templates/">%s</a>') % _('Mail Templates')
|
||||
r += htmltext('<a href="data-sources/">%s</a>') % _('Data sources')
|
||||
r += htmltext('<a href="categories/">%s</a>') % _('Categories')
|
||||
|
|
|
@ -21,6 +21,7 @@ from wcs.admin.logged_errors import LoggedErrorsDirectory
|
|||
from wcs.backoffice.deprecations import DeprecationsDirectory
|
||||
from wcs.blocks import BlockDef
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.comment_templates import CommentTemplate
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.mail_templates import MailTemplate
|
||||
|
@ -46,7 +47,7 @@ class ChangesDirectory(Directory):
|
|||
backoffice_root = get_publisher().get_backoffice_root()
|
||||
object_types = []
|
||||
if backoffice_root.is_accessible('workflows'):
|
||||
object_types += [Workflow, MailTemplate]
|
||||
object_types += [Workflow, MailTemplate, CommentTemplate]
|
||||
if backoffice_root.is_accessible('forms'):
|
||||
object_types += [NamedDataSource, BlockDef, FormDef]
|
||||
if backoffice_root.is_accessible('workflows'):
|
||||
|
@ -103,7 +104,8 @@ class StudioDirectory(Directory):
|
|||
extra_links.append(('../forms/blocks/', pgettext('studio', 'Field blocks')))
|
||||
if backoffice_root.is_accessible('workflows'):
|
||||
extra_links.append(('../workflows/mail-templates/', pgettext('studio', 'Mail templates')))
|
||||
object_types += [Workflow, MailTemplate]
|
||||
extra_links.append(('../workflows/comment-templates/', pgettext('studio', 'Comment templates')))
|
||||
object_types += [Workflow, MailTemplate, CommentTemplate]
|
||||
if backoffice_root.is_accessible('forms'):
|
||||
extra_links.append(('../forms/data-sources/', pgettext('studio', 'Data sources')))
|
||||
object_types += [NamedDataSource, BlockDef, FormDef]
|
||||
|
|
|
@ -301,6 +301,27 @@ class MailTemplateCategory(Category):
|
|||
return MailTemplate
|
||||
|
||||
|
||||
class CommentTemplateCategory(Category):
|
||||
_names = 'comment_template_categories'
|
||||
xml_root_node = 'comment_template_category'
|
||||
backoffice_class = 'wcs.admin.categories.CommentTemplateCategoryPage'
|
||||
backoffice_base_url = 'workflows/comment-templates/categories/'
|
||||
|
||||
# declarations for serialization
|
||||
XML_NODES = [
|
||||
('name', 'str'),
|
||||
('url_name', 'str'),
|
||||
('description', 'str'),
|
||||
('position', 'int'),
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def get_object_class(cls):
|
||||
from .comment_templates import CommentTemplate
|
||||
|
||||
return CommentTemplate
|
||||
|
||||
|
||||
class DataSourceCategory(Category):
|
||||
_names = 'data_source_categories'
|
||||
xml_root_node = 'data_source_category'
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
# w.c.s. - web application for online forms
|
||||
# Copyright (C) 2005-2022 Entr'ouvert
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from quixote import get_publisher
|
||||
|
||||
from wcs.categories import CommentTemplateCategory
|
||||
from wcs.qommon import _, get_logger
|
||||
from wcs.qommon.form import OptGroup
|
||||
from wcs.qommon.xml_storage import XmlStorableObject
|
||||
|
||||
|
||||
class CommentTemplate(XmlStorableObject):
|
||||
_names = 'comment-templates'
|
||||
xml_root_node = 'comment-template'
|
||||
backoffice_class = 'wcs.admin.comment_templates.CommentTemplatePage'
|
||||
verbose_name = _('Comment template')
|
||||
verbose_name_plural = _('Comment templates')
|
||||
|
||||
name = None
|
||||
slug = None
|
||||
description = None
|
||||
comment = None
|
||||
attachments = []
|
||||
category_id = None
|
||||
|
||||
# declarations for serialization
|
||||
XML_NODES = [
|
||||
('name', 'str'),
|
||||
('slug', 'str'),
|
||||
('description', 'str'),
|
||||
('comment', 'str'),
|
||||
('attachments', 'str_list'),
|
||||
]
|
||||
|
||||
def __init__(self, name=None):
|
||||
XmlStorableObject.__init__(self)
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def category(self):
|
||||
return CommentTemplateCategory.get(self.category_id, ignore_errors=True)
|
||||
|
||||
@category.setter
|
||||
def category(self, category):
|
||||
if category:
|
||||
self.category_id = category.id
|
||||
elif self.category_id:
|
||||
self.category_id = None
|
||||
|
||||
def get_admin_url(self):
|
||||
base_url = get_publisher().get_backoffice_url()
|
||||
return '%s/workflows/comment-templates/%s/' % (base_url, self.id)
|
||||
|
||||
def store(self, comment=None, *args, **kwargs):
|
||||
assert not self.is_readonly()
|
||||
if self.slug is None:
|
||||
# set slug if it's not yet there
|
||||
self.slug = self.get_new_slug()
|
||||
super().store(*args, **kwargs)
|
||||
if get_publisher().snapshot_class:
|
||||
get_publisher().snapshot_class.snap(instance=self, comment=comment)
|
||||
|
||||
def get_places_of_use(self):
|
||||
from wcs.workflows import Workflow
|
||||
|
||||
for workflow in Workflow.select(ignore_errors=True, ignore_migration=True):
|
||||
for item in workflow.get_all_items():
|
||||
if item.key != 'register-comment':
|
||||
continue
|
||||
if item.comment_template == self.slug:
|
||||
yield workflow
|
||||
break
|
||||
|
||||
def is_in_use(self):
|
||||
return any(self.get_places_of_use())
|
||||
|
||||
@classmethod
|
||||
def get_as_options_list(cls):
|
||||
def get_option(mt):
|
||||
option = [mt.slug, mt.name, mt.slug]
|
||||
if get_publisher().get_backoffice_root().is_accessible('workflows'):
|
||||
option.append({'data-goto-url': mt.get_admin_url()})
|
||||
return option
|
||||
|
||||
comment_templates_by_category_names = defaultdict(list)
|
||||
for comment_template in cls.select(order_by='name'):
|
||||
name = ''
|
||||
if comment_template.category:
|
||||
name = comment_template.category.name
|
||||
comment_templates_by_category_names[name].append(comment_template)
|
||||
category_names = list(comment_templates_by_category_names.keys())
|
||||
if len(category_names) == 1 and category_names[0] == '':
|
||||
# no category found
|
||||
return [get_option(mt) for mt in comment_templates_by_category_names['']]
|
||||
options = []
|
||||
# sort categories
|
||||
category_names = sorted(category_names)
|
||||
# comment template without categories at the end
|
||||
if category_names[0] == '':
|
||||
category_names = category_names[1:] + ['']
|
||||
# group by category name
|
||||
for name in category_names:
|
||||
options.append(OptGroup(name or _('Without category')))
|
||||
options.extend([get_option(mt) for mt in comment_templates_by_category_names[name]])
|
||||
return options
|
||||
|
||||
@classmethod
|
||||
def get_by_slug(cls, slug, ignore_errors=True):
|
||||
comment_template = super().get_by_slug(slug, ignore_errors=ignore_errors)
|
||||
if comment_template is None:
|
||||
get_logger().warning("comment template '%s' does not exist" % slug)
|
||||
return comment_template
|
||||
|
||||
def export_to_xml(self, include_id=False):
|
||||
root = super().export_to_xml(include_id=include_id)
|
||||
CommentTemplateCategory.object_category_xml_export(self, root, include_id=include_id)
|
||||
return root
|
||||
|
||||
@classmethod
|
||||
def import_from_xml_tree(cls, tree, include_id=False, **kwargs):
|
||||
comment_template = super().import_from_xml_tree(tree, include_id=include_id, **kwargs)
|
||||
CommentTemplateCategory.object_category_xml_import(comment_template, tree, include_id=include_id)
|
||||
return comment_template
|
|
@ -225,12 +225,14 @@ class WcsPublisher(QommonPublisher):
|
|||
'workflow_categories': 0,
|
||||
'block_categories': 0,
|
||||
'mail_template_categories': 0,
|
||||
'comment_template_categories': 0,
|
||||
'data_source_categories': 0,
|
||||
'roles': 0,
|
||||
'settings': 0,
|
||||
'datasources': 0,
|
||||
'wscalls': 0,
|
||||
'mail-templates': 0,
|
||||
'comment-templates': 0,
|
||||
'blockdefs': 0,
|
||||
'apiaccess': 0,
|
||||
}
|
||||
|
@ -496,6 +498,7 @@ class WcsPublisher(QommonPublisher):
|
|||
from wcs.blocks import BlockDef
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.categories import BlockCategory, CardDefCategory, Category, WorkflowCategory
|
||||
from wcs.comment_templates import CommentTemplate
|
||||
from wcs.data_sources import NamedDataSource
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.mail_templates import MailTemplate
|
||||
|
@ -510,6 +513,7 @@ class WcsPublisher(QommonPublisher):
|
|||
Workflow,
|
||||
NamedWsCall,
|
||||
MailTemplate,
|
||||
CommentTemplate,
|
||||
Category,
|
||||
CardDefCategory,
|
||||
WorkflowCategory,
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
{% extends "wcs/backoffice/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block appbar-title %}{% trans "Comment Template" %} - {{ comment_template.name }}{% endblock %}
|
||||
|
||||
{% block appbar-actions %}
|
||||
{% if not comment_template.is_readonly %}
|
||||
<a href="edit">{% trans "Edit" %}</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if comment_template.description %}
|
||||
<div class="bo-block">{{ comment_template.description }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if comment_template.comment %}
|
||||
<div class="section">
|
||||
<div class="comment-comment">{{ comment_template.comment }}</div>
|
||||
</div>
|
||||
|
||||
{% for workflow in comment_template.get_places_of_use %}
|
||||
{% if forloop.first %}
|
||||
<div class="section">
|
||||
<h3>{% trans "Usage in workflows" %}</h3>
|
||||
<ul class="objects-list single-links">
|
||||
{% endif %}
|
||||
<li><a href="{{ workflow.get_admin_url }}">{{ workflow.name }}</a></li>
|
||||
{% if forloop.last %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% else %}
|
||||
<div class="infonotice">{% trans "This comment template still needs to be configured." %}</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,37 @@
|
|||
{% extends "wcs/backoffice/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block appbar-title %}{% trans "Comment Templates" %}{% endblock %}
|
||||
|
||||
{% block appbar-actions %}
|
||||
<a href="categories/">{% trans "Categories" %}</a>
|
||||
<a rel="popup" href="import">{% trans "Import" %}</a>
|
||||
<a rel="popup" href="new">{% trans "New comment template" %}</a>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if categories %}
|
||||
{% for category in categories %}
|
||||
{% if category.comment_templates %}
|
||||
<div class="section">
|
||||
<h2>{{ category.name }}</h2>
|
||||
<ul class="objects-list single-links">
|
||||
{% for comment_template in category.comment_templates %}
|
||||
<li><a href="{{ comment_template.id }}/">{{ comment_template.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% elif comment_templates %}
|
||||
<ul class="objects-list single-links">
|
||||
{% for comment_template in comment_templates %}
|
||||
<li><a href="{{ comment_template.id }}/">{{ comment_template.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div class="infonotice">
|
||||
{% trans "There are no comment templates defined." %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -33,12 +33,27 @@
|
|||
{% if results.workflow_categories %}
|
||||
<li>{% blocktrans count counter=results.workflow_categories %}1 workflow category{% plural %}{{ counter }} workflow categories{% endblocktrans %}</li>
|
||||
{% endif %}
|
||||
{% if results.block_categories %}
|
||||
<li>{% blocktrans count counter=results.block_categories %}1 block category{% plural %}{{ counter }} block categories{% endblocktrans %}</li>
|
||||
{% endif %}
|
||||
{% if results.mail_template_categories %}
|
||||
<li>{% blocktrans count counter=results.mail_template_categories %}1 mail template category{% plural %}{{ counter }} mail template categories{% endblocktrans %}</li>
|
||||
{% endif %}
|
||||
{% if results.comment_template_categories %}
|
||||
<li>{% blocktrans count counter=results.comment_template_categories %}1 comment template category{% plural %}{{ counter }} comment template categories{% endblocktrans %}</li>
|
||||
{% endif %}
|
||||
{% if results.data_source_categories %}
|
||||
<li>{% blocktrans count counter=results.data_source_categories %}1 data source category{% plural %}{{ counter }} data source categories{% endblocktrans %}</li>
|
||||
{% endif %}
|
||||
{% if results.datasources %}
|
||||
<li>{% blocktrans count counter=results.datasources %}1 data source{% plural %}{{ counter }} data sources{% endblocktrans %}</li>
|
||||
{% endif %}
|
||||
{% if results.mail_templates %}
|
||||
<li>{% blocktrans count counter=results.mail_templates %}1 mail template{% plural %}{{ counter }} mail templates{% endblocktrans %}</li>
|
||||
{% endif %}
|
||||
{% if results.comment_templates %}
|
||||
<li>{% blocktrans count counter=results.comment_templates %}1 comment template{% plural %}{{ counter }} comment templates{% endblocktrans %}</li>
|
||||
{% endif %}
|
||||
{% if results.wscalls %}
|
||||
<li>{% blocktrans count counter=results.wscalls %}1 webservice call{% plural %}{{ counter }} webservice calls{% endblocktrans %}</li>
|
||||
{% endif %}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
from quixote import get_publisher
|
||||
from quixote.html import htmltext
|
||||
|
||||
from wcs.comment_templates import CommentTemplate
|
||||
from wcs.workflows import (
|
||||
AttachmentEvolutionPart,
|
||||
EvolutionPart,
|
||||
|
@ -26,7 +27,7 @@ from wcs.workflows import (
|
|||
)
|
||||
|
||||
from ..qommon import _, ezt
|
||||
from ..qommon.form import TextWidget, WidgetListOfRoles
|
||||
from ..qommon.form import SingleSelectWidget, TextWidget, WidgetListOfRoles
|
||||
from ..qommon.template import TemplateError
|
||||
|
||||
|
||||
|
@ -84,14 +85,37 @@ class RegisterCommenterWorkflowStatusItem(WorkflowStatusItem):
|
|||
category = 'interaction'
|
||||
|
||||
comment = None
|
||||
comment_template = None
|
||||
to = None
|
||||
attachments = None
|
||||
|
||||
def add_parameters_widgets(self, form, parameters, prefix='', formdef=None, **kwargs):
|
||||
super().add_parameters_widgets(form, parameters, prefix=prefix, formdef=formdef, **kwargs)
|
||||
subject_body_attrs = {}
|
||||
if 'comment' in parameters:
|
||||
if CommentTemplate.count():
|
||||
subject_body_attrs = {
|
||||
'data-dynamic-display-value': '',
|
||||
'data-dynamic-display-child-of': '%scomment_template' % prefix,
|
||||
}
|
||||
if 'comment' in parameters:
|
||||
form.add(
|
||||
TextWidget, '%scomment' % prefix, title=_('Message'), value=self.comment, cols=80, rows=10
|
||||
TextWidget,
|
||||
'%scomment' % prefix,
|
||||
title=_('Message'),
|
||||
value=self.comment,
|
||||
cols=80,
|
||||
rows=10,
|
||||
attrs=subject_body_attrs,
|
||||
)
|
||||
if 'comment_template' in parameters and CommentTemplate.count():
|
||||
form.add(
|
||||
SingleSelectWidget,
|
||||
'%scomment_template' % prefix,
|
||||
title=_('Comment Template'),
|
||||
value=self.comment_template,
|
||||
options=[(None, '', '')] + CommentTemplate.get_as_options_list(),
|
||||
attrs={'data-dynamic-display-parent': 'true'},
|
||||
)
|
||||
if 'to' in parameters:
|
||||
form.add(
|
||||
|
@ -105,7 +129,7 @@ class RegisterCommenterWorkflowStatusItem(WorkflowStatusItem):
|
|||
)
|
||||
|
||||
def get_parameters(self):
|
||||
return ('comment', 'to', 'attachments', 'condition')
|
||||
return ('comment_template', 'comment', 'to', 'attachments', 'condition')
|
||||
|
||||
def attach_uploads_to_formdata(self, formdata, uploads, to):
|
||||
if not formdata.evolution[-1].parts:
|
||||
|
@ -124,18 +148,36 @@ class RegisterCommenterWorkflowStatusItem(WorkflowStatusItem):
|
|||
if not formdata.evolution:
|
||||
return
|
||||
|
||||
if self.comment_template:
|
||||
comment_template = CommentTemplate.get_by_slug(self.comment_template)
|
||||
if comment_template:
|
||||
comment = comment_template.comment
|
||||
extra_attachments = comment_template.attachments
|
||||
else:
|
||||
message = _(
|
||||
'reference to invalid comment template %(comment_template)s in status %(status)s'
|
||||
) % {
|
||||
'status': self.parent.name,
|
||||
'comment_template': self.comment_template,
|
||||
}
|
||||
get_publisher().record_error(message, formdata=formdata, status_item=self)
|
||||
return
|
||||
else:
|
||||
comment = self.comment
|
||||
extra_attachments = None
|
||||
|
||||
# process attachments first, they might be used in the comment
|
||||
# (with substitution vars)
|
||||
if self.attachments:
|
||||
uploads = self.convert_attachments_to_uploads()
|
||||
if self.attachments or extra_attachments:
|
||||
uploads = self.convert_attachments_to_uploads(extra_attachments)
|
||||
self.attach_uploads_to_formdata(formdata, uploads, self.to)
|
||||
formdata.store() # store and invalidate cache, so references can be used in the comment message.
|
||||
|
||||
# the comment can use attachments done above
|
||||
if self.comment:
|
||||
if comment:
|
||||
try:
|
||||
formdata.evolution[-1].add_part(
|
||||
JournalEvolutionPart(formdata, get_publisher().translate(self.comment), self.to)
|
||||
JournalEvolutionPart(formdata, get_publisher().translate(comment), self.to)
|
||||
)
|
||||
formdata.store()
|
||||
except TemplateError as e:
|
||||
|
|
Loading…
Reference in New Issue