general: add and use a lazy gettext function (#51289)
This commit is contained in:
parent
1565f3abe2
commit
0da7edeab8
|
@ -183,3 +183,7 @@ def test_jquery_debug_mode():
|
||||||
pub.write_cfg()
|
pub.write_cfg()
|
||||||
resp = get_app(pub).get('/category1/test-formdef-1/')
|
resp = get_app(pub).get('/category1/test-formdef-1/')
|
||||||
assert 'jquery.js' in resp.text
|
assert 'jquery.js' in resp.text
|
||||||
|
|
||||||
|
|
||||||
|
def test_i18n_js():
|
||||||
|
get_app(pub).get('/i18n.js')
|
||||||
|
|
|
@ -22,7 +22,7 @@ from wcs.admin import utils
|
||||||
from wcs.admin.fields import FieldDefPage, FieldsDirectory
|
from wcs.admin.fields import FieldDefPage, FieldsDirectory
|
||||||
from wcs.backoffice.snapshots import SnapshotsDirectory
|
from wcs.backoffice.snapshots import SnapshotsDirectory
|
||||||
from wcs.blocks import BlockDef, BlockdefImportError
|
from wcs.blocks import BlockDef, BlockdefImportError
|
||||||
from wcs.qommon import N_, _, misc, template
|
from wcs.qommon import _, misc, template
|
||||||
from wcs.qommon.backoffice.menu import html_top
|
from wcs.qommon.backoffice.menu import html_top
|
||||||
from wcs.qommon.form import FileWidget, Form, HtmlWidget, StringWidget
|
from wcs.qommon.form import FileWidget, Form, HtmlWidget, StringWidget
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ class BlockDirectory(FieldsDirectory):
|
||||||
field_def_page_class = BlockFieldDefPage
|
field_def_page_class = BlockFieldDefPage
|
||||||
blacklisted_types = ['page', 'table', 'table-select', 'tablerows', 'ranked-items', 'blocks', 'computed']
|
blacklisted_types = ['page', 'table', 'table-select', 'tablerows', 'ranked-items', 'blocks', 'computed']
|
||||||
support_import = False
|
support_import = False
|
||||||
readonly_message = N_('This block of fields is readonly.')
|
readonly_message = _('This block of fields is readonly.')
|
||||||
|
|
||||||
def __init__(self, section, *args, **kwargs):
|
def __init__(self, section, *args, **kwargs):
|
||||||
self.section = section
|
self.section = section
|
||||||
|
|
|
@ -21,7 +21,7 @@ from quixote.html import TemplateIO, htmltext
|
||||||
from wcs.carddef import CardDef
|
from wcs.carddef import CardDef
|
||||||
from wcs.categories import CardDefCategory, Category
|
from wcs.categories import CardDefCategory, Category
|
||||||
from wcs.formdef import FormDef
|
from wcs.formdef import FormDef
|
||||||
from wcs.qommon import N_, _
|
from wcs.qommon import _
|
||||||
from wcs.qommon.backoffice.menu import html_top
|
from wcs.qommon.backoffice.menu import html_top
|
||||||
from wcs.qommon.form import Form, HtmlWidget, StringWidget, WysiwygTextWidget
|
from wcs.qommon.form import Form, HtmlWidget, StringWidget, WysiwygTextWidget
|
||||||
|
|
||||||
|
@ -83,8 +83,8 @@ class CategoryPage(Directory):
|
||||||
category_class = Category
|
category_class = Category
|
||||||
category_ui_class = CategoryUI
|
category_ui_class = CategoryUI
|
||||||
formdef_class = FormDef
|
formdef_class = FormDef
|
||||||
usage_title = N_('Forms in this category')
|
usage_title = _('Forms in this category')
|
||||||
empty_message = N_('no form associated to this category')
|
empty_message = _('no form associated to this category')
|
||||||
_q_exports = ['', 'edit', 'delete', 'description']
|
_q_exports = ['', 'edit', 'delete', 'description']
|
||||||
|
|
||||||
def __init__(self, component):
|
def __init__(self, component):
|
||||||
|
@ -112,14 +112,14 @@ class CategoryPage(Directory):
|
||||||
formdefs = self.formdef_class.select(order_by='name')
|
formdefs = self.formdef_class.select(order_by='name')
|
||||||
formdefs = [x for x in formdefs if x.category_id == self.category.id]
|
formdefs = [x for x in formdefs if x.category_id == self.category.id]
|
||||||
r += htmltext('<div class="bo-block">')
|
r += htmltext('<div class="bo-block">')
|
||||||
r += htmltext('<h3>%s</h3>') % _(self.usage_title)
|
r += htmltext('<h3>%s</h3>') % self.usage_title
|
||||||
r += htmltext('<ul>')
|
r += htmltext('<ul>')
|
||||||
for formdef in formdefs:
|
for formdef in formdefs:
|
||||||
r += htmltext('<li><a href="../../%s/">') % str(formdef.id)
|
r += htmltext('<li><a href="../../%s/">') % str(formdef.id)
|
||||||
r += formdef.name
|
r += formdef.name
|
||||||
r += htmltext('</a></li>')
|
r += htmltext('</a></li>')
|
||||||
if not formdefs:
|
if not formdefs:
|
||||||
r += htmltext('<li>%s</li>') % _(self.empty_message)
|
r += htmltext('<li>%s</li>') % self.empty_message
|
||||||
r += htmltext('</ul>')
|
r += htmltext('</ul>')
|
||||||
r += htmltext('</div>')
|
r += htmltext('</div>')
|
||||||
return r.getvalue()
|
return r.getvalue()
|
||||||
|
@ -192,8 +192,8 @@ class CardDefCategoryPage(CategoryPage):
|
||||||
category_class = CardDefCategory
|
category_class = CardDefCategory
|
||||||
category_ui_class = CardDefCategoryUI
|
category_ui_class = CardDefCategoryUI
|
||||||
formdef_class = CardDef
|
formdef_class = CardDef
|
||||||
usage_title = N_('Card models in this category')
|
usage_title = _('Card models in this category')
|
||||||
empty_message = N_('no card model associated to this category')
|
empty_message = _('no card model associated to this category')
|
||||||
|
|
||||||
|
|
||||||
class CategoriesDirectory(Directory):
|
class CategoriesDirectory(Directory):
|
||||||
|
@ -201,7 +201,7 @@ class CategoriesDirectory(Directory):
|
||||||
category_class = Category
|
category_class = Category
|
||||||
category_ui_class = CategoryUI
|
category_ui_class = CategoryUI
|
||||||
category_page_class = CategoryPage
|
category_page_class = CategoryPage
|
||||||
category_explanation = N_('Categories are used to sort the different forms.')
|
category_explanation = _('Categories are used to sort the different forms.')
|
||||||
|
|
||||||
def _q_index(self):
|
def _q_index(self):
|
||||||
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js', 'qommon.wysiwyg.js'])
|
get_response().add_javascript(['jquery.js', 'jquery-ui.js', 'biglist.js', 'qommon.wysiwyg.js'])
|
||||||
|
@ -214,7 +214,7 @@ class CategoriesDirectory(Directory):
|
||||||
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Category')
|
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Category')
|
||||||
r += htmltext('</span>')
|
r += htmltext('</span>')
|
||||||
r += htmltext('</div>')
|
r += htmltext('</div>')
|
||||||
r += htmltext('<div class="explanation bo-block"><p>%s</p></div>') % _(self.category_explanation)
|
r += htmltext('<div class="explanation bo-block"><p>%s</p></div>') % self.category_explanation
|
||||||
categories = self.category_class.select()
|
categories = self.category_class.select()
|
||||||
r += htmltext('<ul class="biglist sortable" id="category-list">')
|
r += htmltext('<ul class="biglist sortable" id="category-list">')
|
||||||
self.category_class.sort_by_position(categories)
|
self.category_class.sort_by_position(categories)
|
||||||
|
@ -273,4 +273,4 @@ class CardDefCategoriesDirectory(CategoriesDirectory):
|
||||||
category_class = CardDefCategory
|
category_class = CardDefCategory
|
||||||
category_ui_class = CardDefCategoryUI
|
category_ui_class = CardDefCategoryUI
|
||||||
category_page_class = CardDefCategoryPage
|
category_page_class = CardDefCategoryPage
|
||||||
category_explanation = N_('Categories are used to sort the different card models.')
|
category_explanation = _('Categories are used to sort the different card models.')
|
||||||
|
|
|
@ -27,7 +27,7 @@ from wcs import fields
|
||||||
from wcs.admin import utils
|
from wcs.admin import utils
|
||||||
from wcs.fields import get_field_options
|
from wcs.fields import get_field_options
|
||||||
from wcs.formdef import FormDef
|
from wcs.formdef import FormDef
|
||||||
from wcs.qommon import N_, _, errors, get_cfg, misc
|
from wcs.qommon import _, errors, get_cfg, misc
|
||||||
from wcs.qommon.admin.menu import command_icon
|
from wcs.qommon.admin.menu import command_icon
|
||||||
from wcs.qommon.backoffice.menu import html_top
|
from wcs.qommon.backoffice.menu import html_top
|
||||||
from wcs.qommon.form import CheckboxWidget, Form, HtmlWidget, SingleSelectWidget, StringWidget
|
from wcs.qommon.form import CheckboxWidget, Form, HtmlWidget, SingleSelectWidget, StringWidget
|
||||||
|
@ -218,7 +218,7 @@ class FieldsDirectory(Directory):
|
||||||
blacklisted_types = []
|
blacklisted_types = []
|
||||||
page_id = None
|
page_id = None
|
||||||
field_var_prefix = '..._'
|
field_var_prefix = '..._'
|
||||||
readonly_message = N_('The fields are readonly.')
|
readonly_message = _('The fields are readonly.')
|
||||||
|
|
||||||
support_import = True
|
support_import = True
|
||||||
|
|
||||||
|
@ -323,7 +323,7 @@ class FieldsDirectory(Directory):
|
||||||
if field.required:
|
if field.required:
|
||||||
required = ''
|
required = ''
|
||||||
else:
|
else:
|
||||||
required = ' - ' + _('optional')
|
required = ' - %s' % _('optional')
|
||||||
r += htmltext('<span class="optional">%s</span>') % required
|
r += htmltext('<span class="optional">%s</span>') % required
|
||||||
if getattr(field, 'condition', None):
|
if getattr(field, 'condition', None):
|
||||||
r += htmltext(' - <span class="condition">%s</span>') % _('depending on condition')
|
r += htmltext(' - <span class="condition">%s</span>') % _('depending on condition')
|
||||||
|
@ -348,8 +348,8 @@ class FieldsDirectory(Directory):
|
||||||
r += htmltext('</ul>')
|
r += htmltext('</ul>')
|
||||||
|
|
||||||
if self.objectdef.is_readonly():
|
if self.objectdef.is_readonly():
|
||||||
get_response().filter['sidebar'] = htmltext('<div class="infonotice"><p>%s</p></div>') % _(
|
get_response().filter['sidebar'] = (
|
||||||
self.readonly_message
|
htmltext('<div class="infonotice"><p>%s</p></div>') % self.readonly_message
|
||||||
)
|
)
|
||||||
if hasattr(self.objectdef, 'snapshot_object'):
|
if hasattr(self.objectdef, 'snapshot_object'):
|
||||||
get_response().filter['sidebar'] += utils.snapshot_info_block(
|
get_response().filter['sidebar'] += utils.snapshot_info_block(
|
||||||
|
@ -472,7 +472,7 @@ class FieldsDirectory(Directory):
|
||||||
{
|
{
|
||||||
'success': 'ok',
|
'success': 'ok',
|
||||||
'additional-action': {
|
'additional-action': {
|
||||||
'message': _('Also move the fields of the page'),
|
'message': str(_('Also move the fields of the page')),
|
||||||
'url': 'move_page_fields?fields=%s&page=%s' % (';'.join(page_field_ids), dropped_element),
|
'url': 'move_page_fields?fields=%s&page=%s' % (';'.join(page_field_ids), dropped_element),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ from wcs.carddef import CardDef
|
||||||
from wcs.categories import Category
|
from wcs.categories import Category
|
||||||
from wcs.formdef import DRAFTS_DEFAULT_LIFESPAN, FormDef, FormdefImportError, FormdefImportRecoverableError
|
from wcs.formdef import DRAFTS_DEFAULT_LIFESPAN, FormDef, FormdefImportError, FormdefImportRecoverableError
|
||||||
from wcs.forms.root import qrcode
|
from wcs.forms.root import qrcode
|
||||||
from wcs.qommon import N_, _, force_str, get_logger, misc, template
|
from wcs.qommon import _, force_str, get_logger, misc, template
|
||||||
from wcs.qommon.afterjobs import AfterJob
|
from wcs.qommon.afterjobs import AfterJob
|
||||||
from wcs.qommon.backoffice.menu import html_top
|
from wcs.qommon.backoffice.menu import html_top
|
||||||
from wcs.qommon.errors import TraversalError
|
from wcs.qommon.errors import TraversalError
|
||||||
|
@ -169,7 +169,7 @@ class AdminFieldDefPage(FieldDefPage):
|
||||||
class AdminFieldsDirectory(FieldsDirectory):
|
class AdminFieldsDirectory(FieldsDirectory):
|
||||||
field_def_page_class = AdminFieldDefPage
|
field_def_page_class = AdminFieldDefPage
|
||||||
field_var_prefix = 'form_var_'
|
field_var_prefix = 'form_var_'
|
||||||
readonly_message = N_('This form is readonly.')
|
readonly_message = _('This form is readonly.')
|
||||||
|
|
||||||
def index_bottom(self):
|
def index_bottom(self):
|
||||||
if self.objectdef.is_readonly():
|
if self.objectdef.is_readonly():
|
||||||
|
@ -189,7 +189,7 @@ class AdminFieldsDirectory(FieldsDirectory):
|
||||||
|
|
||||||
class OptionsDirectory(Directory):
|
class OptionsDirectory(Directory):
|
||||||
category_class = Category
|
category_class = Category
|
||||||
category_empty_choice = N_('Select a category for this form')
|
category_empty_choice = _('Select a category for this form')
|
||||||
_q_exports = [
|
_q_exports = [
|
||||||
'confirmation',
|
'confirmation',
|
||||||
'only_allow_one',
|
'only_allow_one',
|
||||||
|
@ -341,7 +341,7 @@ class OptionsDirectory(Directory):
|
||||||
def category(self):
|
def category(self):
|
||||||
categories = get_categories(self.category_class)
|
categories = get_categories(self.category_class)
|
||||||
form = Form(enctype='multipart/form-data')
|
form = Form(enctype='multipart/form-data')
|
||||||
form.widgets.append(HtmlWidget('<p>%s</p>' % _(self.category_empty_choice)))
|
form.widgets.append(HtmlWidget('<p>%s</p>' % self.category_empty_choice))
|
||||||
form.add(
|
form.add(
|
||||||
SingleSelectWidget,
|
SingleSelectWidget,
|
||||||
'category_id',
|
'category_id',
|
||||||
|
@ -573,10 +573,10 @@ class FormDefPage(Directory):
|
||||||
formdef_default_workflow = '_default'
|
formdef_default_workflow = '_default'
|
||||||
options_directory_class = OptionsDirectory
|
options_directory_class = OptionsDirectory
|
||||||
|
|
||||||
delete_message = N_('You are about to irrevocably delete this form.')
|
delete_message = _('You are about to irrevocably delete this form.')
|
||||||
delete_title = N_('Deleting Form:')
|
delete_title = _('Deleting Form:')
|
||||||
overwrite_message = N_('You can replace this form by uploading a file ' 'or by pointing to a form URL.')
|
overwrite_message = _('You can replace this form by uploading a file ' 'or by pointing to a form URL.')
|
||||||
overwrite_success_message = N_(
|
overwrite_success_message = _(
|
||||||
'The form has been successfully overwritten. '
|
'The form has been successfully overwritten. '
|
||||||
'Do note it kept its existing address and role and workflow parameters.'
|
'Do note it kept its existing address and role and workflow parameters.'
|
||||||
)
|
)
|
||||||
|
@ -834,7 +834,7 @@ class FormDefPage(Directory):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
# removed role ?
|
# removed role ?
|
||||||
roles.append(_('Unknown role (%s)') % x)
|
roles.append(_('Unknown role (%s)') % x)
|
||||||
value = htmltext(', ').join(roles)
|
value = htmltext(', ').join([str(x) for x in roles])
|
||||||
else:
|
else:
|
||||||
value = C_('roles|None')
|
value = C_('roles|None')
|
||||||
return value
|
return value
|
||||||
|
@ -845,7 +845,7 @@ class FormDefPage(Directory):
|
||||||
auth_contexts = get_publisher().get_supported_authentication_contexts()
|
auth_contexts = get_publisher().get_supported_authentication_contexts()
|
||||||
value += ' (%s)' % ', '.join(
|
value += ' (%s)' % ', '.join(
|
||||||
[
|
[
|
||||||
auth_contexts.get(x)
|
str(auth_contexts.get(x))
|
||||||
for x in self.formdef.required_authentication_contexts
|
for x in self.formdef.required_authentication_contexts
|
||||||
if auth_contexts.get(x)
|
if auth_contexts.get(x)
|
||||||
]
|
]
|
||||||
|
@ -1180,11 +1180,11 @@ class FormDefPage(Directory):
|
||||||
def duplicate(self):
|
def duplicate(self):
|
||||||
self.formdefui.formdef.id = None
|
self.formdefui.formdef.id = None
|
||||||
original_name = self.formdefui.formdef.name
|
original_name = self.formdefui.formdef.name
|
||||||
self.formdefui.formdef.name = self.formdefui.formdef.name + _(' (copy)')
|
self.formdefui.formdef.name = '%s %s' % (self.formdefui.formdef.name, _('(copy)'))
|
||||||
formdef_names = [x.name for x in self.formdef_class.select(lightweight=True)]
|
formdef_names = [x.name for x in self.formdef_class.select(lightweight=True)]
|
||||||
no = 2
|
no = 2
|
||||||
while self.formdefui.formdef.name in formdef_names:
|
while self.formdefui.formdef.name in formdef_names:
|
||||||
self.formdefui.formdef.name = _('%(name)s (copy %(no)d)') % {'name': original_name, 'no': no}
|
self.formdefui.formdef.name = str(_('%(name)s (copy %(no)d)')) % {'name': original_name, 'no': no}
|
||||||
no += 1
|
no += 1
|
||||||
self.formdefui.formdef.url_name = None
|
self.formdefui.formdef.url_name = None
|
||||||
self.formdefui.formdef.table_name = None
|
self.formdefui.formdef.table_name = None
|
||||||
|
@ -1212,16 +1212,16 @@ class FormDefPage(Directory):
|
||||||
if check_count_message:
|
if check_count_message:
|
||||||
form.widgets.append(HtmlWidget('<p>%s</p>' % check_count_message))
|
form.widgets.append(HtmlWidget('<p>%s</p>' % check_count_message))
|
||||||
else:
|
else:
|
||||||
form.widgets.append(HtmlWidget('<p>%s</p>' % _(self.delete_message)))
|
form.widgets.append(HtmlWidget('<p>%s</p>' % self.delete_message))
|
||||||
form.add_submit('delete', _('Delete'))
|
form.add_submit('delete', _('Delete'))
|
||||||
form.add_submit('cancel', _('Cancel'))
|
form.add_submit('cancel', _('Cancel'))
|
||||||
if form.get_widget('cancel').parse() or (form.is_submitted() and check_count_message):
|
if form.get_widget('cancel').parse() or (form.is_submitted() and check_count_message):
|
||||||
return redirect('..')
|
return redirect('..')
|
||||||
if not form.is_submitted() or form.has_errors():
|
if not form.is_submitted() or form.has_errors():
|
||||||
get_response().breadcrumb.append(('delete', _('Delete')))
|
get_response().breadcrumb.append(('delete', _('Delete')))
|
||||||
self.html_top(title=_(self.delete_title))
|
self.html_top(title=self.delete_title)
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
r += htmltext('<h2>%s %s</h2>') % (_(self.delete_title), self.formdef.name)
|
r += htmltext('<h2>%s %s</h2>') % (self.delete_title, self.formdef.name)
|
||||||
r += form.render()
|
r += form.render()
|
||||||
return r.getvalue()
|
return r.getvalue()
|
||||||
else:
|
else:
|
||||||
|
@ -1249,7 +1249,7 @@ class FormDefPage(Directory):
|
||||||
self.html_top(title=_('Overwrite'))
|
self.html_top(title=_('Overwrite'))
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
r += htmltext('<h2>%s</h2>') % _('Overwrite')
|
r += htmltext('<h2>%s</h2>') % _('Overwrite')
|
||||||
r += htmltext('<p>%s</p>') % _(self.overwrite_message)
|
r += htmltext('<p>%s</p>') % self.overwrite_message
|
||||||
r += form.render()
|
r += form.render()
|
||||||
return r.getvalue()
|
return r.getvalue()
|
||||||
|
|
||||||
|
@ -1320,7 +1320,7 @@ class FormDefPage(Directory):
|
||||||
new_formdef.roles = self.formdef.roles
|
new_formdef.roles = self.formdef.roles
|
||||||
self.formdef = new_formdef
|
self.formdef = new_formdef
|
||||||
self.formdef.store(comment=_('Overwritten'))
|
self.formdef.store(comment=_('Overwritten'))
|
||||||
get_session().message = ('info', _(self.overwrite_success_message))
|
get_session().message = ('info', str(self.overwrite_success_message))
|
||||||
return redirect('.')
|
return redirect('.')
|
||||||
|
|
||||||
def get_incompatible_field_ids(self, new_formdef):
|
def get_incompatible_field_ids(self, new_formdef):
|
||||||
|
@ -1539,9 +1539,12 @@ class FormDefPage(Directory):
|
||||||
count = self.formdef.data_class().count()
|
count = self.formdef.data_class().count()
|
||||||
archiver = Archiver(self.formdef)
|
archiver = Archiver(self.formdef)
|
||||||
if count > 100: # Arbitrary threshold
|
if count > 100: # Arbitrary threshold
|
||||||
job = get_response().add_after_job(str(N_('Archiving forms')), archiver.archive)
|
job = get_response().add_after_job(
|
||||||
job.done_action_url = self.formdef.get_admin_url() + 'archive?job=%s' % job.id
|
_('Archiving forms'),
|
||||||
job.done_action_label = _('Download Archive')
|
archiver.archive,
|
||||||
|
done_action_url=self.formdef.get_admin_url() + 'archive?job=%(job_id)s',
|
||||||
|
done_action_label=_('Download Archive'),
|
||||||
|
)
|
||||||
job.store()
|
job.store()
|
||||||
return redirect(job.get_processing_url())
|
return redirect(job.get_processing_url())
|
||||||
else:
|
else:
|
||||||
|
@ -1632,9 +1635,12 @@ class FormDefPage(Directory):
|
||||||
count = self.formdef.data_class().count()
|
count = self.formdef.data_class().count()
|
||||||
anonymiser = Anonymiser(self.formdef, status_ids, before_date)
|
anonymiser = Anonymiser(self.formdef, status_ids, before_date)
|
||||||
if count > 100: # Arbitrary threshold
|
if count > 100: # Arbitrary threshold
|
||||||
job = get_response().add_after_job(str(N_('Anonymising forms')), anonymiser.anonymise)
|
job = get_response().add_after_job(
|
||||||
job.done_action_url = self.formdef.get_admin_url()
|
_('Anonymising forms'),
|
||||||
job.done_action_label = _('Back')
|
anonymiser.anonymise,
|
||||||
|
done_action_url=self.formdef.get_admin_url(),
|
||||||
|
done_action_label=_('Back'),
|
||||||
|
)
|
||||||
job.store()
|
job.store()
|
||||||
return redirect(job.get_processing_url())
|
return redirect(job.get_processing_url())
|
||||||
else:
|
else:
|
||||||
|
@ -1739,15 +1745,15 @@ class FormsDirectory(AccessControlled, Directory):
|
||||||
formdef_page_class = FormDefPage
|
formdef_page_class = FormDefPage
|
||||||
formdef_ui_class = FormDefUI
|
formdef_ui_class = FormDefUI
|
||||||
|
|
||||||
top_title = N_('Forms')
|
top_title = _('Forms')
|
||||||
import_title = N_('Import Form')
|
import_title = _('Import Form')
|
||||||
import_submit_label = N_('Import Form')
|
import_submit_label = _('Import Form')
|
||||||
import_paragraph = N_('You can install a new form by uploading a file ' 'or by pointing to the form URL.')
|
import_paragraph = _('You can install a new form by uploading a file ' 'or by pointing to the form URL.')
|
||||||
import_loading_error_message = N_('Error loading form (%s).')
|
import_loading_error_message = _('Error loading form (%s).')
|
||||||
import_success_message = N_(
|
import_success_message = _(
|
||||||
'This form has been successfully imported. ' 'Do note it is disabled by default.'
|
'This form has been successfully imported. ' 'Do note it is disabled by default.'
|
||||||
)
|
)
|
||||||
import_error_message = N_(
|
import_error_message = _(
|
||||||
'Imported form contained errors and has been automatically fixed, '
|
'Imported form contained errors and has been automatically fixed, '
|
||||||
'you should nevertheless check everything is ok. '
|
'you should nevertheless check everything is ok. '
|
||||||
'Do note it is disabled by default.'
|
'Do note it is disabled by default.'
|
||||||
|
@ -1761,7 +1767,7 @@ class FormsDirectory(AccessControlled, Directory):
|
||||||
return super()._q_traverse(path)
|
return super()._q_traverse(path)
|
||||||
|
|
||||||
def _q_index(self):
|
def _q_index(self):
|
||||||
self.html_top(title=_(self.top_title))
|
self.html_top(title=self.top_title)
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
get_response().add_javascript(['jquery.js', 'widget_list.js'])
|
get_response().add_javascript(['jquery.js', 'widget_list.js'])
|
||||||
r += self.form_actions()
|
r += self.form_actions()
|
||||||
|
@ -1865,7 +1871,7 @@ class FormsDirectory(AccessControlled, Directory):
|
||||||
|
|
||||||
form.add(FileWidget, 'file', title=_('File'), required=False)
|
form.add(FileWidget, 'file', title=_('File'), required=False)
|
||||||
form.add(UrlWidget, 'url', title=_('Address'), required=False, size=50)
|
form.add(UrlWidget, 'url', title=_('Address'), required=False, size=50)
|
||||||
form.add_submit('submit', _(self.import_submit_label))
|
form.add_submit('submit', self.import_submit_label)
|
||||||
form.add_submit('cancel', _('Cancel'))
|
form.add_submit('cancel', _('Cancel'))
|
||||||
|
|
||||||
if form.get_submit() == 'cancel':
|
if form.get_submit() == 'cancel':
|
||||||
|
@ -1878,10 +1884,10 @@ class FormsDirectory(AccessControlled, Directory):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
get_response().breadcrumb.append(('import', _('Import')))
|
get_response().breadcrumb.append(('import', _('Import')))
|
||||||
self.html_top(title=_(self.import_title))
|
self.html_top(title=self.import_title)
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
r += htmltext('<h2>%s</h2>') % _(self.import_title)
|
r += htmltext('<h2>%s</h2>') % self.import_title
|
||||||
r += htmltext('<p>%s</p>') % _(self.import_paragraph)
|
r += htmltext('<p>%s</p>') % self.import_paragraph
|
||||||
r += form.render()
|
r += form.render()
|
||||||
return r.getvalue()
|
return r.getvalue()
|
||||||
|
|
||||||
|
@ -1894,7 +1900,7 @@ class FormsDirectory(AccessControlled, Directory):
|
||||||
try:
|
try:
|
||||||
fp = misc.urlopen(url)
|
fp = misc.urlopen(url)
|
||||||
except misc.ConnectionError as e:
|
except misc.ConnectionError as e:
|
||||||
form.set_error('url', _(self.import_loading_error_message) % str(e))
|
form.set_error('url', self.import_loading_error_message % str(e))
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
else:
|
else:
|
||||||
form.set_error('file', _('You have to enter a file or a URL.'))
|
form.set_error('file', _('You have to enter a file or a URL.'))
|
||||||
|
@ -1904,11 +1910,11 @@ class FormsDirectory(AccessControlled, Directory):
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
formdef = self.formdef_class.import_from_xml(fp)
|
formdef = self.formdef_class.import_from_xml(fp)
|
||||||
get_session().message = ('info', _(self.import_success_message))
|
get_session().message = ('info', str(self.import_success_message))
|
||||||
except FormdefImportRecoverableError as e:
|
except FormdefImportRecoverableError as e:
|
||||||
fp.seek(0)
|
fp.seek(0)
|
||||||
formdef = self.formdef_class.import_from_xml(fp, fix_on_error=True)
|
formdef = self.formdef_class.import_from_xml(fp, fix_on_error=True)
|
||||||
get_session().message = ('info', _(self.import_error_message))
|
get_session().message = ('info', str(self.import_error_message))
|
||||||
except FormdefImportError as e:
|
except FormdefImportError as e:
|
||||||
error = True
|
error = True
|
||||||
reason = _(e.msg) % e.msg_args
|
reason = _(e.msg) % e.msg_args
|
||||||
|
@ -1936,7 +1942,7 @@ class FormsDirectory(AccessControlled, Directory):
|
||||||
|
|
||||||
|
|
||||||
class UpdateDigestAfterJob(AfterJob):
|
class UpdateDigestAfterJob(AfterJob):
|
||||||
label = N_('Updating digests')
|
label = _('Updating digests')
|
||||||
|
|
||||||
def __init__(self, formdef):
|
def __init__(self, formdef):
|
||||||
super().__init__(formdef_class=formdef.__class__, formdef_id=formdef.id)
|
super().__init__(formdef_class=formdef.__class__, formdef_id=formdef.id)
|
||||||
|
|
|
@ -38,7 +38,7 @@ from wcs.blocks import BlockDef
|
||||||
from wcs.carddef import CardDef
|
from wcs.carddef import CardDef
|
||||||
from wcs.data_sources import NamedDataSource
|
from wcs.data_sources import NamedDataSource
|
||||||
from wcs.formdef import FormDef
|
from wcs.formdef import FormDef
|
||||||
from wcs.qommon import N_, _, errors, get_cfg, ident, misc, template
|
from wcs.qommon import _, errors, get_cfg, ident, misc, template
|
||||||
from wcs.qommon.admin.cfg import cfg_submit
|
from wcs.qommon.admin.cfg import cfg_submit
|
||||||
from wcs.qommon.admin.emails import EmailsDirectory
|
from wcs.qommon.admin.emails import EmailsDirectory
|
||||||
from wcs.qommon.admin.logger import LoggerDirectory
|
from wcs.qommon.admin.logger import LoggerDirectory
|
||||||
|
@ -706,13 +706,13 @@ class SettingsDirectory(QommonSettingsDirectory):
|
||||||
|
|
||||||
permission_keys = []
|
permission_keys = []
|
||||||
admin_sections = [
|
admin_sections = [
|
||||||
('forms', N_('Forms')),
|
('forms', _('Forms')),
|
||||||
('cards', N_('Card Models')),
|
('cards', _('Card Models')),
|
||||||
('workflows', N_('Workflows')),
|
('workflows', _('Workflows')),
|
||||||
('users', N_('Users')),
|
('users', _('Users')),
|
||||||
('roles', N_('Roles')),
|
('roles', _('Roles')),
|
||||||
('categories', N_('Categories')),
|
('categories', _('Categories')),
|
||||||
('settings', N_('Settings')),
|
('settings', _('Settings')),
|
||||||
]
|
]
|
||||||
for k, v in admin_sections:
|
for k, v in admin_sections:
|
||||||
if k == 'cards' and not StudioDirectory.is_visible():
|
if k == 'cards' and not StudioDirectory.is_visible():
|
||||||
|
@ -1128,10 +1128,13 @@ class SettingsDirectory(QommonSettingsDirectory):
|
||||||
|
|
||||||
exporter = Exporter(dirs, settings=form.get_widget('settings').parse())
|
exporter = Exporter(dirs, settings=form.get_widget('settings').parse())
|
||||||
|
|
||||||
job = get_response().add_after_job(N_('Exporting site settings'), exporter.export)
|
job = get_response().add_after_job(
|
||||||
job.done_action_url = get_request().get_url() + '?download=%s' % job.id
|
_('Exporting site settings'),
|
||||||
job.done_action_label = _('Download Export')
|
exporter.export,
|
||||||
job.done_button_attributes = {'download': 'export.wcs'}
|
done_action_url=get_request().get_url() + '?download=%(job_id)s',
|
||||||
|
done_action_label=_('Download Export'),
|
||||||
|
done_button_attributes={'download': 'export.wcs'},
|
||||||
|
)
|
||||||
job.store()
|
job.store()
|
||||||
return redirect(job.get_processing_url())
|
return redirect(job.get_processing_url())
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ from quixote.directory import Directory
|
||||||
from quixote.html import TemplateIO, htmltext
|
from quixote.html import TemplateIO, htmltext
|
||||||
|
|
||||||
import wcs.qommon.storage as st
|
import wcs.qommon.storage as st
|
||||||
from wcs.qommon import N_, _, errors, force_str, get_cfg, ident, misc
|
from wcs.qommon import _, errors, force_str, get_cfg, ident, misc
|
||||||
from wcs.qommon.admin.emails import EmailsDirectory
|
from wcs.qommon.admin.emails import EmailsDirectory
|
||||||
from wcs.qommon.admin.menu import error_page
|
from wcs.qommon.admin.menu import error_page
|
||||||
from wcs.qommon.backoffice.listing import pagination_links
|
from wcs.qommon.backoffice.listing import pagination_links
|
||||||
|
@ -537,11 +537,11 @@ class UsersDirectory(Directory):
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'email_with_token',
|
'email_with_token',
|
||||||
N_('Identification token'),
|
_('Identification token'),
|
||||||
N_('Available variables: token, token_url, sitename'),
|
_('Available variables: token, token_url, sitename'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('Access to [sitename]'),
|
default_subject=_('Access to [sitename]'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Hello,
|
Hello,
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ from wcs.backoffice.studio import StudioDirectory
|
||||||
from wcs.carddef import CardDef
|
from wcs.carddef import CardDef
|
||||||
from wcs.formdata import Evolution
|
from wcs.formdata import Evolution
|
||||||
from wcs.formdef import FormDef
|
from wcs.formdef import FormDef
|
||||||
from wcs.qommon import N_, _, errors, force_str, get_logger, misc, template
|
from wcs.qommon import _, errors, force_str, get_logger, misc, template
|
||||||
from wcs.qommon.admin.menu import command_icon
|
from wcs.qommon.admin.menu import command_icon
|
||||||
from wcs.qommon.backoffice.menu import html_top
|
from wcs.qommon.backoffice.menu import html_top
|
||||||
from wcs.qommon.form import (
|
from wcs.qommon.form import (
|
||||||
|
@ -356,7 +356,7 @@ class WorkflowItemPage(Directory):
|
||||||
if not self.workflow.is_readonly():
|
if not self.workflow.is_readonly():
|
||||||
submit_label = _('Submit')
|
submit_label = _('Submit')
|
||||||
if hasattr(self.item, 'submit_button_label'):
|
if hasattr(self.item, 'submit_button_label'):
|
||||||
submit_label = _(self.item.submit_button_label)
|
submit_label = self.item.submit_button_label
|
||||||
form.add_submit('submit', submit_label)
|
form.add_submit('submit', submit_label)
|
||||||
form.add_submit('cancel', _('Cancel'))
|
form.add_submit('cancel', _('Cancel'))
|
||||||
|
|
||||||
|
@ -955,7 +955,7 @@ class WorkflowVariablesFieldsDirectory(FieldsDirectory):
|
||||||
support_import = False
|
support_import = False
|
||||||
blacklisted_types = ['page', 'blocks', 'computed']
|
blacklisted_types = ['page', 'blocks', 'computed']
|
||||||
field_var_prefix = 'form_option_'
|
field_var_prefix = 'form_option_'
|
||||||
readonly_message = N_('This workflow is readonly.')
|
readonly_message = _('This workflow is readonly.')
|
||||||
|
|
||||||
def index_top(self):
|
def index_top(self):
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
|
@ -978,7 +978,7 @@ class WorkflowBackofficeFieldsDirectory(FieldsDirectory):
|
||||||
blacklisted_types = ['page', 'blocks', 'computed']
|
blacklisted_types = ['page', 'blocks', 'computed']
|
||||||
blacklisted_attributes = ['condition']
|
blacklisted_attributes = ['condition']
|
||||||
field_var_prefix = 'form_var_'
|
field_var_prefix = 'form_var_'
|
||||||
readonly_message = N_('This workflow is readonly.')
|
readonly_message = _('This workflow is readonly.')
|
||||||
|
|
||||||
def index_top(self):
|
def index_top(self):
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
|
@ -1746,11 +1746,14 @@ class WorkflowPage(Directory):
|
||||||
def duplicate(self):
|
def duplicate(self):
|
||||||
self.workflow_ui.workflow.id = None
|
self.workflow_ui.workflow.id = None
|
||||||
original_name = self.workflow_ui.workflow.name
|
original_name = self.workflow_ui.workflow.name
|
||||||
self.workflow_ui.workflow.name = self.workflow_ui.workflow.name + _(' (copy)')
|
self.workflow_ui.workflow.name = '%s %s' % (self.workflow_ui.workflow.name, _('(copy)'))
|
||||||
workflow_names = [x.name for x in Workflow.select()]
|
workflow_names = [x.name for x in Workflow.select()]
|
||||||
no = 2
|
no = 2
|
||||||
while self.workflow_ui.workflow.name in workflow_names:
|
while self.workflow_ui.workflow.name in workflow_names:
|
||||||
self.workflow_ui.workflow.name = _('%(name)s (copy %(no)d)') % {'name': original_name, 'no': no}
|
self.workflow_ui.workflow.name = str(_('%(name)s (copy %(no)d)')) % {
|
||||||
|
'name': original_name,
|
||||||
|
'no': no,
|
||||||
|
}
|
||||||
no += 1
|
no += 1
|
||||||
self.workflow_ui.workflow.store()
|
self.workflow_ui.workflow.store()
|
||||||
return redirect('../%s/' % self.workflow_ui.workflow.id)
|
return redirect('../%s/' % self.workflow_ui.workflow.id)
|
||||||
|
|
|
@ -1261,7 +1261,7 @@ def validate_expression(request, *args, **kwargs):
|
||||||
if expression and re.match(r'^=.*\[[a-zA-Z_]\w*\]', expression):
|
if expression and re.match(r'^=.*\[[a-zA-Z_]\w*\]', expression):
|
||||||
hint['klass'] = 'warning'
|
hint['klass'] = 'warning'
|
||||||
hint['msg'] = _('Make sure you want a Python expression, not a simple template string.')
|
hint['msg'] = _('Make sure you want a Python expression, not a simple template string.')
|
||||||
return HttpResponse(json.dumps(hint), content_type='application/json')
|
return JsonResponse(hint)
|
||||||
|
|
||||||
|
|
||||||
def validate_condition(request, *args, **kwargs):
|
def validate_condition(request, *args, **kwargs):
|
||||||
|
@ -1274,7 +1274,7 @@ def validate_condition(request, *args, **kwargs):
|
||||||
except ValidationError as e:
|
except ValidationError as e:
|
||||||
hint['klass'] = 'error'
|
hint['klass'] = 'error'
|
||||||
hint['msg'] = str(e)
|
hint['msg'] = str(e)
|
||||||
return HttpResponse(json.dumps(hint), content_type='application/json')
|
return JsonResponse(hint)
|
||||||
|
|
||||||
|
|
||||||
def provisionning(request):
|
def provisionning(request):
|
||||||
|
|
|
@ -28,7 +28,7 @@ from wcs.carddef import CardDef
|
||||||
from wcs.categories import CardDefCategory
|
from wcs.categories import CardDefCategory
|
||||||
from wcs.workflows import Workflow
|
from wcs.workflows import Workflow
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
from ..qommon.misc import C_
|
from ..qommon.misc import C_
|
||||||
from ..qommon.storage import NotEqual, Null
|
from ..qommon.storage import NotEqual, Null
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ class CardDefUI(FormDefUI):
|
||||||
|
|
||||||
class CardDefOptionsDirectory(OptionsDirectory):
|
class CardDefOptionsDirectory(OptionsDirectory):
|
||||||
category_class = CardDefCategory
|
category_class = CardDefCategory
|
||||||
category_empty_choice = N_('Select a category for this card model')
|
category_empty_choice = _('Select a category for this card model')
|
||||||
|
|
||||||
|
|
||||||
class CardDefPage(FormDefPage):
|
class CardDefPage(FormDefPage):
|
||||||
|
@ -51,12 +51,12 @@ class CardDefPage(FormDefPage):
|
||||||
|
|
||||||
options_directory_class = CardDefOptionsDirectory
|
options_directory_class = CardDefOptionsDirectory
|
||||||
|
|
||||||
delete_message = N_('You are about to irrevocably delete this card model.')
|
delete_message = _('You are about to irrevocably delete this card model.')
|
||||||
delete_title = N_('Deleting Card Model:')
|
delete_title = _('Deleting Card Model:')
|
||||||
overwrite_message = N_(
|
overwrite_message = _(
|
||||||
'You can replace this card model by uploading a file ' 'or by pointing to a form URL.'
|
'You can replace this card model by uploading a file ' 'or by pointing to a form URL.'
|
||||||
)
|
)
|
||||||
overwrite_success_message = N_(
|
overwrite_success_message = _(
|
||||||
'The card model has been successfully overwritten. '
|
'The card model has been successfully overwritten. '
|
||||||
'Do note it kept its existing address and role and workflow parameters.'
|
'Do note it kept its existing address and role and workflow parameters.'
|
||||||
)
|
)
|
||||||
|
@ -250,15 +250,15 @@ class CardsDirectory(FormsDirectory):
|
||||||
formdef_page_class = CardDefPage
|
formdef_page_class = CardDefPage
|
||||||
formdef_ui_class = CardDefUI
|
formdef_ui_class = CardDefUI
|
||||||
|
|
||||||
top_title = N_('Card Models')
|
top_title = _('Card Models')
|
||||||
import_title = N_('Import Card Model')
|
import_title = _('Import Card Model')
|
||||||
import_submit_label = N_('Import Card Model')
|
import_submit_label = _('Import Card Model')
|
||||||
import_paragraph = N_(
|
import_paragraph = _(
|
||||||
'You can install a new card model by uploading a file ' 'or by pointing to the card model URL.'
|
'You can install a new card model by uploading a file ' 'or by pointing to the card model URL.'
|
||||||
)
|
)
|
||||||
import_loading_error_message = N_('Error loading card model (%s).')
|
import_loading_error_message = _('Error loading card model (%s).')
|
||||||
import_success_message = N_('This card model has been successfully imported. ')
|
import_success_message = _('This card model has been successfully imported. ')
|
||||||
import_error_message = N_(
|
import_error_message = _(
|
||||||
'Imported card model contained errors and has been automatically fixed, '
|
'Imported card model contained errors and has been automatically fixed, '
|
||||||
'you should nevertheless check everything is ok. '
|
'you should nevertheless check everything is ok. '
|
||||||
)
|
)
|
||||||
|
|
|
@ -26,7 +26,7 @@ from wcs import fields
|
||||||
from wcs.carddef import CardDef
|
from wcs.carddef import CardDef
|
||||||
from wcs.categories import CardDefCategory
|
from wcs.categories import CardDefCategory
|
||||||
|
|
||||||
from ..qommon import N_, _, errors, template
|
from ..qommon import _, errors, template
|
||||||
from ..qommon.afterjobs import AfterJob
|
from ..qommon.afterjobs import AfterJob
|
||||||
from ..qommon.backoffice.menu import html_top
|
from ..qommon.backoffice.menu import html_top
|
||||||
from ..qommon.form import FileWidget, Form
|
from ..qommon.form import FileWidget, Form
|
||||||
|
@ -99,7 +99,7 @@ class CardPage(FormPage):
|
||||||
]
|
]
|
||||||
admin_permission = 'cards'
|
admin_permission = 'cards'
|
||||||
formdef_class = CardDef
|
formdef_class = CardDef
|
||||||
search_label = N_('Search in card content')
|
search_label = _('Search in card content')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def add(self):
|
def add(self):
|
||||||
|
@ -331,8 +331,8 @@ class CardFillPage(FormFillPage):
|
||||||
class CardBackOfficeStatusPage(FormBackOfficeStatusPage):
|
class CardBackOfficeStatusPage(FormBackOfficeStatusPage):
|
||||||
form_page_class = CardFillPage
|
form_page_class = CardFillPage
|
||||||
|
|
||||||
sidebar_recorded_message = N_('The card has been recorded on %(date)s with the number %(number)s.')
|
sidebar_recorded_message = _('The card has been recorded on %(date)s with the number %(number)s.')
|
||||||
sidebar_recorded_by_agent_message = N_(
|
sidebar_recorded_by_agent_message = _(
|
||||||
'The card has been recorded on %(date)s with the number %(number)s by %(agent)s.'
|
'The card has been recorded on %(date)s with the number %(number)s by %(agent)s.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ from wcs.roles import logged_users_role
|
||||||
from wcs.variables import LazyFieldVar
|
from wcs.variables import LazyFieldVar
|
||||||
from wcs.workflows import WorkflowStatusItem, template_on_formdata
|
from wcs.workflows import WorkflowStatusItem, template_on_formdata
|
||||||
|
|
||||||
from ..qommon import N_, _, emails, errors, ezt, force_str, get_cfg, get_logger, misc, ngettext, ods, sms
|
from ..qommon import _, emails, errors, ezt, force_str, get_cfg, get_logger, misc, ngettext, ods, sms
|
||||||
from ..qommon.admin.emails import EmailsDirectory
|
from ..qommon.admin.emails import EmailsDirectory
|
||||||
from ..qommon.admin.menu import command_icon
|
from ..qommon.admin.menu import command_icon
|
||||||
from ..qommon.afterjobs import AfterJob
|
from ..qommon.afterjobs import AfterJob
|
||||||
|
@ -127,7 +127,7 @@ def geojson_formdatas(formdatas, geoloc_key='base', fields=None):
|
||||||
'url': formdata_backoffice_url,
|
'url': formdata_backoffice_url,
|
||||||
'status_name': str(htmlescape(status.name)),
|
'status_name': str(htmlescape(status.name)),
|
||||||
'status_colour': '#%s' % status_colour,
|
'status_colour': '#%s' % status_colour,
|
||||||
'view_label': _('View'),
|
'view_label': force_text(_('View')),
|
||||||
},
|
},
|
||||||
'geometry': {
|
'geometry': {
|
||||||
'type': 'Point',
|
'type': 'Point',
|
||||||
|
@ -1060,7 +1060,7 @@ class ManagementDirectory(Directory):
|
||||||
FakeField('status', 'status', _('Status')),
|
FakeField('status', 'status', _('Status')),
|
||||||
]
|
]
|
||||||
get_response().set_content_type('application/json')
|
get_response().set_content_type('application/json')
|
||||||
return json.dumps(geojson_formdatas(formdatas, fields=fields))
|
return json.dumps(geojson_formdatas(formdatas, fields=fields), cls=misc.JSONEncoder)
|
||||||
|
|
||||||
def map(self):
|
def map(self):
|
||||||
if not get_publisher().is_using_postgresql():
|
if not get_publisher().is_using_postgresql():
|
||||||
|
@ -1107,7 +1107,7 @@ class FormPage(Directory):
|
||||||
use_default_view = False
|
use_default_view = False
|
||||||
admin_permission = 'forms'
|
admin_permission = 'forms'
|
||||||
formdef_class = FormDef
|
formdef_class = FormDef
|
||||||
search_label = N_('Search in form content')
|
search_label = _('Search in form content')
|
||||||
WCS_SYNC_EXPORT_LIMIT = 100 # Arbitrary threshold
|
WCS_SYNC_EXPORT_LIMIT = 100 # Arbitrary threshold
|
||||||
|
|
||||||
def __init__(self, component=None, formdef=None, view=None, update_breadcrumbs=True):
|
def __init__(self, component=None, formdef=None, view=None, update_breadcrumbs=True):
|
||||||
|
@ -1568,7 +1568,7 @@ class FormPage(Directory):
|
||||||
r += htmltext('<input type="hidden" name="order_by" value="%s"/>') % order_by
|
r += htmltext('<input type="hidden" name="order_by" value="%s"/>') % order_by
|
||||||
|
|
||||||
if get_publisher().is_using_postgresql():
|
if get_publisher().is_using_postgresql():
|
||||||
r += htmltext('<h3>%s</h3>') % _(self.search_label)
|
r += htmltext('<h3>%s</h3>') % self.search_label
|
||||||
if get_request().form.get('q'):
|
if get_request().form.get('q'):
|
||||||
q = force_text(get_request().form.get('q'))
|
q = force_text(get_request().form.get('q'))
|
||||||
r += htmltext('<input class="inline-input" name="q" value="%s">') % force_str(q)
|
r += htmltext('<input class="inline-input" name="q" value="%s">') % force_str(q)
|
||||||
|
@ -2331,7 +2331,7 @@ class FormPage(Directory):
|
||||||
fields, selected_filter, user=user, query=query, criterias=criterias
|
fields, selected_filter, user=user, query=query, criterias=criterias
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
return json.dumps(geojson_formdatas(items, fields=fields))
|
return json.dumps(geojson_formdatas(items, fields=fields), cls=misc.JSONEncoder)
|
||||||
|
|
||||||
def ics(self):
|
def ics(self):
|
||||||
if get_request().has_anonymised_data_api_restriction():
|
if get_request().has_anonymised_data_api_restriction():
|
||||||
|
@ -2716,8 +2716,8 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
||||||
]
|
]
|
||||||
form_page_class = FormFillPage
|
form_page_class = FormFillPage
|
||||||
|
|
||||||
sidebar_recorded_message = N_('The form has been recorded on %(date)s with the number %(number)s.')
|
sidebar_recorded_message = _('The form has been recorded on %(date)s with the number %(number)s.')
|
||||||
sidebar_recorded_by_agent_message = N_(
|
sidebar_recorded_by_agent_message = _(
|
||||||
'The form has been recorded on %(date)s with the number %(number)s by %(agent)s.'
|
'The form has been recorded on %(date)s with the number %(number)s by %(agent)s.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2811,13 +2811,13 @@ class FormBackOfficeStatusPage(FormStatusPage):
|
||||||
agent_user = get_publisher().user_class.get(formdata.submission_agent_id, ignore_errors=True)
|
agent_user = get_publisher().user_class.get(formdata.submission_agent_id, ignore_errors=True)
|
||||||
|
|
||||||
if agent_user:
|
if agent_user:
|
||||||
r += _(self.sidebar_recorded_by_agent_message) % {
|
r += self.sidebar_recorded_by_agent_message % {
|
||||||
'date': tm,
|
'date': tm,
|
||||||
'number': formdata.get_display_id(),
|
'number': formdata.get_display_id(),
|
||||||
'agent': agent_user.get_display_name(),
|
'agent': agent_user.get_display_name(),
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
r += _(self.sidebar_recorded_message) % {'date': tm, 'number': formdata.get_display_id()}
|
r += self.sidebar_recorded_message % {'date': tm, 'number': formdata.get_display_id()}
|
||||||
r += htmltext('</p>')
|
r += htmltext('</p>')
|
||||||
try:
|
try:
|
||||||
status_colour = formdata.get_status().colour
|
status_colour = formdata.get_status().colour
|
||||||
|
@ -3441,7 +3441,7 @@ class FakeField:
|
||||||
def __init__(self, id, type_, label, addable=True):
|
def __init__(self, id, type_, label, addable=True):
|
||||||
self.id = id
|
self.id = id
|
||||||
self.type = type_
|
self.type = type_
|
||||||
self.label = label
|
self.label = force_text(label)
|
||||||
self.fake = True
|
self.fake = True
|
||||||
self.varname = id.replace('-', '_')
|
self.varname = id.replace('-', '_')
|
||||||
self.store_display_value = None
|
self.store_display_value = None
|
||||||
|
@ -3545,10 +3545,10 @@ var month_line = %(month_line)s;
|
||||||
var year_line = %(year_line)s;
|
var year_line = %(year_line)s;
|
||||||
</script>'''
|
</script>'''
|
||||||
% {
|
% {
|
||||||
'weekday_line': json.dumps(weekday_totals),
|
'weekday_line': json.dumps(weekday_totals, cls=misc.JSONEncoder),
|
||||||
'hour_line': json.dumps(hour_totals),
|
'hour_line': json.dumps(hour_totals, cls=misc.JSONEncoder),
|
||||||
'month_line': json.dumps(monthly_totals),
|
'month_line': json.dumps(monthly_totals, cls=misc.JSONEncoder),
|
||||||
'year_line': json.dumps(yearly_totals),
|
'year_line': json.dumps(yearly_totals, cls=misc.JSONEncoder),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -3735,7 +3735,7 @@ class MassActionAfterJob(AfterJob):
|
||||||
|
|
||||||
|
|
||||||
class CsvExportAfterJob(AfterJob):
|
class CsvExportAfterJob(AfterJob):
|
||||||
label = N_('Exporting forms in CSV')
|
label = _('Exporting to CSV file')
|
||||||
|
|
||||||
def __init__(self, formdef, **kwargs):
|
def __init__(self, formdef, **kwargs):
|
||||||
super().__init__(formdef_class=formdef.__class__, formdef_id=formdef.id, **kwargs)
|
super().__init__(formdef_class=formdef.__class__, formdef_id=formdef.id, **kwargs)
|
||||||
|
@ -3799,6 +3799,8 @@ class CsvExportAfterJob(AfterJob):
|
||||||
|
|
||||||
|
|
||||||
class OdsExportAfterJob(CsvExportAfterJob):
|
class OdsExportAfterJob(CsvExportAfterJob):
|
||||||
|
label = _('Exporting to ODS file')
|
||||||
|
|
||||||
def __init__(self, formdef, **kwargs):
|
def __init__(self, formdef, **kwargs):
|
||||||
super().__init__(formdef=formdef, **kwargs)
|
super().__init__(formdef=formdef, **kwargs)
|
||||||
self.file_name = '%s.ods' % formdef.url_name
|
self.file_name = '%s.ods' % formdef.url_name
|
||||||
|
|
|
@ -28,7 +28,7 @@ import wcs.admin.users
|
||||||
import wcs.admin.workflows
|
import wcs.admin.workflows
|
||||||
from wcs.formdef import FormDef
|
from wcs.formdef import FormDef
|
||||||
|
|
||||||
from ..qommon import N_, _, errors, get_cfg, misc, template
|
from ..qommon import _, errors, get_cfg, misc, template
|
||||||
from ..qommon.afterjobs import AfterJob
|
from ..qommon.afterjobs import AfterJob
|
||||||
from ..qommon.backoffice import BackofficeRootDirectory
|
from ..qommon.backoffice import BackofficeRootDirectory
|
||||||
from ..qommon.backoffice.menu import html_top
|
from ..qommon.backoffice.menu import html_top
|
||||||
|
@ -54,16 +54,16 @@ class RootDirectory(BackofficeRootDirectory):
|
||||||
submission = SubmissionDirectory()
|
submission = SubmissionDirectory()
|
||||||
|
|
||||||
menu_items = [
|
menu_items = [
|
||||||
('submission/', N_('Submission')),
|
('submission/', _('Submission')),
|
||||||
('management/', N_('Management')),
|
('management/', _('Management')),
|
||||||
('data/', N_('Cards'), {'check_display_function': studio.is_visible}),
|
('data/', _('Cards'), {'check_display_function': studio.is_visible}),
|
||||||
('studio/', N_('Studio'), {'check_display_function': studio.is_visible}),
|
('studio/', _('Studio'), {'check_display_function': studio.is_visible}),
|
||||||
('forms/', N_('Forms Workshop'), {'sub': True}),
|
('forms/', _('Forms Workshop'), {'sub': True}),
|
||||||
('cards/', N_('Card Models'), {'sub': True, 'check_display_function': studio.is_visible}),
|
('cards/', _('Card Models'), {'sub': True, 'check_display_function': studio.is_visible}),
|
||||||
('workflows/', N_('Workflows Workshop'), {'sub': True}),
|
('workflows/', _('Workflows Workshop'), {'sub': True}),
|
||||||
('users/', N_('Users'), {'check_display_function': roles.is_visible}),
|
('users/', _('Users'), {'check_display_function': roles.is_visible}),
|
||||||
('roles/', N_('Roles'), {'check_display_function': roles.is_visible}),
|
('roles/', _('Roles'), {'check_display_function': roles.is_visible}),
|
||||||
('settings/', N_('Settings')),
|
('settings/', _('Settings')),
|
||||||
]
|
]
|
||||||
|
|
||||||
def _q_traverse(self, path):
|
def _q_traverse(self, path):
|
||||||
|
|
|
@ -21,7 +21,7 @@ from quixote import get_publisher, get_request
|
||||||
from quixote.html import htmltag, htmltext
|
from quixote.html import htmltag, htmltext
|
||||||
|
|
||||||
from . import data_sources, fields
|
from . import data_sources, fields
|
||||||
from .qommon import N_, _, misc
|
from .qommon import _, misc
|
||||||
from .qommon.form import CompositeWidget, WidgetList
|
from .qommon.form import CompositeWidget, WidgetList
|
||||||
from .qommon.storage import StorableObject
|
from .qommon.storage import StorableObject
|
||||||
from .qommon.template import Template
|
from .qommon.template import Template
|
||||||
|
@ -136,7 +136,7 @@ class BlockDef(StorableObject):
|
||||||
unknown_datasources.add(data_source.get('type'))
|
unknown_datasources.add(data_source.get('type'))
|
||||||
if unknown_datasources:
|
if unknown_datasources:
|
||||||
raise BlockdefImportError(
|
raise BlockdefImportError(
|
||||||
N_('Unknown datasources'), details=', '.join(sorted(unknown_datasources))
|
_('Unknown datasources'), details=', '.join(sorted(unknown_datasources))
|
||||||
)
|
)
|
||||||
|
|
||||||
return blockdef
|
return blockdef
|
||||||
|
@ -146,7 +146,7 @@ class BlockDef(StorableObject):
|
||||||
charset = 'utf-8'
|
charset = 'utf-8'
|
||||||
blockdef = cls()
|
blockdef = cls()
|
||||||
if tree.find('name') is None or not tree.find('name').text:
|
if tree.find('name') is None or not tree.find('name').text:
|
||||||
raise BlockdefImportError(N_('Missing name'))
|
raise BlockdefImportError(_('Missing name'))
|
||||||
|
|
||||||
# if the tree we get is actually a ElementTree for real, we get its
|
# if the tree we get is actually a ElementTree for real, we get its
|
||||||
# root element and go on happily.
|
# root element and go on happily.
|
||||||
|
@ -154,7 +154,7 @@ class BlockDef(StorableObject):
|
||||||
tree = tree.getroot()
|
tree = tree.getroot()
|
||||||
|
|
||||||
if tree.tag != cls.xml_root_node:
|
if tree.tag != cls.xml_root_node:
|
||||||
raise BlockdefImportError(N_('Unexpected root node'))
|
raise BlockdefImportError(_('Unexpected root node'))
|
||||||
|
|
||||||
if include_id and tree.attrib.get('id'):
|
if include_id and tree.attrib.get('id'):
|
||||||
blockdef.id = tree.attrib.get('id')
|
blockdef.id = tree.attrib.get('id')
|
||||||
|
@ -169,7 +169,7 @@ class BlockDef(StorableObject):
|
||||||
try:
|
try:
|
||||||
field_o = fields.get_field_class_by_type(field.findtext('type'))()
|
field_o = fields.get_field_class_by_type(field.findtext('type'))()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise BlockdefImportError(N_('Unknown field type'), details=field.findtext('type'))
|
raise BlockdefImportError(_('Unknown field type'), details=field.findtext('type'))
|
||||||
field_o.init_with_xml(field, charset, include_id=True)
|
field_o.init_with_xml(field, charset, include_id=True)
|
||||||
blockdef.fields.append(field_o)
|
blockdef.fields.append(field_o)
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ from wcs.carddata import CardData
|
||||||
from wcs.categories import CardDefCategory
|
from wcs.categories import CardDefCategory
|
||||||
from wcs.formdef import FormDef, get_formdefs_of_all_kinds
|
from wcs.formdef import FormDef, get_formdefs_of_all_kinds
|
||||||
|
|
||||||
from .qommon import N_, _, misc
|
from .qommon import _, force_text, misc
|
||||||
from .qommon.storage import Equal, ILike, NotEqual
|
from .qommon.storage import Equal, ILike, NotEqual
|
||||||
from .qommon.template import Template
|
from .qommon.template import Template
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@ class CardDef(FormDef):
|
||||||
data_sql_prefix = 'carddata'
|
data_sql_prefix = 'carddata'
|
||||||
pickle_module_name = 'carddef'
|
pickle_module_name = 'carddef'
|
||||||
xml_root_node = 'carddef'
|
xml_root_node = 'carddef'
|
||||||
verbose_name = N_('Card model')
|
verbose_name = _('Card model')
|
||||||
verbose_name_plural = N_('Card models')
|
verbose_name_plural = _('Card models')
|
||||||
|
|
||||||
confirmation = False
|
confirmation = False
|
||||||
|
|
||||||
|
@ -93,19 +93,19 @@ class CardDef(FormDef):
|
||||||
from wcs.wf.remove import RemoveWorkflowStatusItem
|
from wcs.wf.remove import RemoveWorkflowStatusItem
|
||||||
from wcs.workflows import ChoiceWorkflowStatusItem, EditableWorkflowStatusItem, Workflow
|
from wcs.workflows import ChoiceWorkflowStatusItem, EditableWorkflowStatusItem, Workflow
|
||||||
|
|
||||||
workflow = Workflow(name=_('Default (cards)'))
|
workflow = Workflow(name=force_text(_('Default (cards)')))
|
||||||
workflow.id = '_carddef_default'
|
workflow.id = '_carddef_default'
|
||||||
workflow.roles = {
|
workflow.roles = {
|
||||||
'_viewer': _('Viewer'),
|
'_viewer': force_text(_('Viewer')),
|
||||||
'_editor': _('Editor'),
|
'_editor': force_text(_('Editor')),
|
||||||
}
|
}
|
||||||
status = workflow.add_status(_('Recorded'), 'recorded')
|
status = workflow.add_status(force_text(_('Recorded')), 'recorded')
|
||||||
deleted_status = workflow.add_status(_('Deleted'), 'deleted')
|
deleted_status = workflow.add_status(force_text(_('Deleted')), 'deleted')
|
||||||
|
|
||||||
editable = EditableWorkflowStatusItem()
|
editable = EditableWorkflowStatusItem()
|
||||||
editable.id = '_editable'
|
editable.id = '_editable'
|
||||||
editable.by = ['_editor']
|
editable.by = ['_editor']
|
||||||
editable.label = _('Edit Card')
|
editable.label = force_text(_('Edit Card'))
|
||||||
editable.status = status.id
|
editable.status = status.id
|
||||||
editable.parent = status
|
editable.parent = status
|
||||||
status.items.append(editable)
|
status.items.append(editable)
|
||||||
|
@ -113,7 +113,7 @@ class CardDef(FormDef):
|
||||||
action_delete = ChoiceWorkflowStatusItem()
|
action_delete = ChoiceWorkflowStatusItem()
|
||||||
action_delete.id = '_action_delete'
|
action_delete.id = '_action_delete'
|
||||||
action_delete.by = ['_editor']
|
action_delete.by = ['_editor']
|
||||||
action_delete.label = _('Delete Card')
|
action_delete.label = force_text(_('Delete Card'))
|
||||||
action_delete.status = deleted_status.id
|
action_delete.status = deleted_status.id
|
||||||
action_delete.require_confirmation = True
|
action_delete.require_confirmation = True
|
||||||
action_delete.parent = status
|
action_delete.parent = status
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
from quixote import get_publisher
|
from quixote import get_publisher
|
||||||
from quixote.html import htmltext
|
from quixote.html import htmltext
|
||||||
|
|
||||||
from .qommon import N_
|
from .qommon import _
|
||||||
from .qommon.misc import simplify
|
from .qommon.misc import simplify
|
||||||
from .qommon.storage import StorableObject
|
from .qommon.storage import StorableObject
|
||||||
from .qommon.substitution import Substitutions
|
from .qommon.substitution import Substitutions
|
||||||
|
@ -128,6 +128,6 @@ class CardDefCategory(Category):
|
||||||
XML_NODES = [('name', 'str'), ('url_name', 'str'), ('description', 'str'), ('position', 'int')]
|
XML_NODES = [('name', 'str'), ('url_name', 'str'), ('description', 'str'), ('position', 'int')]
|
||||||
|
|
||||||
|
|
||||||
Substitutions.register('category_name', category=N_('General'), comment=N_('Category Name'))
|
Substitutions.register('category_name', category=_('General'), comment=_('Category Name'))
|
||||||
Substitutions.register('category_description', category=N_('General'), comment=N_('Category Description'))
|
Substitutions.register('category_description', category=_('General'), comment=_('Category Description'))
|
||||||
Substitutions.register('category_id', category=N_('General'), comment=N_('Category Identifier'))
|
Substitutions.register('category_id', category=_('General'), comment=_('Category Identifier'))
|
||||||
|
|
|
@ -26,7 +26,7 @@ from quixote import get_publisher, get_request, get_session
|
||||||
from quixote.html import TemplateIO
|
from quixote.html import TemplateIO
|
||||||
|
|
||||||
from .api_utils import sign_url_auto_orig
|
from .api_utils import sign_url_auto_orig
|
||||||
from .qommon import N_, _, force_str, get_logger, misc
|
from .qommon import _, force_str, get_logger, misc
|
||||||
from .qommon.afterjobs import AfterJob
|
from .qommon.afterjobs import AfterJob
|
||||||
from .qommon.cron import CronJob
|
from .qommon.cron import CronJob
|
||||||
from .qommon.form import CompositeWidget, OptGroup, SingleSelectWidget, StringWidget
|
from .qommon.form import CompositeWidget, OptGroup, SingleSelectWidget, StringWidget
|
||||||
|
@ -916,7 +916,7 @@ def build_agenda_datasources(publisher):
|
||||||
|
|
||||||
|
|
||||||
class RefreshAgendas(AfterJob):
|
class RefreshAgendas(AfterJob):
|
||||||
label = N_('Refreshing agendas')
|
label = _('Refreshing agendas')
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
build_agenda_datasources(get_publisher())
|
build_agenda_datasources(get_publisher())
|
||||||
|
|
|
@ -201,7 +201,7 @@ class PrefillSelectionWidget(CompositeWidget):
|
||||||
attrs={
|
attrs={
|
||||||
'data-dynamic-display-child-of': 'prefill$type',
|
'data-dynamic-display-child-of': 'prefill$type',
|
||||||
'data-dynamic-display-value-in': '|'.join(
|
'data-dynamic-display-value-in': '|'.join(
|
||||||
[x[1] for x in options if x[0] not in ('none', 'geolocation')]
|
[str(x[1]) for x in options if x[0] not in ('none', 'geolocation')]
|
||||||
),
|
),
|
||||||
'inline_title': _('Locked'),
|
'inline_title': _('Locked'),
|
||||||
},
|
},
|
||||||
|
@ -268,7 +268,7 @@ class Field:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_type_label(self):
|
def get_type_label(self):
|
||||||
return _(self.description)
|
return self.description
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def include_in_listing(self):
|
def include_in_listing(self):
|
||||||
|
@ -822,7 +822,7 @@ def register_field_class(klass):
|
||||||
|
|
||||||
class TitleField(Field):
|
class TitleField(Field):
|
||||||
key = 'title'
|
key = 'title'
|
||||||
description = N_('Title')
|
description = _('Title')
|
||||||
html_tag = 'h3'
|
html_tag = 'h3'
|
||||||
display_locations = ['validation', 'summary']
|
display_locations = ['validation', 'summary']
|
||||||
|
|
||||||
|
@ -885,7 +885,7 @@ register_field_class(TitleField)
|
||||||
|
|
||||||
class SubtitleField(TitleField):
|
class SubtitleField(TitleField):
|
||||||
key = 'subtitle'
|
key = 'subtitle'
|
||||||
description = N_('Subtitle')
|
description = _('Subtitle')
|
||||||
html_tag = 'h4'
|
html_tag = 'h4'
|
||||||
|
|
||||||
|
|
||||||
|
@ -894,7 +894,7 @@ register_field_class(SubtitleField)
|
||||||
|
|
||||||
class CommentField(Field):
|
class CommentField(Field):
|
||||||
key = 'comment'
|
key = 'comment'
|
||||||
description = N_('Comment')
|
description = _('Comment')
|
||||||
display_locations = []
|
display_locations = []
|
||||||
|
|
||||||
def get_text(self):
|
def get_text(self):
|
||||||
|
@ -995,7 +995,7 @@ def is_datasource_advanced(value):
|
||||||
|
|
||||||
class StringField(WidgetField):
|
class StringField(WidgetField):
|
||||||
key = 'string'
|
key = 'string'
|
||||||
description = N_('Text (line)')
|
description = _('Text (line)')
|
||||||
|
|
||||||
widget_class = WcsExtraStringWidget
|
widget_class = WcsExtraStringWidget
|
||||||
size = None
|
size = None
|
||||||
|
@ -1091,7 +1091,7 @@ register_field_class(StringField)
|
||||||
|
|
||||||
class TextField(WidgetField):
|
class TextField(WidgetField):
|
||||||
key = 'text'
|
key = 'text'
|
||||||
description = N_('Long Text')
|
description = _('Long Text')
|
||||||
|
|
||||||
widget_class = TextWidget
|
widget_class = TextWidget
|
||||||
cols = None
|
cols = None
|
||||||
|
@ -1156,7 +1156,7 @@ register_field_class(TextField)
|
||||||
|
|
||||||
class EmailField(WidgetField):
|
class EmailField(WidgetField):
|
||||||
key = 'email'
|
key = 'email'
|
||||||
description = N_('Email')
|
description = _('Email')
|
||||||
|
|
||||||
widget_class = EmailWidget
|
widget_class = EmailWidget
|
||||||
|
|
||||||
|
@ -1181,7 +1181,7 @@ register_field_class(EmailField)
|
||||||
|
|
||||||
class BoolField(WidgetField):
|
class BoolField(WidgetField):
|
||||||
key = 'bool'
|
key = 'bool'
|
||||||
description = N_('Check Box (single choice)')
|
description = _('Check Box (single choice)')
|
||||||
allow_complex = True
|
allow_complex = True
|
||||||
|
|
||||||
widget_class = CheckboxWidget
|
widget_class = CheckboxWidget
|
||||||
|
@ -1202,9 +1202,9 @@ class BoolField(WidgetField):
|
||||||
|
|
||||||
def get_view_value(self, value, **kwargs):
|
def get_view_value(self, value, **kwargs):
|
||||||
if value is True or value == 'True':
|
if value is True or value == 'True':
|
||||||
return _('Yes')
|
return str(_('Yes'))
|
||||||
elif value is False or value == 'False':
|
elif value is False or value == 'False':
|
||||||
return _('No')
|
return str(_('No'))
|
||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
@ -1278,7 +1278,7 @@ register_field_class(BoolField)
|
||||||
|
|
||||||
class FileField(WidgetField):
|
class FileField(WidgetField):
|
||||||
key = 'file'
|
key = 'file'
|
||||||
description = N_('File Upload')
|
description = _('File Upload')
|
||||||
allow_complex = True
|
allow_complex = True
|
||||||
|
|
||||||
document_type = None
|
document_type = None
|
||||||
|
@ -1497,7 +1497,7 @@ class FileField(WidgetField):
|
||||||
# self.file_type is a combination of file type, we create a
|
# self.file_type is a combination of file type, we create a
|
||||||
# virtual one from them
|
# virtual one from them
|
||||||
if parts and len(parts) > 1:
|
if parts and len(parts) > 1:
|
||||||
label = ', '.join(parts)
|
label = ', '.join([str(x) for x in parts])
|
||||||
else:
|
else:
|
||||||
label = ','.join(file_type)
|
label = ','.join(file_type)
|
||||||
self.document_type = {
|
self.document_type = {
|
||||||
|
@ -1534,7 +1534,7 @@ register_field_class(FileField)
|
||||||
|
|
||||||
class DateField(WidgetField):
|
class DateField(WidgetField):
|
||||||
key = 'date'
|
key = 'date'
|
||||||
description = N_('Date')
|
description = _('Date')
|
||||||
|
|
||||||
widget_class = DateWidget
|
widget_class = DateWidget
|
||||||
minimum_date = None
|
minimum_date = None
|
||||||
|
@ -1807,7 +1807,7 @@ class ItemFieldMixin:
|
||||||
|
|
||||||
class ItemField(WidgetField, MapOptionsMixin, ItemFieldMixin):
|
class ItemField(WidgetField, MapOptionsMixin, ItemFieldMixin):
|
||||||
key = 'item'
|
key = 'item'
|
||||||
description = N_('List')
|
description = _('List')
|
||||||
allow_complex = True
|
allow_complex = True
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
|
@ -2163,7 +2163,7 @@ register_field_class(ItemField)
|
||||||
|
|
||||||
class ItemsField(WidgetField, ItemFieldMixin):
|
class ItemsField(WidgetField, ItemFieldMixin):
|
||||||
key = 'items'
|
key = 'items'
|
||||||
description = N_('Multiple choice list')
|
description = _('Multiple choice list')
|
||||||
allow_complex = True
|
allow_complex = True
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
|
@ -2501,7 +2501,7 @@ class PageCondition(Condition):
|
||||||
|
|
||||||
class PageField(Field):
|
class PageField(Field):
|
||||||
key = 'page'
|
key = 'page'
|
||||||
description = N_('Page')
|
description = _('Page')
|
||||||
|
|
||||||
post_conditions = None
|
post_conditions = None
|
||||||
|
|
||||||
|
@ -2595,7 +2595,7 @@ register_field_class(PageField)
|
||||||
|
|
||||||
class TableField(WidgetField):
|
class TableField(WidgetField):
|
||||||
key = 'table'
|
key = 'table'
|
||||||
description = N_('Table')
|
description = _('Table')
|
||||||
allow_complex = True
|
allow_complex = True
|
||||||
|
|
||||||
rows = None
|
rows = None
|
||||||
|
@ -2764,7 +2764,7 @@ register_field_class(TableField)
|
||||||
|
|
||||||
class TableSelectField(TableField):
|
class TableSelectField(TableField):
|
||||||
key = 'table-select'
|
key = 'table-select'
|
||||||
description = N_('Table of Lists')
|
description = _('Table of Lists')
|
||||||
allow_complex = True
|
allow_complex = True
|
||||||
|
|
||||||
items = None
|
items = None
|
||||||
|
@ -2813,7 +2813,7 @@ register_field_class(TableSelectField)
|
||||||
|
|
||||||
class TableRowsField(WidgetField):
|
class TableRowsField(WidgetField):
|
||||||
key = 'tablerows'
|
key = 'tablerows'
|
||||||
description = N_('Table with rows')
|
description = _('Table with rows')
|
||||||
allow_complex = True
|
allow_complex = True
|
||||||
|
|
||||||
total_row = True
|
total_row = True
|
||||||
|
@ -2956,7 +2956,7 @@ register_field_class(TableRowsField)
|
||||||
|
|
||||||
class MapField(WidgetField, MapOptionsMixin):
|
class MapField(WidgetField, MapOptionsMixin):
|
||||||
key = 'map'
|
key = 'map'
|
||||||
description = N_('Map')
|
description = _('Map')
|
||||||
|
|
||||||
default_position = None
|
default_position = None
|
||||||
init_with_geoloc = False
|
init_with_geoloc = False
|
||||||
|
@ -3054,7 +3054,7 @@ register_field_class(MapField)
|
||||||
|
|
||||||
class RankedItemsField(WidgetField):
|
class RankedItemsField(WidgetField):
|
||||||
key = 'ranked-items'
|
key = 'ranked-items'
|
||||||
description = N_('Ranked Items')
|
description = _('Ranked Items')
|
||||||
allow_complex = True
|
allow_complex = True
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
|
@ -3141,7 +3141,7 @@ register_field_class(RankedItemsField)
|
||||||
|
|
||||||
class PasswordField(WidgetField):
|
class PasswordField(WidgetField):
|
||||||
key = 'password'
|
key = 'password'
|
||||||
description = N_('Password')
|
description = _('Password')
|
||||||
|
|
||||||
min_length = 0
|
min_length = 0
|
||||||
max_length = 0
|
max_length = 0
|
||||||
|
@ -3465,13 +3465,13 @@ def get_field_options(blacklisted_types):
|
||||||
if klass.key in blacklisted_types:
|
if klass.key in blacklisted_types:
|
||||||
continue
|
continue
|
||||||
if issubclass(klass, WidgetField):
|
if issubclass(klass, WidgetField):
|
||||||
widgets.append((klass.key, _(klass.description), klass.key))
|
widgets.append((klass.key, klass.description, klass.key))
|
||||||
else:
|
else:
|
||||||
non_widgets.append((klass.key, _(klass.description), klass.key))
|
non_widgets.append((klass.key, klass.description, klass.key))
|
||||||
options = widgets + [('', '—', '')] + non_widgets
|
options = widgets + [('', '—', '')] + non_widgets
|
||||||
|
|
||||||
# add computed field in its own "section"
|
# add computed field in its own "section"
|
||||||
options.extend([('', '—', ''), (ComputedField.key, _(ComputedField.description), ComputedField.key)])
|
options.extend([('', '—', ''), (ComputedField.key, ComputedField.description, ComputedField.key)])
|
||||||
|
|
||||||
if get_publisher().has_site_option('fields-blocks') and (
|
if get_publisher().has_site_option('fields-blocks') and (
|
||||||
not blacklisted_types or 'blocks' not in blacklisted_types
|
not blacklisted_types or 'blocks' not in blacklisted_types
|
||||||
|
|
|
@ -25,7 +25,7 @@ import time
|
||||||
from quixote import get_publisher, get_request, get_session
|
from quixote import get_publisher, get_request, get_session
|
||||||
from quixote.http_request import Upload
|
from quixote.http_request import Upload
|
||||||
|
|
||||||
from .qommon import N_, _, misc
|
from .qommon import _, misc
|
||||||
from .qommon.evalutils import make_datetime
|
from .qommon.evalutils import make_datetime
|
||||||
from .qommon.publisher import get_cfg
|
from .qommon.publisher import get_cfg
|
||||||
from .qommon.storage import Contains, Intersects, Null, StorableObject
|
from .qommon.storage import Contains, Intersects, Null, StorableObject
|
||||||
|
@ -1421,14 +1421,14 @@ class FormData(StorableObject):
|
||||||
self.__dict__ = dict
|
self.__dict__ = dict
|
||||||
|
|
||||||
|
|
||||||
Substitutions.register('form_receipt_date', category=N_('Form'), comment=N_('Form Receipt Date'))
|
Substitutions.register('form_receipt_date', category=_('Form'), comment=_('Form Receipt Date'))
|
||||||
Substitutions.register('form_receipt_time', category=N_('Form'), comment=N_('Form Receipt Time'))
|
Substitutions.register('form_receipt_time', category=_('Form'), comment=_('Form Receipt Time'))
|
||||||
Substitutions.register('form_number', category=N_('Form'), comment=N_('Form Number'))
|
Substitutions.register('form_number', category=_('Form'), comment=_('Form Number'))
|
||||||
Substitutions.register('form_details', category=N_('Form'), comment=N_('Form Details'))
|
Substitutions.register('form_details', category=_('Form'), comment=_('Form Details'))
|
||||||
Substitutions.register('form_url', category=N_('Form'), comment=N_('Form URL'))
|
Substitutions.register('form_url', category=_('Form'), comment=_('Form URL'))
|
||||||
Substitutions.register('form_url_backoffice', category=N_('Form'), comment=N_('Form URL (backoffice)'))
|
Substitutions.register('form_url_backoffice', category=_('Form'), comment=_('Form URL (backoffice)'))
|
||||||
Substitutions.register('form_status_url', category=N_('Form'), comment=N_('Form Status URL'))
|
Substitutions.register('form_status_url', category=_('Form'), comment=_('Form Status URL'))
|
||||||
Substitutions.register('form_tracking_code', category=N_('Form'), comment=N_('Form Tracking Code'))
|
Substitutions.register('form_tracking_code', category=_('Form'), comment=_('Form Tracking Code'))
|
||||||
Substitutions.register('form_user_display_name', category=N_('Form'), comment=N_('Form Submitter Name'))
|
Substitutions.register('form_user_display_name', category=_('Form'), comment=_('Form Submitter Name'))
|
||||||
Substitutions.register('form_user_email', category=N_('Form'), comment=N_('Form Submitter Email'))
|
Substitutions.register('form_user_email', category=_('Form'), comment=_('Form Submitter Email'))
|
||||||
Substitutions.register_dynamic_source(FormData)
|
Substitutions.register_dynamic_source(FormData)
|
||||||
|
|
|
@ -35,7 +35,7 @@ from quixote.http_request import Upload
|
||||||
from . import data_sources, fields
|
from . import data_sources, fields
|
||||||
from .categories import Category
|
from .categories import Category
|
||||||
from .formdata import FormData
|
from .formdata import FormData
|
||||||
from .qommon import N_, PICKLE_KWARGS, _, force_str, get_cfg
|
from .qommon import PICKLE_KWARGS, _, force_str, get_cfg
|
||||||
from .qommon.admin.emails import EmailsDirectory
|
from .qommon.admin.emails import EmailsDirectory
|
||||||
from .qommon.cron import CronJob
|
from .qommon.cron import CronJob
|
||||||
from .qommon.form import Form, HtmlWidget, UploadedFile
|
from .qommon.form import Form, HtmlWidget, UploadedFile
|
||||||
|
@ -84,8 +84,8 @@ class FormDef(StorableObject):
|
||||||
data_sql_prefix = 'formdata'
|
data_sql_prefix = 'formdata'
|
||||||
pickle_module_name = 'formdef'
|
pickle_module_name = 'formdef'
|
||||||
xml_root_node = 'formdef'
|
xml_root_node = 'formdef'
|
||||||
verbose_name = N_('Form')
|
verbose_name = _('Form')
|
||||||
verbose_name_plural = N_('Forms')
|
verbose_name_plural = _('Forms')
|
||||||
|
|
||||||
name = None
|
name = None
|
||||||
description = None
|
description = None
|
||||||
|
@ -916,7 +916,7 @@ class FormDef(StorableObject):
|
||||||
try:
|
try:
|
||||||
field_o = fields.get_field_class_by_type(field.get('type'))()
|
field_o = fields.get_field_class_by_type(field.get('type'))()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise FormdefImportError(N_('Unknown field type'), details=field.findtext('type'))
|
raise FormdefImportError(_('Unknown field type'), details=field.findtext('type'))
|
||||||
field_o.init_with_json(field, include_id=True)
|
field_o.init_with_json(field, include_id=True)
|
||||||
if not field_o.id:
|
if not field_o.id:
|
||||||
# this assumes all fields will have id, or none of them
|
# this assumes all fields will have id, or none of them
|
||||||
|
@ -1100,7 +1100,7 @@ class FormDef(StorableObject):
|
||||||
known_field_ids = set()
|
known_field_ids = set()
|
||||||
for field in formdef.fields:
|
for field in formdef.fields:
|
||||||
if field.id in known_field_ids:
|
if field.id in known_field_ids:
|
||||||
raise FormdefImportRecoverableError(N_('Duplicated field identifiers'))
|
raise FormdefImportRecoverableError(_('Duplicated field identifiers'))
|
||||||
known_field_ids.add(field.id)
|
known_field_ids.add(field.id)
|
||||||
|
|
||||||
return formdef
|
return formdef
|
||||||
|
@ -1116,7 +1116,7 @@ class FormDef(StorableObject):
|
||||||
assert charset == 'utf-8'
|
assert charset == 'utf-8'
|
||||||
formdef = cls()
|
formdef = cls()
|
||||||
if tree.find('name') is None or not tree.find('name').text:
|
if tree.find('name') is None or not tree.find('name').text:
|
||||||
raise FormdefImportError(N_('Missing name'))
|
raise FormdefImportError(_('Missing name'))
|
||||||
|
|
||||||
# if the tree we get is actually a ElementTree for real, we get its
|
# if the tree we get is actually a ElementTree for real, we get its
|
||||||
# root element and go on happily.
|
# root element and go on happily.
|
||||||
|
@ -1124,7 +1124,7 @@ class FormDef(StorableObject):
|
||||||
tree = tree.getroot()
|
tree = tree.getroot()
|
||||||
|
|
||||||
if tree.tag != cls.xml_root_node:
|
if tree.tag != cls.xml_root_node:
|
||||||
raise FormdefImportError(N_('Unexpected root node'))
|
raise FormdefImportError(_('Unexpected root node'))
|
||||||
|
|
||||||
if include_id and tree.attrib.get('id'):
|
if include_id and tree.attrib.get('id'):
|
||||||
formdef.id = tree.attrib.get('id')
|
formdef.id = tree.attrib.get('id')
|
||||||
|
@ -1145,7 +1145,7 @@ class FormDef(StorableObject):
|
||||||
try:
|
try:
|
||||||
field_o = fields.get_field_class_by_type(field.findtext('type'))()
|
field_o = fields.get_field_class_by_type(field.findtext('type'))()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise FormdefImportError(N_('Unknown field type'), details=field.findtext('type'))
|
raise FormdefImportError(_('Unknown field type'), details=field.findtext('type'))
|
||||||
field_o.init_with_xml(field, charset, include_id=True)
|
field_o.init_with_xml(field, charset, include_id=True)
|
||||||
if fix_on_error or not field_o.id:
|
if fix_on_error or not field_o.id:
|
||||||
# this assumes all fields will have id, or none of them
|
# this assumes all fields will have id, or none of them
|
||||||
|
@ -1299,7 +1299,7 @@ class FormDef(StorableObject):
|
||||||
|
|
||||||
if unknown_datasources:
|
if unknown_datasources:
|
||||||
raise FormdefImportError(
|
raise FormdefImportError(
|
||||||
N_('Unknown datasources'), details=', '.join(sorted(unknown_datasources))
|
_('Unknown datasources'), details=', '.join(sorted(unknown_datasources))
|
||||||
)
|
)
|
||||||
|
|
||||||
return formdef
|
return formdef
|
||||||
|
@ -1340,7 +1340,7 @@ class FormDef(StorableObject):
|
||||||
else:
|
else:
|
||||||
details.append('%s' % field.get_rst_view_value(value, indent=' '))
|
details.append('%s' % field.get_rst_view_value(value, indent=' '))
|
||||||
details.append('')
|
details.append('')
|
||||||
return '\n'.join(details)
|
return '\n'.join([str(x) for x in details])
|
||||||
|
|
||||||
def get_submitter_email(self, formdata):
|
def get_submitter_email(self, formdata):
|
||||||
users_cfg = get_cfg('users', {})
|
users_cfg = get_cfg('users', {})
|
||||||
|
@ -1434,7 +1434,7 @@ class FormDef(StorableObject):
|
||||||
details.append(' %s' % formdata.get_status_label())
|
details.append(' %s' % formdata.get_status_label())
|
||||||
if evo.comment:
|
if evo.comment:
|
||||||
details.append('\n%s\n' % evo.comment)
|
details.append('\n%s\n' % evo.comment)
|
||||||
return '\n\n----\n\n' + '\n'.join(details)
|
return '\n\n----\n\n' + '\n'.join([str(x) for x in details])
|
||||||
|
|
||||||
def is_of_concern_for_role_id(self, role_id):
|
def is_of_concern_for_role_id(self, role_id):
|
||||||
if not self.workflow_roles:
|
if not self.workflow_roles:
|
||||||
|
@ -1618,11 +1618,11 @@ class FormDef(StorableObject):
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'new_user',
|
'new_user',
|
||||||
N_('Notification of creation to user'),
|
_('Notification of creation to user'),
|
||||||
enabled=False,
|
enabled=False,
|
||||||
category=N_('Workflow'),
|
category=_('Workflow'),
|
||||||
default_subject=N_('New form ({{ form_name }})'),
|
default_subject=_('New form ({{ form_name }})'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Hello,
|
Hello,
|
||||||
|
|
||||||
|
@ -1642,10 +1642,10 @@ For reference, here are the details:
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'change_user',
|
'change_user',
|
||||||
N_('Notification of change to user'),
|
_('Notification of change to user'),
|
||||||
category=N_('Workflow'),
|
category=_('Workflow'),
|
||||||
default_subject=N_('Form status change ({{ form_name }})'),
|
default_subject=_('Form status change ({{ form_name }})'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Hello,
|
Hello,
|
||||||
|
|
||||||
|
@ -1669,11 +1669,11 @@ You can consult it with this link: {{ form_url }}
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'new_receiver',
|
'new_receiver',
|
||||||
N_('Notification of creation to receiver'),
|
_('Notification of creation to receiver'),
|
||||||
enabled=False,
|
enabled=False,
|
||||||
category=N_('Workflow'),
|
category=_('Workflow'),
|
||||||
default_subject=N_('New form ({{ form_name }})'),
|
default_subject=_('New form ({{ form_name }})'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Hello,
|
Hello,
|
||||||
|
|
||||||
|
@ -1692,10 +1692,10 @@ For reference, here are the details:
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'change_receiver',
|
'change_receiver',
|
||||||
N_('Notification of change to receiver'),
|
_('Notification of change to receiver'),
|
||||||
category=N_('Workflow'),
|
category=_('Workflow'),
|
||||||
default_subject=N_('Form status change ({{ form_name }})'),
|
default_subject=_('Form status change ({{ form_name }})'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Hello,
|
Hello,
|
||||||
|
|
||||||
|
@ -1715,7 +1715,7 @@ Status of the form just changed (from "{{ form_previous_status }}" to "{{ form_s
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
Substitutions.register('form_name', category=N_('Form'), comment=N_('Form Name'))
|
Substitutions.register('form_name', category=_('Form'), comment=_('Form Name'))
|
||||||
|
|
||||||
|
|
||||||
def clean_drafts(publisher):
|
def clean_drafts(publisher):
|
||||||
|
|
|
@ -22,14 +22,14 @@ from wcs.formdef import FormDef
|
||||||
from wcs.forms.common import FormTemplateMixin
|
from wcs.forms.common import FormTemplateMixin
|
||||||
from wcs.wf.jump import jump_and_perform
|
from wcs.wf.jump import jump_and_perform
|
||||||
|
|
||||||
from ..qommon import N_, errors, misc, template, tokens
|
from ..qommon import _, errors, misc, template, tokens
|
||||||
from ..qommon.form import Form
|
from ..qommon.form import Form
|
||||||
|
|
||||||
|
|
||||||
class MissingOrExpiredToken(PublishError):
|
class MissingOrExpiredToken(PublishError):
|
||||||
status_code = 404
|
status_code = 404
|
||||||
title = N_('Error')
|
title = _('Error')
|
||||||
description = N_('This action link has already been used or has expired.')
|
description = _('This action link has already been used or has expired.')
|
||||||
|
|
||||||
|
|
||||||
class ActionsDirectory(Directory):
|
class ActionsDirectory(Directory):
|
||||||
|
|
|
@ -44,7 +44,7 @@ from wcs.roles import logged_users_role
|
||||||
from wcs.variables import LazyFormDef
|
from wcs.variables import LazyFormDef
|
||||||
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef, WorkflowStatusItem
|
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef, WorkflowStatusItem
|
||||||
|
|
||||||
from ..qommon import N_, _, emails, errors, get_cfg, get_logger, misc, template
|
from ..qommon import _, emails, errors, get_cfg, get_logger, misc, template
|
||||||
from ..qommon.admin.emails import EmailsDirectory
|
from ..qommon.admin.emails import EmailsDirectory
|
||||||
from ..qommon.form import CheckboxWidget, EmailWidget, Form, HiddenErrorWidget, HtmlWidget, StringWidget
|
from ..qommon.form import CheckboxWidget, EmailWidget, Form, HiddenErrorWidget, HtmlWidget, StringWidget
|
||||||
from ..qommon.template import TemplateError
|
from ..qommon.template import TemplateError
|
||||||
|
@ -1962,14 +1962,14 @@ class PublicFormStatusPage(FormStatusPage):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
TextsDirectory.register('welcome-logged', N_('Welcome text on home page for logged users'))
|
TextsDirectory.register('welcome-logged', _('Welcome text on home page for logged users'))
|
||||||
|
|
||||||
TextsDirectory.register('welcome-unlogged', N_('Welcome text on home page for unlogged users'))
|
TextsDirectory.register('welcome-unlogged', _('Welcome text on home page for unlogged users'))
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'captcha-page',
|
'captcha-page',
|
||||||
N_('Explanation text before the CAPTCHA'),
|
_('Explanation text before the CAPTCHA'),
|
||||||
default=N_(
|
default=_(
|
||||||
'''<h3>Verification</h3>
|
'''<h3>Verification</h3>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -1981,44 +1981,44 @@ In order to submit the form you need to complete this simple question.
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'form-recorded',
|
'form-recorded',
|
||||||
N_('Message when a form has been recorded'),
|
_('Message when a form has been recorded'),
|
||||||
category=N_('Forms'),
|
category=_('Forms'),
|
||||||
default=N_(
|
default=_('The form has been recorded on {{ form_receipt_datetime }} with the number {{ form_number }}.'),
|
||||||
'The form has been recorded on {{ form_receipt_datetime }} with the number {{ form_number }}.'
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'form-recorded-allow-one',
|
'form-recorded-allow-one',
|
||||||
N_('Message when a form has been recorded, and the form is set to only allow one per user'),
|
_('Message when a form has been recorded, and the form is set to only allow one per user'),
|
||||||
category=N_('Forms'),
|
category=_('Forms'),
|
||||||
default=N_('The form has been recorded on {{ form_receipt_datetime }}.'),
|
default=_('The form has been recorded on {{ form_receipt_datetime }}.'),
|
||||||
)
|
)
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'check-before-submit',
|
'check-before-submit',
|
||||||
N_('Message when a form is displayed before validation'),
|
_('Message when a form is displayed before validation'),
|
||||||
category=N_('Forms'),
|
category=_('Forms'),
|
||||||
default=N_('Check values then click submit.'),
|
default=_('Check values then click submit.'),
|
||||||
)
|
)
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'tracking-code-email-dialog',
|
'tracking-code-email-dialog',
|
||||||
N_('Message in tracking code popup dialog'),
|
_('Message in tracking code popup dialog'),
|
||||||
category=N_('Forms'),
|
category=_('Forms'),
|
||||||
default=N_('You can get a reminder of the tracking code by email.'),
|
default=_('You can get a reminder of the tracking code by email.'),
|
||||||
)
|
)
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'tracking-code-short-text', N_('Short text in the tracking code box'), category=N_('Forms')
|
'tracking-code-short-text',
|
||||||
|
_('Short text in the tracking code box'),
|
||||||
|
category=_('Forms'),
|
||||||
)
|
)
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'tracking-code-reminder',
|
'tracking-code-reminder',
|
||||||
N_('Tracking Code'),
|
_('Tracking Code'),
|
||||||
category=N_('Miscellaneous'),
|
category=_('Miscellaneous'),
|
||||||
default_subject=N_('Tracking Code reminder'),
|
default_subject=_('Tracking Code reminder'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Hello,
|
Hello,
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ class LoggedError:
|
||||||
exception=None,
|
exception=None,
|
||||||
):
|
):
|
||||||
error = cls()
|
error = cls()
|
||||||
error.summary = error_summary
|
error.summary = str(error_summary)
|
||||||
error.traceback = plain_error_msg
|
error.traceback = plain_error_msg
|
||||||
error.expression = expression
|
error.expression = expression
|
||||||
error.expression_type = expression_type
|
error.expression_type = expression_type
|
||||||
|
|
|
@ -25,7 +25,7 @@ from quixote.html import TemplateIO, htmltext
|
||||||
|
|
||||||
from wcs.api_utils import get_secret_and_orig, sign_url
|
from wcs.api_utils import get_secret_and_orig, sign_url
|
||||||
|
|
||||||
from .qommon import N_, get_logger
|
from .qommon import _, get_logger
|
||||||
from .qommon.misc import http_post_request, json_loads, urlopen
|
from .qommon.misc import http_post_request, json_loads, urlopen
|
||||||
|
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ def push_document(user, filename, stream):
|
||||||
|
|
||||||
if get_response():
|
if get_response():
|
||||||
get_response().add_after_job(
|
get_response().add_after_job(
|
||||||
N_('Sending file %(filename)s in portfolio of %(user_name)s')
|
_('Sending file %(filename)s in portfolio of %(user_name)s')
|
||||||
% {'filename': filename, 'user_name': user.display_name},
|
% {'filename': filename, 'user_name': user.display_name},
|
||||||
afterjob,
|
afterjob,
|
||||||
)
|
)
|
||||||
|
|
|
@ -21,6 +21,7 @@ import threading
|
||||||
import django.apps
|
import django.apps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
|
from django.utils.functional import lazy
|
||||||
from quixote import get_publisher
|
from quixote import get_publisher
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -37,7 +38,7 @@ force_str = force_text
|
||||||
PICKLE_KWARGS = {'encoding': 'bytes', 'fix_imports': True}
|
PICKLE_KWARGS = {'encoding': 'bytes', 'fix_imports': True}
|
||||||
|
|
||||||
|
|
||||||
def _(message):
|
def gettext(message):
|
||||||
pub = get_publisher()
|
pub = get_publisher()
|
||||||
if pub is None:
|
if pub is None:
|
||||||
return message
|
return message
|
||||||
|
@ -55,6 +56,8 @@ def N_(x):
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
_ = lazy(gettext, str)
|
||||||
|
|
||||||
from . import publisher # noqa pylint: disable=wrong-import-position
|
from . import publisher # noqa pylint: disable=wrong-import-position
|
||||||
from .publisher import get_cfg # noqa pylint: disable=wrong-import-position
|
from .publisher import get_cfg # noqa pylint: disable=wrong-import-position
|
||||||
from .publisher import get_logger # noqa pylint: disable=wrong-import-position
|
from .publisher import get_logger # noqa pylint: disable=wrong-import-position
|
||||||
|
|
|
@ -61,9 +61,7 @@ class EmailsDirectory(Directory):
|
||||||
emails_cfg = get_cfg('emails', {})
|
emails_cfg = get_cfg('emails', {})
|
||||||
cfg_key = 'email-%s' % email_key
|
cfg_key = 'email-%s' % email_key
|
||||||
default_subject = cls.emails_dict[email_key].get('default_subject')
|
default_subject = cls.emails_dict[email_key].get('default_subject')
|
||||||
if default_subject:
|
real_subject = emails_cfg.get(cfg_key + '_subject') or str(default_subject)
|
||||||
default_subject = _(default_subject)
|
|
||||||
real_subject = emails_cfg.get(cfg_key + '_subject') or default_subject
|
|
||||||
return real_subject
|
return real_subject
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -71,9 +69,7 @@ class EmailsDirectory(Directory):
|
||||||
emails_cfg = get_cfg('emails', {})
|
emails_cfg = get_cfg('emails', {})
|
||||||
cfg_key = 'email-%s' % email_key
|
cfg_key = 'email-%s' % email_key
|
||||||
default_body = cls.emails_dict[email_key].get('default_body')
|
default_body = cls.emails_dict[email_key].get('default_body')
|
||||||
if default_body:
|
real_body = emails_cfg.get(cfg_key) or str(default_body)
|
||||||
default_body = _(default_body)
|
|
||||||
real_body = emails_cfg.get(cfg_key) or default_body
|
|
||||||
return real_body
|
return real_body
|
||||||
|
|
||||||
def options(self):
|
def options(self):
|
||||||
|
@ -193,7 +189,7 @@ class EmailsDirectory(Directory):
|
||||||
categories = {}
|
categories = {}
|
||||||
for k, v in self.emails_dict.items():
|
for k, v in self.emails_dict.items():
|
||||||
if v.get('category'):
|
if v.get('category'):
|
||||||
translated_category = _(v.get('category'))
|
translated_category = v.get('category')
|
||||||
else:
|
else:
|
||||||
translated_category = _('Miscellaneous')
|
translated_category = _('Miscellaneous')
|
||||||
if translated_category not in categories:
|
if translated_category not in categories:
|
||||||
|
@ -205,11 +201,11 @@ class EmailsDirectory(Directory):
|
||||||
r += htmltext('<h3>%s</h3>') % category_key
|
r += htmltext('<h3>%s</h3>') % category_key
|
||||||
|
|
||||||
keys = categories.get(category_key)
|
keys = categories.get(category_key)
|
||||||
keys.sort(key=lambda x: _(self.emails_dict[x]['description']))
|
keys.sort(key=lambda x: self.emails_dict[x]['description'])
|
||||||
r += htmltext('<ul>')
|
r += htmltext('<ul>')
|
||||||
for email_key in keys:
|
for email_key in keys:
|
||||||
email_values = self.emails_dict[email_key]
|
email_values = self.emails_dict[email_key]
|
||||||
r += htmltext('<li><a href="%s">%s</a></li>') % (email_key, _(email_values['description']))
|
r += htmltext('<li><a href="%s">%s</a></li>') % (email_key, email_values['description'])
|
||||||
r += htmltext('</ul>')
|
r += htmltext('</ul>')
|
||||||
|
|
||||||
r += htmltext('<p>')
|
r += htmltext('<p>')
|
||||||
|
@ -221,8 +217,8 @@ class EmailsDirectory(Directory):
|
||||||
emails_cfg = get_cfg('emails', {})
|
emails_cfg = get_cfg('emails', {})
|
||||||
cfg_key = 'email-%s' % email_key
|
cfg_key = 'email-%s' % email_key
|
||||||
|
|
||||||
default_subject = _(self.emails_dict[email_key].get('default_subject'))
|
default_subject = self.emails_dict[email_key].get('default_subject')
|
||||||
default_body = _(self.emails_dict[email_key].get('default_body'))
|
default_body = self.emails_dict[email_key].get('default_body')
|
||||||
|
|
||||||
displayed_subject = emails_cfg.get(cfg_key + '_subject') or default_subject
|
displayed_subject = emails_cfg.get(cfg_key + '_subject') or default_subject
|
||||||
displayed_body = emails_cfg.get(cfg_key) or default_body
|
displayed_body = emails_cfg.get(cfg_key) or default_body
|
||||||
|
@ -273,8 +269,8 @@ class EmailsDirectory(Directory):
|
||||||
if check_template and not check_template(template):
|
if check_template and not check_template(template):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
default_subject = _(self.emails_dict[email_key].get('default_subject'))
|
default_subject = self.emails_dict[email_key].get('default_subject')
|
||||||
default_body = _(self.emails_dict[email_key].get('default_body'))
|
default_body = self.emails_dict[email_key].get('default_body')
|
||||||
|
|
||||||
if template == default_body:
|
if template == default_body:
|
||||||
template = None
|
template = None
|
||||||
|
@ -298,8 +294,8 @@ class EmailsDirectory(Directory):
|
||||||
|
|
||||||
return self.email(
|
return self.email(
|
||||||
component,
|
component,
|
||||||
_(self.emails_dict[component]['description']),
|
self.emails_dict[component]['description'],
|
||||||
_(self.emails_dict[component]['hint']),
|
self.emails_dict[component]['hint'],
|
||||||
self.emails_dict[component]['enabled'],
|
self.emails_dict[component]['enabled'],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ import re
|
||||||
from quixote import get_publisher, get_request
|
from quixote import get_publisher, get_request
|
||||||
from quixote.html import TemplateIO, htmltext
|
from quixote.html import TemplateIO, htmltext
|
||||||
|
|
||||||
from .. import N_, _
|
from .. import _
|
||||||
from ..backoffice.menu import html_top
|
from ..backoffice.menu import html_top
|
||||||
|
|
||||||
|
|
||||||
|
@ -113,16 +113,16 @@ def get_vc_version():
|
||||||
|
|
||||||
def command_icon(url, type, label=None, popup=False):
|
def command_icon(url, type, label=None, popup=False):
|
||||||
labels = {
|
labels = {
|
||||||
'edit': N_('Edit'),
|
'edit': _('Edit'),
|
||||||
'remove': N_('Remove'),
|
'remove': _('Remove'),
|
||||||
'duplicate': N_('Duplicate'),
|
'duplicate': _('Duplicate'),
|
||||||
'view': N_('View'),
|
'view': _('View'),
|
||||||
}
|
}
|
||||||
if label:
|
if label:
|
||||||
klass = 'button'
|
klass = 'button'
|
||||||
else:
|
else:
|
||||||
klass = ''
|
klass = ''
|
||||||
label = _(labels[type])
|
label = labels[type]
|
||||||
root_url = get_publisher().get_application_static_files_root_url()
|
root_url = get_publisher().get_application_static_files_root_url()
|
||||||
rel = ''
|
rel = ''
|
||||||
if popup:
|
if popup:
|
||||||
|
|
|
@ -41,7 +41,7 @@ class TextsDirectory(Directory):
|
||||||
if os.path.exists(filepath):
|
if os.path.exists(filepath):
|
||||||
return htmltext(open(filepath).read())
|
return htmltext(open(filepath).read())
|
||||||
return ''
|
return ''
|
||||||
text = _(default)
|
text = str(default) # make sure translation is applied
|
||||||
|
|
||||||
if not text.startswith('<'):
|
if not text.startswith('<'):
|
||||||
text = '<p>%s</p>' % text
|
text = '<p>%s</p>' % text
|
||||||
|
@ -79,7 +79,7 @@ class TextsDirectory(Directory):
|
||||||
categories = {}
|
categories = {}
|
||||||
for k, v in self.texts_dict.items():
|
for k, v in self.texts_dict.items():
|
||||||
if v.get('category'):
|
if v.get('category'):
|
||||||
translated_category = _(v.get('category'))
|
translated_category = v.get('category')
|
||||||
else:
|
else:
|
||||||
translated_category = _('Miscellaneous')
|
translated_category = _('Miscellaneous')
|
||||||
if translated_category not in categories:
|
if translated_category not in categories:
|
||||||
|
@ -91,11 +91,11 @@ class TextsDirectory(Directory):
|
||||||
r += htmltext('<h3>%s</h3>') % category_key
|
r += htmltext('<h3>%s</h3>') % category_key
|
||||||
|
|
||||||
keys = categories.get(category_key)
|
keys = categories.get(category_key)
|
||||||
keys.sort(key=lambda x: _(self.texts_dict[x]['description']))
|
keys.sort(key=lambda x: self.texts_dict[x]['description'])
|
||||||
r += htmltext('<ul>')
|
r += htmltext('<ul>')
|
||||||
for text_key in keys:
|
for text_key in keys:
|
||||||
text_values = self.texts_dict[text_key]
|
text_values = self.texts_dict[text_key]
|
||||||
r += htmltext('<li><a href="%s">%s</a></li>') % (text_key, _(text_values['description']))
|
r += htmltext('<li><a href="%s">%s</a></li>') % (text_key, text_values['description'])
|
||||||
r += htmltext('</ul>')
|
r += htmltext('</ul>')
|
||||||
|
|
||||||
r += htmltext('<p>')
|
r += htmltext('<p>')
|
||||||
|
@ -108,9 +108,7 @@ class TextsDirectory(Directory):
|
||||||
cfg_key = 'text-%s' % text_key
|
cfg_key = 'text-%s' % text_key
|
||||||
|
|
||||||
default_text = self.texts_dict.get(text_key, {}).get('default')
|
default_text = self.texts_dict.get(text_key, {}).get('default')
|
||||||
if default_text:
|
if not default_text:
|
||||||
default_text = _(default_text)
|
|
||||||
else:
|
|
||||||
filepath = os.path.join(get_publisher().DATA_DIR, 'texts', '%s.html' % text_key)
|
filepath = os.path.join(get_publisher().DATA_DIR, 'texts', '%s.html' % text_key)
|
||||||
if os.path.exists(str(filepath)):
|
if os.path.exists(str(filepath)):
|
||||||
default_text = open(str(filepath)).read()
|
default_text = open(str(filepath)).read()
|
||||||
|
@ -176,7 +174,7 @@ class TextsDirectory(Directory):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
hint = self.texts_dict[component]['hint']
|
hint = self.texts_dict[component]['hint']
|
||||||
return self.text(component, _(self.texts_dict[component]['description']), hint and _(hint))
|
return self.text(component, self.texts_dict[component]['description'], hint)
|
||||||
|
|
||||||
def _q_traverse(self, path):
|
def _q_traverse(self, path):
|
||||||
get_response().breadcrumb.append(('texts/', _('Texts')))
|
get_response().breadcrumb.append(('texts/', _('Texts')))
|
||||||
|
|
|
@ -22,7 +22,7 @@ import uuid
|
||||||
from quixote import get_publisher, get_response
|
from quixote import get_publisher, get_response
|
||||||
from quixote.directory import Directory
|
from quixote.directory import Directory
|
||||||
|
|
||||||
from . import N_, _, errors
|
from . import N_, _, errors, force_text
|
||||||
from .storage import StorableObject
|
from .storage import StorableObject
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@ class AfterJobStatusDirectory(Directory):
|
||||||
response = get_response()
|
response = get_response()
|
||||||
response.set_content_type('text/plain')
|
response.set_content_type('text/plain')
|
||||||
if not job.completion_status:
|
if not job.completion_status:
|
||||||
return job.status + '|' + _(job.status)
|
return job.status + '|' + str(_(job.status))
|
||||||
return job.status + '|' + _(job.status) + ' ' + job.completion_status
|
return job.status + '|' + str(_(job.status)) + ' ' + job.completion_status
|
||||||
|
|
||||||
|
|
||||||
class AfterJob(StorableObject):
|
class AfterJob(StorableObject):
|
||||||
|
@ -55,12 +55,24 @@ class AfterJob(StorableObject):
|
||||||
def __init__(self, label=None, cmd=None, **kwargs):
|
def __init__(self, label=None, cmd=None, **kwargs):
|
||||||
super().__init__(id=str(uuid.uuid4()))
|
super().__init__(id=str(uuid.uuid4()))
|
||||||
if label:
|
if label:
|
||||||
self.label = label
|
self.label = force_text(label)
|
||||||
|
self.done_action_label_arg = kwargs.pop('done_action_label', None)
|
||||||
|
self.done_action_url_arg = kwargs.pop('done_action_url', None)
|
||||||
|
self.done_button_attributes_arg = kwargs.pop('done_button_attributes', None)
|
||||||
self.creation_time = time.time()
|
self.creation_time = time.time()
|
||||||
self.job_cmd = cmd
|
self.job_cmd = cmd
|
||||||
self.status = N_('registered')
|
self.status = N_('registered')
|
||||||
self.kwargs = kwargs
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
def done_action_label(self):
|
||||||
|
return self.done_action_label_arg
|
||||||
|
|
||||||
|
def done_action_url(self):
|
||||||
|
return self.done_action_url_arg % {'job_id': self.id}
|
||||||
|
|
||||||
|
def done_action_attributes(self):
|
||||||
|
return self.done_button_attributes_arg
|
||||||
|
|
||||||
def run(self, spool=False):
|
def run(self, spool=False):
|
||||||
if self.completion_time:
|
if self.completion_time:
|
||||||
return
|
return
|
||||||
|
@ -94,6 +106,8 @@ class AfterJob(StorableObject):
|
||||||
self.store()
|
self.store()
|
||||||
|
|
||||||
def __getstate__(self):
|
def __getstate__(self):
|
||||||
|
if getattr(self, 'done_action_label_arg', None):
|
||||||
|
self.done_action_label_arg = force_text(self.done_action_label_arg)
|
||||||
if not isinstance(self.job_cmd, str):
|
if not isinstance(self.job_cmd, str):
|
||||||
obj_dict = self.__dict__.copy()
|
obj_dict = self.__dict__.copy()
|
||||||
obj_dict['job_cmd'] = None
|
obj_dict['job_cmd'] = None
|
||||||
|
|
|
@ -21,7 +21,7 @@ from quixote import get_publisher
|
||||||
from quixote.errors import AccessError, TraversalError
|
from quixote.errors import AccessError, TraversalError
|
||||||
from quixote.html import TemplateIO, htmltext
|
from quixote.html import TemplateIO, htmltext
|
||||||
|
|
||||||
from . import N_, template
|
from . import _, template
|
||||||
|
|
||||||
|
|
||||||
class AccessForbiddenError(AccessError):
|
class AccessForbiddenError(AccessError):
|
||||||
|
@ -59,7 +59,7 @@ class AccessUnauthorizedError(AccessForbiddenError):
|
||||||
return AccessForbiddenError.render(self)
|
return AccessForbiddenError.render(self)
|
||||||
|
|
||||||
if self.public_msg:
|
if self.public_msg:
|
||||||
session.message = ('error', self.public_msg)
|
session.message = ('error', str(self.public_msg))
|
||||||
login_url = get_publisher().get_root_url() + 'login/'
|
login_url = get_publisher().get_root_url() + 'login/'
|
||||||
login_url += '?' + urllib.parse.urlencode({'next': request.get_frontoffice_url()})
|
login_url += '?' + urllib.parse.urlencode({'next': request.get_frontoffice_url()})
|
||||||
return quixote.redirect(login_url)
|
return quixote.redirect(login_url)
|
||||||
|
@ -132,8 +132,8 @@ class SMSError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
TraversalError.title = N_('Page not found')
|
TraversalError.title = _('Page not found')
|
||||||
TraversalError.description = N_(
|
TraversalError.description = _(
|
||||||
"The requested link does not exist on this site. If "
|
"The requested link does not exist on this site. If "
|
||||||
"you arrived here by following a link from an external "
|
"you arrived here by following a link from an external "
|
||||||
"page, please inform that page's maintainer."
|
"page, please inform that page's maintainer."
|
||||||
|
@ -141,9 +141,7 @@ TraversalError.description = N_(
|
||||||
|
|
||||||
|
|
||||||
def format_publish_error(exc):
|
def format_publish_error(exc):
|
||||||
from . import _
|
|
||||||
|
|
||||||
if getattr(exc, 'public_msg', None):
|
if getattr(exc, 'public_msg', None):
|
||||||
return template.error_page(exc.format(), _(exc.title))
|
return template.error_page(exc.format(), exc.title)
|
||||||
else:
|
else:
|
||||||
return template.error_page(_(exc.description), _(exc.title))
|
return template.error_page(exc.description, exc.title)
|
||||||
|
|
|
@ -69,7 +69,7 @@ from quixote.html import TemplateIO, htmlescape, htmltag, htmltext, stringify
|
||||||
from wcs.conditions import Condition, ValidationError
|
from wcs.conditions import Condition, ValidationError
|
||||||
|
|
||||||
from ..portfolio import has_portfolio
|
from ..portfolio import has_portfolio
|
||||||
from . import N_, _, force_str, misc, ngettext
|
from . import _, force_str, misc, ngettext
|
||||||
from .humantime import humanduration2seconds, seconds2humanduration, timewords
|
from .humantime import humanduration2seconds, seconds2humanduration, timewords
|
||||||
from .misc import HAS_PDFTOPPM, json_loads, strftime
|
from .misc import HAS_PDFTOPPM, json_loads, strftime
|
||||||
from .publisher import get_cfg
|
from .publisher import get_cfg
|
||||||
|
@ -79,17 +79,10 @@ from .template_utils import render_block_to_string
|
||||||
from .upload_storage import PicklableUpload # noqa pylint: disable=unused-import
|
from .upload_storage import PicklableUpload # noqa pylint: disable=unused-import
|
||||||
from .upload_storage import UploadStorageError
|
from .upload_storage import UploadStorageError
|
||||||
|
|
||||||
Widget.REQUIRED_ERROR = N_('required field')
|
Widget.REQUIRED_ERROR = _('required field')
|
||||||
get_error_orig = Widget.get_error
|
get_error_orig = Widget.get_error
|
||||||
|
|
||||||
|
|
||||||
def get_i18n_error(self, request=None):
|
|
||||||
error = get_error_orig(self, request)
|
|
||||||
if error == Widget.REQUIRED_ERROR:
|
|
||||||
return _(error)
|
|
||||||
return error
|
|
||||||
|
|
||||||
|
|
||||||
def is_prefilled(self):
|
def is_prefilled(self):
|
||||||
if hasattr(self, 'prefilled'):
|
if hasattr(self, 'prefilled'):
|
||||||
return self.prefilled
|
return self.prefilled
|
||||||
|
@ -99,6 +92,7 @@ def is_prefilled(self):
|
||||||
|
|
||||||
def render_title(self, title):
|
def render_title(self, title):
|
||||||
if title:
|
if title:
|
||||||
|
title = str(title) # apply translation if required
|
||||||
if self.required:
|
if self.required:
|
||||||
title += htmltext('<span title="%s" class="required">*</span>') % _('This field is required.')
|
title += htmltext('<span title="%s" class="required">*</span>') % _('This field is required.')
|
||||||
attrs = {
|
attrs = {
|
||||||
|
@ -178,7 +172,6 @@ def render_widget_content(self):
|
||||||
return htmltext(force_str(render_block_to_string(template_names, 'widget-content', context)))
|
return htmltext(force_str(render_block_to_string(template_names, 'widget-content', context)))
|
||||||
|
|
||||||
|
|
||||||
Widget.get_error = get_i18n_error
|
|
||||||
Widget.render = render
|
Widget.render = render
|
||||||
Widget.cleanup = None
|
Widget.cleanup = None
|
||||||
Widget.render_error = render_error
|
Widget.render_error = render_error
|
||||||
|
@ -211,7 +204,9 @@ class SubmitWidget(quixote.form.widget.SubmitWidget):
|
||||||
self.attrs['formnovalidate'] = 'formnovalidate'
|
self.attrs['formnovalidate'] = 'formnovalidate'
|
||||||
value = htmlescape(self.label) if self.label else None
|
value = htmlescape(self.label) if self.label else None
|
||||||
return (
|
return (
|
||||||
htmltag('button', name=self.name, value=value, **self.attrs) + self.label + htmltext('</button>')
|
htmltag('button', name=self.name, value=value, **self.attrs)
|
||||||
|
+ str(self.label)
|
||||||
|
+ htmltext('</button>')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -254,13 +249,13 @@ Widget.transfer_form_value = transfer_form_value
|
||||||
|
|
||||||
|
|
||||||
class Form(QuixoteForm):
|
class Form(QuixoteForm):
|
||||||
TOKEN_NOTICE = N_(
|
TOKEN_NOTICE = _(
|
||||||
"The form you have submitted is invalid. Most "
|
"The form you have submitted is invalid. Most "
|
||||||
"likely it has been successfully submitted once "
|
"likely it has been successfully submitted once "
|
||||||
"already. Please review the form data "
|
"already. Please review the form data "
|
||||||
"and submit the form again."
|
"and submit the form again."
|
||||||
)
|
)
|
||||||
ERROR_NOTICE = N_("There were errors processing your form. See below for details.")
|
ERROR_NOTICE = _("There were errors processing your form. See below for details.")
|
||||||
|
|
||||||
info = None
|
info = None
|
||||||
captcha = None
|
captcha = None
|
||||||
|
@ -295,6 +290,9 @@ class Form(QuixoteForm):
|
||||||
widget.advanced = advanced
|
widget.advanced = advanced
|
||||||
return widget
|
return widget
|
||||||
|
|
||||||
|
def set_error(self, name, error):
|
||||||
|
super().set_error(name, force_text(error))
|
||||||
|
|
||||||
def remove(self, name):
|
def remove(self, name):
|
||||||
widget = self._names.get(name)
|
widget = self._names.get(name)
|
||||||
if widget:
|
if widget:
|
||||||
|
@ -344,13 +342,13 @@ class Form(QuixoteForm):
|
||||||
return r.getvalue()
|
return r.getvalue()
|
||||||
|
|
||||||
def _render_info_notice(self):
|
def _render_info_notice(self):
|
||||||
return htmltext('<div class="infonotice">%s</div>' % _(self.info))
|
return htmltext('<div class="infonotice">%s</div>' % self.info)
|
||||||
|
|
||||||
def _render_error_notice(self):
|
def _render_error_notice(self):
|
||||||
errors = []
|
errors = []
|
||||||
classnames = ['errornotice']
|
classnames = ['errornotice']
|
||||||
if self.has_errors():
|
if self.has_errors():
|
||||||
errors.append(_(QuixoteForm._render_error_notice(self)))
|
errors.append(QuixoteForm._render_error_notice(self))
|
||||||
if self.global_error_messages:
|
if self.global_error_messages:
|
||||||
errors.extend(self.global_error_messages)
|
errors.extend(self.global_error_messages)
|
||||||
classnames.append('global-errors')
|
classnames.append('global-errors')
|
||||||
|
@ -531,7 +529,7 @@ class DurationWidget(StringWidget):
|
||||||
if value:
|
if value:
|
||||||
value = seconds2humanduration(int(value))
|
value = seconds2humanduration(int(value))
|
||||||
if 'hint' in kwargs:
|
if 'hint' in kwargs:
|
||||||
kwargs['hint'] += htmltext('<br>')
|
kwargs['hint'] = str(kwargs['hint']) + htmltext('<br>')
|
||||||
else:
|
else:
|
||||||
kwargs['hint'] = ''
|
kwargs['hint'] = ''
|
||||||
kwargs['hint'] += htmltext(_('Usable units of time: %s.')) % ', '.join(timewords())
|
kwargs['hint'] += htmltext(_('Usable units of time: %s.')) % ', '.join(timewords())
|
||||||
|
@ -629,7 +627,7 @@ class CheckboxWidget(QuixoteCheckboxWidget):
|
||||||
# custom style.
|
# custom style.
|
||||||
return (
|
return (
|
||||||
htmltext('<label %s>%s<span>' % (data_attrs, checkbox))
|
htmltext('<label %s>%s<span>' % (data_attrs, checkbox))
|
||||||
+ inline_title
|
+ str(inline_title)
|
||||||
+ htmltext('</span></label>')
|
+ htmltext('</span></label>')
|
||||||
)
|
)
|
||||||
return checkbox
|
return checkbox
|
||||||
|
@ -1040,64 +1038,75 @@ class ValidationWidget(CompositeWidget):
|
||||||
(
|
(
|
||||||
'digits',
|
'digits',
|
||||||
{
|
{
|
||||||
'title': N_('Digits'),
|
'title': _('Digits'),
|
||||||
'regex': r'\d+',
|
'regex': r'\d+',
|
||||||
'error_message': N_('Only digits are allowed'),
|
'error_message': _('Only digits are allowed'),
|
||||||
'html_inputmode': 'numeric',
|
'html_inputmode': 'numeric',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'phone',
|
'phone',
|
||||||
{
|
{
|
||||||
'title': N_('Phone Number'),
|
'title': _('Phone Number'),
|
||||||
'regex': r'\+?[-\(\)\d\.\s/]+',
|
'regex': r'\+?[-\(\)\d\.\s/]+',
|
||||||
'error_message': N_('Invalid phone number'),
|
'error_message': _('Invalid phone number'),
|
||||||
'html_input_type': 'tel',
|
'html_input_type': 'tel',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'phone-fr',
|
'phone-fr',
|
||||||
{
|
{
|
||||||
'title': N_('Phone Number (France)'),
|
'title': _('Phone Number (France)'),
|
||||||
'function': 'validate_phone_fr',
|
'function': 'validate_phone_fr',
|
||||||
'error_message': N_('Invalid phone number'),
|
'error_message': _('Invalid phone number'),
|
||||||
'html_input_type': 'tel',
|
'html_input_type': 'tel',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'zipcode-fr',
|
'zipcode-fr',
|
||||||
{
|
{
|
||||||
'title': N_('Zip Code (France)'),
|
'title': _('Zip Code (France)'),
|
||||||
'regex': r'\d{5}',
|
'regex': r'\d{5}',
|
||||||
'error_message': N_('Invalid zip code'),
|
'error_message': _('Invalid zip code'),
|
||||||
'html_inputmode': 'numeric',
|
'html_inputmode': 'numeric',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'siren-fr',
|
'siren-fr',
|
||||||
{
|
{
|
||||||
'title': N_('SIREN Code (France)'),
|
'title': _('SIREN Code (France)'),
|
||||||
'function': 'validate_siren',
|
'function': 'validate_siren',
|
||||||
'error_message': N_('Invalid SIREN code'),
|
'error_message': _('Invalid SIREN code'),
|
||||||
'html_inputmode': 'numeric',
|
'html_inputmode': 'numeric',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'siret-fr',
|
'siret-fr',
|
||||||
{
|
{
|
||||||
'title': N_('SIRET Code (France)'),
|
'title': _('SIRET Code (France)'),
|
||||||
'function': 'validate_siret',
|
'function': 'validate_siret',
|
||||||
'error_message': N_('Invalid SIRET code'),
|
'error_message': _('Invalid SIRET code'),
|
||||||
'html_inputmode': 'numeric',
|
'html_inputmode': 'numeric',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'nir-fr',
|
'nir-fr',
|
||||||
{'title': N_('NIR (France)'), 'error_message': N_('Invalid NIR'), 'function': 'validate_nir'},
|
{
|
||||||
|
'title': _('NIR (France)'),
|
||||||
|
'error_message': _('Invalid NIR'),
|
||||||
|
'function': 'validate_nir',
|
||||||
|
},
|
||||||
),
|
),
|
||||||
('iban', {'title': N_('IBAN'), 'function': 'validate_iban', 'error_message': N_('Invalid IBAN')}),
|
(
|
||||||
('regex', {'title': N_('Regular Expression')}),
|
'iban',
|
||||||
('django', {'title': N_('Django Condition')}),
|
{
|
||||||
|
'title': _('IBAN'),
|
||||||
|
'function': 'validate_iban',
|
||||||
|
'error_message': _('Invalid IBAN'),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
('regex', {'title': _('Regular Expression')}),
|
||||||
|
('django', {'title': _('Django Condition')}),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1106,7 +1115,7 @@ class ValidationWidget(CompositeWidget):
|
||||||
if not value:
|
if not value:
|
||||||
value = {}
|
value = {}
|
||||||
|
|
||||||
options = [(None, _('None'))] + [(x, _(y['title'])) for x, y in self.validation_methods.items()]
|
options = [(None, _('None'))] + [(x, y['title']) for x, y in self.validation_methods.items()]
|
||||||
|
|
||||||
self.add(
|
self.add(
|
||||||
SingleSelectWidget,
|
SingleSelectWidget,
|
||||||
|
@ -1150,7 +1159,7 @@ class ValidationWidget(CompositeWidget):
|
||||||
attrs={
|
attrs={
|
||||||
'data-dynamic-display-child-of': 'validation$type',
|
'data-dynamic-display-child-of': 'validation$type',
|
||||||
'data-dynamic-display-value-in': '|'.join(
|
'data-dynamic-display-value-in': '|'.join(
|
||||||
[validation_labels.get('regex'), validation_labels.get('django')]
|
[str(validation_labels.get('regex')), str(validation_labels.get('django'))]
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1212,7 +1221,7 @@ class ValidationWidget(CompositeWidget):
|
||||||
return validation.get('error_message')
|
return validation.get('error_message')
|
||||||
validation_method = cls.validation_methods.get(validation['type'])
|
validation_method = cls.validation_methods.get(validation['type'])
|
||||||
if validation_method and 'error_message' in validation_method:
|
if validation_method and 'error_message' in validation_method:
|
||||||
return _(validation_method['error_message'])
|
return validation_method['error_message']
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_validation_pattern(cls, validation):
|
def get_validation_pattern(cls, validation):
|
||||||
|
@ -1600,9 +1609,9 @@ class CaptchaWidget(CompositeWidget):
|
||||||
# don't get twice the same number
|
# don't get twice the same number
|
||||||
b = random.randint(2, 9)
|
b = random.randint(2, 9)
|
||||||
if mode == 'arithmetic-simple':
|
if mode == 'arithmetic-simple':
|
||||||
operator = random.choice([N_('plus'), N_('minus')])
|
operator = random.choice([_('plus'), _('minus')])
|
||||||
else:
|
else:
|
||||||
operator = random.choice([N_('times'), N_('plus'), N_('minus')])
|
operator = random.choice([_('times'), _('plus'), _('minus')])
|
||||||
if operator == 'times':
|
if operator == 'times':
|
||||||
answer = a * b
|
answer = a * b
|
||||||
elif operator == 'plus':
|
elif operator == 'plus':
|
||||||
|
@ -1614,7 +1623,7 @@ class CaptchaWidget(CompositeWidget):
|
||||||
self.question = _('What is the result of %(a)d %(op)s %(b)d?') % {
|
self.question = _('What is the result of %(a)d %(op)s %(b)d?') % {
|
||||||
'a': a,
|
'a': a,
|
||||||
'b': b,
|
'b': b,
|
||||||
'op': _(operator),
|
'op': operator,
|
||||||
}
|
}
|
||||||
self.hint = kwargs.get('hint')
|
self.hint = kwargs.get('hint')
|
||||||
if self.hint is None:
|
if self.hint is None:
|
||||||
|
@ -1656,7 +1665,7 @@ class WidgetList(quixote.form.widget.WidgetList):
|
||||||
):
|
):
|
||||||
|
|
||||||
if add_element_label == 'Add row':
|
if add_element_label == 'Add row':
|
||||||
add_element_label = _('Add row')
|
add_element_label = str(_('Add row'))
|
||||||
|
|
||||||
CompositeWidget.__init__(self, name, value=value, **kwargs)
|
CompositeWidget.__init__(self, name, value=value, **kwargs)
|
||||||
self.element_type = element_type
|
self.element_type = element_type
|
||||||
|
@ -1769,7 +1778,7 @@ class WidgetDict(quixote.form.widget.WidgetDict):
|
||||||
):
|
):
|
||||||
|
|
||||||
if add_element_label == 'Add row':
|
if add_element_label == 'Add row':
|
||||||
add_element_label = _('Add row')
|
add_element_label = str(_('Add row'))
|
||||||
|
|
||||||
quixote.form.widget.WidgetDict.__init__(
|
quixote.form.widget.WidgetDict.__init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -185,13 +185,13 @@ class HTTPResponse(quixote.http_response.HTTPResponse):
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_after_job(self, label_or_instance, cmd=None, fire_and_forget=False):
|
def add_after_job(self, label_or_instance, cmd=None, fire_and_forget=False, **kwargs):
|
||||||
if not self.after_jobs:
|
if not self.after_jobs:
|
||||||
self.after_jobs = []
|
self.after_jobs = []
|
||||||
if isinstance(label_or_instance, AfterJob):
|
if isinstance(label_or_instance, AfterJob):
|
||||||
job = label_or_instance
|
job = label_or_instance
|
||||||
else:
|
else:
|
||||||
job = AfterJob(label=label_or_instance, cmd=cmd)
|
job = AfterJob(label=label_or_instance, cmd=cmd, **kwargs)
|
||||||
if fire_and_forget:
|
if fire_and_forget:
|
||||||
job.id = None
|
job.id = None
|
||||||
self.after_jobs.append(job)
|
self.after_jobs.append(job)
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from . import N_, _, ngettext
|
from . import _, ngettext
|
||||||
|
|
||||||
_minute = 60
|
_minute = 60
|
||||||
_hour = 60 * 60
|
_hour = 60 * 60
|
||||||
|
@ -34,12 +34,12 @@ def list2human(stringlist):
|
||||||
|
|
||||||
|
|
||||||
_humandurations = (
|
_humandurations = (
|
||||||
((N_("day"), N_("days")), _day),
|
((_("day"), _("days")), _day),
|
||||||
((N_("hour"), N_("hours")), _hour),
|
((_("hour"), _("hours")), _hour),
|
||||||
((N_("month"), N_("months")), _month),
|
((_("month"), _("months")), _month),
|
||||||
((N_("year"), N_("years")), _year),
|
((_("year"), _("years")), _year),
|
||||||
((N_("minute"), N_("minutes")), _minute),
|
((_("minute"), _("minutes")), _minute),
|
||||||
((N_("second"), N_("seconds")), 1),
|
((_("second"), _("seconds")), 1),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ def timewords():
|
||||||
result = []
|
result = []
|
||||||
for words, dummy in _humandurations:
|
for words, dummy in _humandurations:
|
||||||
for word in words:
|
for word in words:
|
||||||
result.append(_(word))
|
result.append(str(word)) # str() to force translation
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,7 +58,6 @@ def humanduration2seconds(humanduration):
|
||||||
seconds = 0
|
seconds = 0
|
||||||
for words, quantity in _humandurations:
|
for words, quantity in _humandurations:
|
||||||
for word in words:
|
for word in words:
|
||||||
word = _(word)
|
|
||||||
m = re.search(r"(\d+)\s*\b%s\b" % word, humanduration)
|
m = re.search(r"(\d+)\s*\b%s\b" % word, humanduration)
|
||||||
if m:
|
if m:
|
||||||
seconds = seconds + int(m.group(1)) * quantity
|
seconds = seconds + int(m.group(1)) * quantity
|
||||||
|
|
|
@ -29,7 +29,7 @@ from quixote.html import TemplateIO, htmltext
|
||||||
from wcs.formdata import flatten_dict
|
from wcs.formdata import flatten_dict
|
||||||
from wcs.workflows import WorkflowStatusItem
|
from wcs.workflows import WorkflowStatusItem
|
||||||
|
|
||||||
from .. import N_, _, get_cfg, get_logger, template
|
from .. import _, get_cfg, get_logger, template
|
||||||
from ..backoffice.menu import html_top
|
from ..backoffice.menu import html_top
|
||||||
from ..form import (
|
from ..form import (
|
||||||
CompositeWidget,
|
CompositeWidget,
|
||||||
|
@ -42,7 +42,7 @@ from ..form import (
|
||||||
from ..misc import http_get_page, http_post_request, json_loads
|
from ..misc import http_get_page, http_post_request, json_loads
|
||||||
from .base import AuthMethod
|
from .base import AuthMethod
|
||||||
|
|
||||||
ADMIN_TITLE = N_('FranceConnect')
|
ADMIN_TITLE = _('FranceConnect')
|
||||||
|
|
||||||
# XXX: make an OIDC auth method that FranceConnect would inherit from
|
# XXX: make an OIDC auth method that FranceConnect would inherit from
|
||||||
|
|
||||||
|
@ -122,13 +122,13 @@ class MethodDirectory(Directory):
|
||||||
|
|
||||||
class MethodAdminDirectory(Directory):
|
class MethodAdminDirectory(Directory):
|
||||||
title = ADMIN_TITLE
|
title = ADMIN_TITLE
|
||||||
label = N_('Configure FranceConnect identification method')
|
label = _('Configure FranceConnect identification method')
|
||||||
|
|
||||||
_q_exports = ['']
|
_q_exports = ['']
|
||||||
|
|
||||||
PLATFORMS = [
|
PLATFORMS = [
|
||||||
{
|
{
|
||||||
'name': N_('Development citizens'),
|
'name': _('Development citizens'),
|
||||||
'slug': 'dev-particulier',
|
'slug': 'dev-particulier',
|
||||||
'authorization_url': 'https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize',
|
'authorization_url': 'https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize',
|
||||||
'token_url': 'https://fcp.integ01.dev-franceconnect.fr/api/v1/token',
|
'token_url': 'https://fcp.integ01.dev-franceconnect.fr/api/v1/token',
|
||||||
|
@ -136,7 +136,7 @@ class MethodAdminDirectory(Directory):
|
||||||
'logout_url': 'https://fcp.integ01.dev-franceconnect.fr/api/v1/logout',
|
'logout_url': 'https://fcp.integ01.dev-franceconnect.fr/api/v1/logout',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': N_('Development enterprise'),
|
'name': _('Development enterprise'),
|
||||||
'slug': 'dev-entreprise',
|
'slug': 'dev-entreprise',
|
||||||
'authorization_url': 'https://fce.integ01.dev-franceconnect.fr/api/v1/authorize',
|
'authorization_url': 'https://fce.integ01.dev-franceconnect.fr/api/v1/authorize',
|
||||||
'token_url': 'https://fce.integ01.dev-franceconnect.fr/api/v1/token',
|
'token_url': 'https://fce.integ01.dev-franceconnect.fr/api/v1/token',
|
||||||
|
@ -144,7 +144,7 @@ class MethodAdminDirectory(Directory):
|
||||||
'logout_url': 'https://fce.integ01.dev-franceconnect.fr/api/v1/logout',
|
'logout_url': 'https://fce.integ01.dev-franceconnect.fr/api/v1/logout',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': N_('Production citizens'),
|
'name': _('Production citizens'),
|
||||||
'slug': 'prod-particulier',
|
'slug': 'prod-particulier',
|
||||||
'authorization_url': 'https://app.franceconnect.gouv.fr/api/v1/authorize',
|
'authorization_url': 'https://app.franceconnect.gouv.fr/api/v1/authorize',
|
||||||
'token_url': 'https://app.franceconnect.gouv.fr/api/v1/token',
|
'token_url': 'https://app.franceconnect.gouv.fr/api/v1/token',
|
||||||
|
@ -154,22 +154,22 @@ class MethodAdminDirectory(Directory):
|
||||||
]
|
]
|
||||||
|
|
||||||
CONFIG = [
|
CONFIG = [
|
||||||
('client_id', N_('Client ID')),
|
('client_id', _('Client ID')),
|
||||||
('client_secret', N_('Client secret')),
|
('client_secret', _('Client secret')),
|
||||||
('platform', N_('Platform')),
|
('platform', _('Platform')),
|
||||||
('scopes', N_('Scopes')),
|
('scopes', _('Scopes')),
|
||||||
('user_field_mappings', N_('User field mappings')),
|
('user_field_mappings', _('User field mappings')),
|
||||||
]
|
]
|
||||||
|
|
||||||
KNOWN_ATTRIBUTES = [
|
KNOWN_ATTRIBUTES = [
|
||||||
('given_name', N_('first names separated by spaces')),
|
('given_name', _('first names separated by spaces')),
|
||||||
('family_name', N_('birth\'s last name')),
|
('family_name', _('birth\'s last name')),
|
||||||
('birthdate', N_('birthdate formatted as YYYY-MM-DD')),
|
('birthdate', _('birthdate formatted as YYYY-MM-DD')),
|
||||||
('gender', N_('gender \'male\' for men, and \'female\' for women')),
|
('gender', _('gender \'male\' for men, and \'female\' for women')),
|
||||||
('birthplace', N_('INSEE code of the place of birth')),
|
('birthplace', _('INSEE code of the place of birth')),
|
||||||
('birthcountry', N_('INSEE code of the country of birth')),
|
('birthcountry', _('INSEE code of the country of birth')),
|
||||||
('email', N_('email')),
|
('email', _('email')),
|
||||||
('siret', N_('SIRET or SIREN number of the enterprise')),
|
('siret', _('SIRET or SIREN number of the enterprise')),
|
||||||
# Note: FranceConnect website also refer to adress and phones attributes
|
# Note: FranceConnect website also refer to adress and phones attributes
|
||||||
# but we don't know what must be expected of their value.
|
# but we don't know what must be expected of their value.
|
||||||
]
|
]
|
||||||
|
@ -189,7 +189,7 @@ class MethodAdminDirectory(Directory):
|
||||||
widget = UserFieldMappingTableWidget
|
widget = UserFieldMappingTableWidget
|
||||||
elif key == 'platform':
|
elif key == 'platform':
|
||||||
widget = SingleSelectWidget
|
widget = SingleSelectWidget
|
||||||
kwargs['options'] = [(platform['slug'], _(platform['name'])) for platform in cls.PLATFORMS]
|
kwargs['options'] = [(platform['slug'], platform['name']) for platform in cls.PLATFORMS]
|
||||||
elif key == 'scopes':
|
elif key == 'scopes':
|
||||||
default = 'identite_pivot address email phones'
|
default = 'identite_pivot address email phones'
|
||||||
hint = _(
|
hint = _(
|
||||||
|
@ -202,7 +202,7 @@ class MethodAdminDirectory(Directory):
|
||||||
form.add(
|
form.add(
|
||||||
widget,
|
widget,
|
||||||
key,
|
key,
|
||||||
title=_(title),
|
title=title,
|
||||||
hint=hint,
|
hint=hint,
|
||||||
required=True,
|
required=True,
|
||||||
value=instance.get(key, default),
|
value=instance.get(key, default),
|
||||||
|
@ -233,7 +233,7 @@ class MethodAdminDirectory(Directory):
|
||||||
if 'submit' in get_request().form and form.is_submitted() and not form.has_errors():
|
if 'submit' in get_request().form and form.is_submitted() and not form.has_errors():
|
||||||
return self.submit(form)
|
return self.submit(form)
|
||||||
|
|
||||||
html_top('settings', title=_(self.title))
|
html_top('settings', title=self.title)
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
r += htmltext('<h2>%s</h2>') % self.title
|
r += htmltext('<h2>%s</h2>') % self.title
|
||||||
fc_callback = pub.get_frontoffice_url() + '/ident/fc/callback'
|
fc_callback = pub.get_frontoffice_url() + '/ident/fc/callback'
|
||||||
|
@ -268,7 +268,7 @@ class MethodAdminDirectory(Directory):
|
||||||
'<table class="franceconnect-attrs"><thead>' '<tr><th>%s</th><th>%s</th></tr></thead><tbody>'
|
'<table class="franceconnect-attrs"><thead>' '<tr><th>%s</th><th>%s</th></tr></thead><tbody>'
|
||||||
) % (_('Attribute'), _('Description'))
|
) % (_('Attribute'), _('Description'))
|
||||||
for attribute, description in self.KNOWN_ATTRIBUTES:
|
for attribute, description in self.KNOWN_ATTRIBUTES:
|
||||||
r += htmltext('<tr><td><code>%s</code></td><td>%s</td></tr>') % (attribute, _(description))
|
r += htmltext('<tr><td><code>%s</code></td><td>%s</td></tr>') % (attribute, description)
|
||||||
r += htmltext('</tbody></table></div>')
|
r += htmltext('</tbody></table></div>')
|
||||||
|
|
||||||
return r.getvalue()
|
return r.getvalue()
|
||||||
|
@ -444,7 +444,7 @@ class FCAuthMethod(AuthMethod):
|
||||||
user.set_attributes_from_formdata(user.form_data)
|
user.set_attributes_from_formdata(user.form_data)
|
||||||
|
|
||||||
AUTHORIZATION_REQUEST_ERRORS = {
|
AUTHORIZATION_REQUEST_ERRORS = {
|
||||||
'access_denied': N_('user did not authorize login'),
|
'access_denied': _('user did not authorize login'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def callback(self):
|
def callback(self):
|
||||||
|
@ -465,7 +465,7 @@ class FCAuthMethod(AuthMethod):
|
||||||
if error:
|
if error:
|
||||||
# we log only errors whose user is not responsible
|
# we log only errors whose user is not responsible
|
||||||
msg = self.AUTHORIZATION_REQUEST_ERRORS.get(error)
|
msg = self.AUTHORIZATION_REQUEST_ERRORS.get(error)
|
||||||
logger.error(_('FranceConnect authentication failed: %s'), _(msg) if msg else error)
|
logger.error(_('FranceConnect authentication failed: %s'), msg if msg else error)
|
||||||
return redirect(next_url)
|
return redirect(next_url)
|
||||||
access_token, id_token = self.get_access_token(request.form['code'])
|
access_token, id_token = self.get_access_token(request.form['code'])
|
||||||
if not access_token:
|
if not access_token:
|
||||||
|
|
|
@ -29,7 +29,7 @@ from quixote import get_publisher, get_request, get_response, get_session, redir
|
||||||
from quixote.directory import Directory
|
from quixote.directory import Directory
|
||||||
from quixote.html import TemplateIO, htmltext
|
from quixote.html import TemplateIO, htmltext
|
||||||
|
|
||||||
from .. import N_, _, errors, get_cfg, get_logger, misc, saml2utils, template, x509utils
|
from .. import _, errors, get_cfg, get_logger, misc, saml2utils, template, x509utils
|
||||||
from ..admin.menu import command_icon
|
from ..admin.menu import command_icon
|
||||||
from ..backoffice.menu import html_top
|
from ..backoffice.menu import html_top
|
||||||
from ..form import (
|
from ..form import (
|
||||||
|
@ -47,7 +47,7 @@ from ..storage import atomic_write
|
||||||
from ..tokens import Token
|
from ..tokens import Token
|
||||||
from .base import AuthMethod
|
from .base import AuthMethod
|
||||||
|
|
||||||
ADMIN_TITLE = N_('SAML2')
|
ADMIN_TITLE = _('SAML2')
|
||||||
|
|
||||||
|
|
||||||
def is_idp_managing_user_attributes():
|
def is_idp_managing_user_attributes():
|
||||||
|
@ -214,16 +214,16 @@ class MethodDirectory(Directory):
|
||||||
|
|
||||||
|
|
||||||
class AdminIDPDir(Directory):
|
class AdminIDPDir(Directory):
|
||||||
title = N_('Identity Providers')
|
title = _('Identity Providers')
|
||||||
|
|
||||||
_q_exports = ['', 'new', 'new_remote']
|
_q_exports = ['', 'new', 'new_remote']
|
||||||
|
|
||||||
def _q_traverse(self, path):
|
def _q_traverse(self, path):
|
||||||
get_response().breadcrumb.append(('idp/', _(self.title)))
|
get_response().breadcrumb.append(('idp/', self.title))
|
||||||
return Directory._q_traverse(self, path)
|
return Directory._q_traverse(self, path)
|
||||||
|
|
||||||
def _q_index(self):
|
def _q_index(self):
|
||||||
html_top('settings', title=_(self.title))
|
html_top('settings', title=self.title)
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
r += htmltext('<h2>%s</h2>') % _('Identity Providers')
|
r += htmltext('<h2>%s</h2>') % _('Identity Providers')
|
||||||
r += htmltext('<ul id="nav-idp-admin">\n')
|
r += htmltext('<ul id="nav-idp-admin">\n')
|
||||||
|
@ -790,18 +790,18 @@ class AdminIDPUI(Directory):
|
||||||
|
|
||||||
class MethodAdminDirectory(Directory):
|
class MethodAdminDirectory(Directory):
|
||||||
title = ADMIN_TITLE
|
title = ADMIN_TITLE
|
||||||
label = N_('Configure SAML identification method')
|
label = _('Configure SAML identification method')
|
||||||
|
|
||||||
_q_exports = ['', 'sp', 'idp', 'identities']
|
_q_exports = ['', 'sp', 'idp', 'identities']
|
||||||
|
|
||||||
idp = AdminIDPDir()
|
idp = AdminIDPDir()
|
||||||
|
|
||||||
def _q_traverse(self, path):
|
def _q_traverse(self, path):
|
||||||
get_response().breadcrumb.append(('idp/', _(self.title)))
|
get_response().breadcrumb.append(('idp/', self.title))
|
||||||
return Directory._q_traverse(self, path)
|
return Directory._q_traverse(self, path)
|
||||||
|
|
||||||
def _q_index(self):
|
def _q_index(self):
|
||||||
html_top('settings', title=_(self.title))
|
html_top('settings', title=self.title)
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
r += htmltext('<h2>SAML 2.0</h2>')
|
r += htmltext('<h2>SAML 2.0</h2>')
|
||||||
r += htmltext('<dl> <dt><a href="sp">%s</a></dt> <dd>%s</dd>') % (
|
r += htmltext('<dl> <dt><a href="sp">%s</a></dt> <dd>%s</dd>') % (
|
||||||
|
@ -1185,7 +1185,7 @@ class MethodUserDirectory(Directory):
|
||||||
|
|
||||||
class IdPAuthMethod(AuthMethod):
|
class IdPAuthMethod(AuthMethod):
|
||||||
key = 'idp'
|
key = 'idp'
|
||||||
description = N_('SAML identity provider')
|
description = _('SAML identity provider')
|
||||||
method_directory = MethodDirectory
|
method_directory = MethodDirectory
|
||||||
method_admin_directory = MethodAdminDirectory
|
method_admin_directory = MethodAdminDirectory
|
||||||
method_user_directory = MethodUserDirectory
|
method_user_directory = MethodUserDirectory
|
||||||
|
|
|
@ -23,7 +23,7 @@ from quixote.html import TemplateIO, htmltext
|
||||||
|
|
||||||
from wcs.qommon.admin.texts import TextsDirectory
|
from wcs.qommon.admin.texts import TextsDirectory
|
||||||
|
|
||||||
from .. import N_, _, emails, errors, get_cfg, get_logger, misc, ngettext
|
from .. import _, emails, errors, get_cfg, get_logger, misc, ngettext
|
||||||
from .. import storage as st
|
from .. import storage as st
|
||||||
from .. import template, tokens
|
from .. import template, tokens
|
||||||
from ..admin.emails import EmailsDirectory
|
from ..admin.emails import EmailsDirectory
|
||||||
|
@ -680,21 +680,21 @@ class MethodDirectory(Directory):
|
||||||
emails.custom_template_email('password-subscription-notification', data, user.email)
|
emails.custom_template_email('password-subscription-notification', data, user.email)
|
||||||
|
|
||||||
|
|
||||||
ADMIN_TITLE = N_('Username / Password')
|
ADMIN_TITLE = _('Username / Password')
|
||||||
|
|
||||||
|
|
||||||
class MethodAdminDirectory(Directory):
|
class MethodAdminDirectory(Directory):
|
||||||
title = ADMIN_TITLE
|
title = ADMIN_TITLE
|
||||||
label = N_('Configure username/password identification method')
|
label = _('Configure username/password identification method')
|
||||||
|
|
||||||
_q_exports = ['', 'passwords', 'identities']
|
_q_exports = ['', 'passwords', 'identities']
|
||||||
|
|
||||||
def _q_index(self):
|
def _q_index(self):
|
||||||
html_top('settings', title=_(ADMIN_TITLE))
|
html_top('settings', title=ADMIN_TITLE)
|
||||||
get_response().breadcrumb.append(('password/', _(self.title)))
|
get_response().breadcrumb.append(('password/', ADMIN_TITLE))
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
|
|
||||||
r += htmltext('<h2>%s</h2>') % _(ADMIN_TITLE)
|
r += htmltext('<h2>%s</h2>') % ADMIN_TITLE
|
||||||
|
|
||||||
r += get_session().display_message()
|
r += get_session().display_message()
|
||||||
|
|
||||||
|
@ -958,7 +958,7 @@ class MethodUserDirectory(Directory):
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
def email(self):
|
def email(self):
|
||||||
html_top('users', title=_(ADMIN_TITLE))
|
html_top('users', title=ADMIN_TITLE)
|
||||||
r = TemplateIO(html=True)
|
r = TemplateIO(html=True)
|
||||||
get_response().breadcrumb.append(('email', 'Email Password'))
|
get_response().breadcrumb.append(('email', 'Email Password'))
|
||||||
r += htmltext('<h2>%s</h2>') % _('Email Password')
|
r += htmltext('<h2>%s</h2>') % _('Email Password')
|
||||||
|
@ -1065,11 +1065,11 @@ class PasswordAuthMethod(AuthMethod):
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'password-subscription-notification',
|
'password-subscription-notification',
|
||||||
N_('Subscription notification for password account'),
|
_('Subscription notification for password account'),
|
||||||
N_('Available variables: email, website, token_url, token, admin_email, username, password'),
|
_('Available variables: email, website, token_url, token, admin_email, username, password'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('Subscription Confirmation'),
|
default_subject=_('Subscription Confirmation'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
We have received a request for subscription of your email address,
|
We have received a request for subscription of your email address,
|
||||||
"[email]", to the [website] web site.
|
"[email]", to the [website] web site.
|
||||||
|
@ -1089,11 +1089,11 @@ to [admin_email].
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'change-password-request',
|
'change-password-request',
|
||||||
N_('Request for password change'),
|
_('Request for password change'),
|
||||||
N_('Available variables: change_url, cancel_url, token, time'),
|
_('Available variables: change_url, cancel_url, token, time'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('Change Password Request'),
|
default_subject=_('Change Password Request'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
"""\
|
"""\
|
||||||
You have (or someone impersonating you has) requested to change your
|
You have (or someone impersonating you has) requested to change your
|
||||||
password. To complete the change, visit the following link:
|
password. To complete the change, visit the following link:
|
||||||
|
@ -1114,11 +1114,11 @@ If you do nothing, the request will lapse after 3 days (precisely on
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'new-generated-password',
|
'new-generated-password',
|
||||||
N_('New generated password'),
|
_('New generated password'),
|
||||||
N_('Available variables: username, password, hostname'),
|
_('Available variables: username, password, hostname'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('Your new password'),
|
default_subject=_('Your new password'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Hello,
|
Hello,
|
||||||
|
|
||||||
|
@ -1134,11 +1134,11 @@ account details:
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'new-account-approved',
|
'new-account-approved',
|
||||||
N_('Approval of new account'),
|
_('Approval of new account'),
|
||||||
N_('Available variables: username, password'),
|
_('Available variables: username, password'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('Your account has been approved'),
|
default_subject=_('Your account has been approved'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Your account has been approved.
|
Your account has been approved.
|
||||||
|
|
||||||
|
@ -1152,11 +1152,11 @@ Account details:
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'warning-about-unused-account',
|
'warning-about-unused-account',
|
||||||
N_('Warning about unusued account'),
|
_('Warning about unusued account'),
|
||||||
N_('Available variables: username'),
|
_('Available variables: username'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('Your account is unused'),
|
default_subject=_('Your account is unused'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Your account ([username]) is not being used.
|
Your account ([username]) is not being used.
|
||||||
'''
|
'''
|
||||||
|
@ -1165,11 +1165,11 @@ Your account ([username]) is not being used.
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'notification-of-removed-account',
|
'notification-of-removed-account',
|
||||||
N_('Notification of removal of unused account'),
|
_('Notification of removal of unused account'),
|
||||||
N_('Available variables: username'),
|
_('Available variables: username'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('Your account has been removed'),
|
default_subject=_('Your account has been removed'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Your account ([username]) was not being used, it has therefore been removed.
|
Your account ([username]) was not being used, it has therefore been removed.
|
||||||
'''
|
'''
|
||||||
|
@ -1178,11 +1178,11 @@ Your account ([username]) was not being used, it has therefore been removed.
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'new-registration-admin-notification',
|
'new-registration-admin-notification',
|
||||||
N_('Notification of new registration to administrators'),
|
_('Notification of new registration to administrators'),
|
||||||
N_('Available variables: hostname, email_as_username, username'),
|
_('Available variables: hostname, email_as_username, username'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('New Registration'),
|
default_subject=_('New Registration'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Hello,
|
Hello,
|
||||||
|
|
||||||
|
@ -1196,11 +1196,11 @@ A new account has been created on [hostname].
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'new-account-generated-password',
|
'new-account-generated-password',
|
||||||
N_('Welcome email, with generated password'),
|
_('Welcome email, with generated password'),
|
||||||
N_('Available variables: hostname, username, password, email_as_username'),
|
_('Available variables: hostname, username, password, email_as_username'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('Welcome to [hostname]'),
|
default_subject=_('Welcome to [hostname]'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Welcome to [hostname],
|
Welcome to [hostname],
|
||||||
|
|
||||||
|
@ -1211,11 +1211,11 @@ Your password is: [password]
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'password-email-create-anew',
|
'password-email-create-anew',
|
||||||
N_('Email with a new password for the user'),
|
_('Email with a new password for the user'),
|
||||||
N_('Available variables: hostname, name, username, password'),
|
_('Available variables: hostname, name, username, password'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('Your new password for [hostname]'),
|
default_subject=_('Your new password for [hostname]'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Hello [name],
|
Hello [name],
|
||||||
|
|
||||||
|
@ -1226,11 +1226,11 @@ Here is your new password for [hostname]: [password]
|
||||||
|
|
||||||
EmailsDirectory.register(
|
EmailsDirectory.register(
|
||||||
'password-email-current',
|
'password-email-current',
|
||||||
N_('Email with current password for the user'),
|
_('Email with current password for the user'),
|
||||||
N_('Available variables: hostname, name, username, password'),
|
_('Available variables: hostname, name, username, password'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default_subject=N_('Your password for [hostname]'),
|
default_subject=_('Your password for [hostname]'),
|
||||||
default_body=N_(
|
default_body=_(
|
||||||
'''\
|
'''\
|
||||||
Hello [name],
|
Hello [name],
|
||||||
|
|
||||||
|
@ -1242,9 +1242,9 @@ Here is your password for [hostname]: [password]
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'account-created',
|
'account-created',
|
||||||
N_('Text when account confirmed by user'),
|
_('Text when account confirmed by user'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default=N_(
|
default=_(
|
||||||
'''<p>
|
'''<p>
|
||||||
Your account has been created.
|
Your account has been created.
|
||||||
</p>'''
|
</p>'''
|
||||||
|
@ -1253,9 +1253,9 @@ Your account has been created.
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'password-forgotten-token-sent',
|
'password-forgotten-token-sent',
|
||||||
N_('Text when an email with a change password token has been sent'),
|
_('Text when an email with a change password token has been sent'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default=N_(
|
default=_(
|
||||||
'''<p>
|
'''<p>
|
||||||
A token for changing your password has been emailed to you. Follow the instructions in that email to change your password.
|
A token for changing your password has been emailed to you. Follow the instructions in that email to change your password.
|
||||||
</p>
|
</p>
|
||||||
|
@ -1267,9 +1267,9 @@ A token for changing your password has been emailed to you. Follow the instructi
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'new-password-sent-by-email',
|
'new-password-sent-by-email',
|
||||||
N_('Text when new password has been sent'),
|
_('Text when new password has been sent'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default=N_(
|
default=_(
|
||||||
'''<p>
|
'''<p>
|
||||||
Your new password has been sent to you by email.
|
Your new password has been sent to you by email.
|
||||||
</p>
|
</p>
|
||||||
|
@ -1279,13 +1279,13 @@ Your new password has been sent to you by email.
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
TextsDirectory.register('new-account', N_('Text on top of registration form'), category=N_('Identification'))
|
TextsDirectory.register('new-account', _('Text on top of registration form'), category=_('Identification'))
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'password-forgotten-link',
|
'password-forgotten-link',
|
||||||
N_('Text on login page, linking to the forgotten password request page'),
|
_('Text on login page, linking to the forgotten password request page'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default=N_(
|
default=_(
|
||||||
'''<p>
|
'''<p>
|
||||||
If you have an account, but have forgotten your password, you should go
|
If you have an account, but have forgotten your password, you should go
|
||||||
to the <a href="%(forgotten_url)s">Lost password page</a> and submit a request
|
to the <a href="%(forgotten_url)s">Lost password page</a> and submit a request
|
||||||
|
@ -1296,9 +1296,9 @@ to change your password.
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'password-forgotten-enter-username',
|
'password-forgotten-enter-username',
|
||||||
N_('Text on forgotten password request page'),
|
_('Text on forgotten password request page'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default=N_(
|
default=_(
|
||||||
'''<p>
|
'''<p>
|
||||||
If you have an account, but have forgotten your password, enter your user name
|
If you have an account, but have forgotten your password, enter your user name
|
||||||
below and submit a request to change your password.
|
below and submit a request to change your password.
|
||||||
|
@ -1308,10 +1308,10 @@ below and submit a request to change your password.
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'password-account-link-to-register-page',
|
'password-account-link-to-register-page',
|
||||||
N_('Text linking the login page to the account creation page'),
|
_('Text linking the login page to the account creation page'),
|
||||||
hint=N_('Available variable: register_url'),
|
hint=_('Available variable: register_url'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default=N_(
|
default=_(
|
||||||
'''<p>
|
'''<p>
|
||||||
If you do not have an account, you should go to the <a href="[register_url]">
|
If you do not have an account, you should go to the <a href="[register_url]">
|
||||||
New Account page</a>.
|
New Account page</a>.
|
||||||
|
@ -1321,22 +1321,22 @@ New Account page</a>.
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'invalid-password-token',
|
'invalid-password-token',
|
||||||
N_('Text when an invalid password token is used'),
|
_('Text when an invalid password token is used'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default=N_(
|
default=_(
|
||||||
'''<p>
|
'''<p>
|
||||||
Sorry, the token you used is invalid, or has already been used.
|
Sorry, the token you used is invalid, or has already been used.
|
||||||
</p>'''
|
</p>'''
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
TextsDirectory.register('top-of-login', N_('Text on top of the login page'), category=N_('Identification'))
|
TextsDirectory.register('top-of-login', _('Text on top of the login page'), category=_('Identification'))
|
||||||
|
|
||||||
TextsDirectory.register(
|
TextsDirectory.register(
|
||||||
'email-sent-confirm-creation',
|
'email-sent-confirm-creation',
|
||||||
N_('Text when a mail for confirmation of an account creation has been sent'),
|
_('Text when a mail for confirmation of an account creation has been sent'),
|
||||||
category=N_('Identification'),
|
category=_('Identification'),
|
||||||
default=N_('An email has been sent to you so you can confirm your account creation.'),
|
default=_('An email has been sent to you so you can confirm your account creation.'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -600,6 +600,10 @@ class JSONEncoder(json.JSONEncoder):
|
||||||
'content': base64.b64encode(obj.get_content()),
|
'content': base64.b64encode(obj.get_content()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if obj.__class__.__name__ == '__proxy__':
|
||||||
|
# lazy gettext
|
||||||
|
return str(obj)
|
||||||
|
|
||||||
# Let the base class default method raise the TypeError
|
# Let the base class default method raise the TypeError
|
||||||
return json.JSONEncoder.default(self, obj)
|
return json.JSONEncoder.default(self, obj)
|
||||||
|
|
||||||
|
@ -620,7 +624,7 @@ def json_response(data):
|
||||||
get_response().set_header('Access-Control-Allow-Origin', get_request().get_environ('HTTP_ORIGIN'))
|
get_response().set_header('Access-Control-Allow-Origin', get_request().get_environ('HTTP_ORIGIN'))
|
||||||
get_response().set_header('Access-Control-Allow-Credentials', 'true')
|
get_response().set_header('Access-Control-Allow-Credentials', 'true')
|
||||||
get_response().set_header('Access-Control-Allow-Headers', 'x-requested-with')
|
get_response().set_header('Access-Control-Allow-Headers', 'x-requested-with')
|
||||||
json_str = json.dumps(data)
|
json_str = json.dumps(data, cls=JSONEncoder)
|
||||||
for variable in ('jsonpCallback', 'callback'):
|
for variable in ('jsonpCallback', 'callback'):
|
||||||
if variable in get_request().form:
|
if variable in get_request().form:
|
||||||
get_response().set_content_type('application/javascript')
|
get_response().set_content_type('application/javascript')
|
||||||
|
@ -1002,6 +1006,7 @@ def get_document_types(current_document_type):
|
||||||
document_types.update(get_cfg('filetypes', {}))
|
document_types.update(get_cfg('filetypes', {}))
|
||||||
for key, document_type in document_types.items():
|
for key, document_type in document_types.items():
|
||||||
document_type['id'] = key
|
document_type['id'] = key
|
||||||
|
document_type['label'] = str(document_type['label'])
|
||||||
# add current file type if it does not exist anymore in the settings
|
# add current file type if it does not exist anymore in the settings
|
||||||
cur_dt = current_document_type
|
cur_dt = current_document_type
|
||||||
if cur_dt and cur_dt['id'] not in document_types:
|
if cur_dt and cur_dt['id'] not in document_types:
|
||||||
|
|
|
@ -20,7 +20,7 @@ from quixote.html import TemplateIO, htmltext
|
||||||
|
|
||||||
from wcs.qommon.admin.texts import TextsDirectory
|
from wcs.qommon.admin.texts import TextsDirectory
|
||||||
|
|
||||||
from . import N_, _, errors, get_cfg, template
|
from . import _, errors, get_cfg, template
|
||||||
from .form import Form, HtmlWidget, PasswordWidget
|
from .form import Form, HtmlWidget, PasswordWidget
|
||||||
from .ident.password import check_password
|
from .ident.password import check_password
|
||||||
from .ident.password_accounts import PasswordAccount
|
from .ident.password_accounts import PasswordAccount
|
||||||
|
@ -194,4 +194,4 @@ class MyspaceDirectory(Directory):
|
||||||
return form.render()
|
return form.render()
|
||||||
|
|
||||||
|
|
||||||
TextsDirectory.register('top-of-profile', N_('Text on top of the profile page'))
|
TextsDirectory.register('top-of-profile', _('Text on top of the profile page'))
|
||||||
|
|
|
@ -45,7 +45,7 @@ from django.utils import translation
|
||||||
from django.utils.encoding import force_bytes, force_text
|
from django.utils.encoding import force_bytes, force_text
|
||||||
from quixote.publish import Publisher, get_publisher, get_request, get_response
|
from quixote.publish import Publisher, get_publisher, get_request, get_response
|
||||||
|
|
||||||
from . import N_, _, errors, force_str, logger, storage, template
|
from . import _, errors, force_str, logger, storage, template
|
||||||
from .cron import CronJob
|
from .cron import CronJob
|
||||||
from .http_request import HTTPRequest
|
from .http_request import HTTPRequest
|
||||||
from .http_response import AfterJob, HTTPResponse
|
from .http_response import AfterJob, HTTPResponse
|
||||||
|
@ -185,7 +185,13 @@ class QommonPublisher(Publisher):
|
||||||
request.response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % exc.realm
|
request.response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % exc.realm
|
||||||
if request.is_json():
|
if request.is_json():
|
||||||
request.response.set_content_type('application/json')
|
request.response.set_content_type('application/json')
|
||||||
return json.dumps({'err': 1, 'err_class': exc.title, 'err_desc': exc.public_msg})
|
return json.dumps(
|
||||||
|
{
|
||||||
|
'err': 1,
|
||||||
|
'err_class': str(exc.title),
|
||||||
|
'err_desc': str(exc.public_msg) if exc.public_msg else None,
|
||||||
|
}
|
||||||
|
)
|
||||||
if isinstance(exc, errors.TraversalError):
|
if isinstance(exc, errors.TraversalError):
|
||||||
raise Http404()
|
raise Http404()
|
||||||
output = self.format_publish_error(exc)
|
output = self.format_publish_error(exc)
|
||||||
|
@ -1003,9 +1009,9 @@ def get_publisher_class():
|
||||||
return builtins.__dict__.get('__publisher_class')
|
return builtins.__dict__.get('__publisher_class')
|
||||||
|
|
||||||
|
|
||||||
Substitutions.register('site_name', category=N_('General'), comment=N_('Site Name'))
|
Substitutions.register('site_name', category=_('General'), comment=_('Site Name'))
|
||||||
Substitutions.register('site_theme', category=N_('General'), comment=N_('Current Theme Name'))
|
Substitutions.register('site_theme', category=_('General'), comment=_('Current Theme Name'))
|
||||||
Substitutions.register('site_url', category=N_('General'), comment=N_('Site URL'))
|
Substitutions.register('site_url', category=_('General'), comment=_('Site URL'))
|
||||||
Substitutions.register('site_url_backoffice', category=N_('General'), comment=N_('Site URL (backoffice)'))
|
Substitutions.register('site_url_backoffice', category=_('General'), comment=_('Site URL (backoffice)'))
|
||||||
Substitutions.register('today', category=N_('General'), comment=N_('Current Date'))
|
Substitutions.register('today', category=_('General'), comment=_('Current Date'))
|
||||||
Substitutions.register('now', category=N_('General'), comment=N_('Current Date & Time'))
|
Substitutions.register('now', category=_('General'), comment=_('Current Date & Time'))
|
||||||
|
|
|
@ -153,6 +153,9 @@ class Session(QommonSession, CaptchaSession, StorableObject):
|
||||||
return odict
|
return odict
|
||||||
|
|
||||||
def store(self, *args, **kwargs):
|
def store(self, *args, **kwargs):
|
||||||
|
if self.message:
|
||||||
|
# escape lazy gettext
|
||||||
|
self.message = (self.message[0], str(self.message[1]))
|
||||||
current_dict = copy.copy(self.__dict__)
|
current_dict = copy.copy(self.__dict__)
|
||||||
orig_dict = current_dict.pop('__orig_dict__', {})
|
orig_dict = current_dict.pop('__orig_dict__', {})
|
||||||
current_dict.pop('_access_time', None)
|
current_dict.pop('_access_time', None)
|
||||||
|
|
|
@ -144,7 +144,7 @@ class Substitutions:
|
||||||
% (_('Category'), _('Variable'), _('Comment'))
|
% (_('Category'), _('Variable'), _('Comment'))
|
||||||
)
|
)
|
||||||
r += htmltext('<tbody>')
|
r += htmltext('<tbody>')
|
||||||
vars = [(_(y.get('category')), x, _(y.get('comment'))) for x, y in cls.substitutions_dict.items()]
|
vars = [(y.get('category'), x, y.get('comment')) for x, y in cls.substitutions_dict.items()]
|
||||||
for dynamic_source in cls.dynamic_sources:
|
for dynamic_source in cls.dynamic_sources:
|
||||||
vars.extend(dynamic_source.get_substitution_variables_list())
|
vars.extend(dynamic_source.get_substitution_variables_list())
|
||||||
vars.sort()
|
vars.sort()
|
||||||
|
|
|
@ -458,7 +458,7 @@ def ezt_raises(exception, on_parse=False):
|
||||||
message = _('syntax error in ezt template: %s')
|
message = _('syntax error in ezt template: %s')
|
||||||
else:
|
else:
|
||||||
message = _('failure to render ezt template: %s')
|
message = _('failure to render ezt template: %s')
|
||||||
raise TemplateError(message % ' '.join(parts))
|
raise TemplateError(message % ' '.join([str(x) for x in parts]))
|
||||||
|
|
||||||
|
|
||||||
class Template:
|
class Template:
|
||||||
|
|
10
wcs/root.py
10
wcs/root.py
|
@ -33,7 +33,7 @@ from .forms import root
|
||||||
from .forms.actions import ActionsDirectory
|
from .forms.actions import ActionsDirectory
|
||||||
from .forms.preview import PreviewDirectory
|
from .forms.preview import PreviewDirectory
|
||||||
from .myspace import MyspaceDirectory
|
from .myspace import MyspaceDirectory
|
||||||
from .qommon import _, errors, get_cfg, get_logger, ident, saml2, template
|
from .qommon import _, errors, get_cfg, get_logger, ident, misc, saml2, template
|
||||||
from .qommon.afterjobs import AfterJobStatusDirectory
|
from .qommon.afterjobs import AfterJobStatusDirectory
|
||||||
from .qommon.form import Form, RadiobuttonsWidget
|
from .qommon.form import Form, RadiobuttonsWidget
|
||||||
from .qommon.pages import PagesDirectory
|
from .qommon.pages import PagesDirectory
|
||||||
|
@ -92,7 +92,7 @@ class LoginDirectory(Directory):
|
||||||
RadiobuttonsWidget,
|
RadiobuttonsWidget,
|
||||||
'method',
|
'method',
|
||||||
options=[
|
options=[
|
||||||
(x.key, _(x.description)) for x in ident.get_method_classes() if x.key in ident_methods
|
(x.key, x.description) for x in ident.get_method_classes() if x.key in ident_methods
|
||||||
],
|
],
|
||||||
delim=htmltext('<br/>'),
|
delim=htmltext('<br/>'),
|
||||||
)
|
)
|
||||||
|
@ -157,7 +157,7 @@ class RegisterDirectory(Directory):
|
||||||
RadiobuttonsWidget,
|
RadiobuttonsWidget,
|
||||||
'method',
|
'method',
|
||||||
options=[
|
options=[
|
||||||
(x.key, _(x.description)) for x in ident.get_method_classes() if x.key in ident_methods
|
(x.key, x.description) for x in ident.get_method_classes() if x.key in ident_methods
|
||||||
],
|
],
|
||||||
delim=htmltext('<br/>'),
|
delim=htmltext('<br/>'),
|
||||||
)
|
)
|
||||||
|
@ -344,7 +344,7 @@ class RootDirectory(Directory):
|
||||||
# they would propose the returned json content for download if
|
# they would propose the returned json content for download if
|
||||||
# it was served with the appropriate content type :/
|
# it was served with the appropriate content type :/
|
||||||
get_response().set_content_type('text/plain')
|
get_response().set_content_type('text/plain')
|
||||||
return json.dumps(results)
|
return json.dumps(results, cls=misc.JSONEncoder)
|
||||||
|
|
||||||
def feed_substitution_parts(self):
|
def feed_substitution_parts(self):
|
||||||
get_publisher().substitutions.feed(get_session())
|
get_publisher().substitutions.feed(get_session())
|
||||||
|
@ -446,7 +446,7 @@ class RootDirectory(Directory):
|
||||||
'email_domain_suggest': _('Did you want to write'),
|
'email_domain_suggest': _('Did you want to write'),
|
||||||
'email_domain_fix': _('Apply fix'),
|
'email_domain_fix': _('Apply fix'),
|
||||||
}
|
}
|
||||||
return 'WCS_I18N = %s;\n' % json.dumps(strings)
|
return 'WCS_I18N = %s;\n' % json.dumps(strings, cls=misc.JSONEncoder)
|
||||||
|
|
||||||
admin = None
|
admin = None
|
||||||
backoffice = None
|
backoffice = None
|
||||||
|
|
|
@ -25,7 +25,7 @@ from wcs.qommon.storage import Null
|
||||||
|
|
||||||
class UnknownUser:
|
class UnknownUser:
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _('unknown user')
|
return str(_('unknown user'))
|
||||||
|
|
||||||
|
|
||||||
class Snapshot:
|
class Snapshot:
|
||||||
|
@ -53,7 +53,7 @@ class Snapshot:
|
||||||
if get_session():
|
if get_session():
|
||||||
obj.user_id = get_session().user
|
obj.user_id = get_session().user
|
||||||
obj.serialization = ET.tostring(instance.export_to_xml(include_id=True)).decode('utf-8')
|
obj.serialization = ET.tostring(instance.export_to_xml(include_id=True)).decode('utf-8')
|
||||||
obj.comment = comment
|
obj.comment = str(comment) if comment else None
|
||||||
obj.label = label
|
obj.label = label
|
||||||
latest = cls.get_latest(obj.object_type, obj.object_id)
|
latest = cls.get_latest(obj.object_type, obj.object_id)
|
||||||
if label is not None or latest is None or obj.serialization != latest.serialization:
|
if label is not None or latest is None or obj.serialization != latest.serialization:
|
||||||
|
|
|
@ -2645,6 +2645,9 @@ class Session(SqlMixin, wcs.sessions.BasicSession):
|
||||||
|
|
||||||
@guard_postgres
|
@guard_postgres
|
||||||
def store(self):
|
def store(self):
|
||||||
|
if self.message:
|
||||||
|
# escape lazy gettext
|
||||||
|
self.message = (self.message[0], str(self.message[1]))
|
||||||
sql_dict = {
|
sql_dict = {
|
||||||
'id': self.id,
|
'id': self.id,
|
||||||
'session_data': bytearray(pickle.dumps(self.__dict__, protocol=2)),
|
'session_data': bytearray(pickle.dumps(self.__dict__, protocol=2)),
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<li>{% trans "Latest occurence:" %} {{ error.latest_occurence_timestamp }}</li>
|
<li>{% trans "Latest occurence:" %} {{ error.latest_occurence_timestamp }}</li>
|
||||||
<li>{% trans "Count:" %} {{ error.occurences_count }}</li>
|
<li>{% trans "Count:" %} {{ error.occurences_count }}</li>
|
||||||
{% if formdef %}
|
{% if formdef %}
|
||||||
<li>{% trans formdef.verbose_name %}{% trans ":" %} <a href="{{ formdef.get_admin_url }}">{{ formdef.name }}</a></li>
|
<li>{{ formdef.verbose_name }}{% trans ":" %} <a href="{{ formdef.get_admin_url }}">{{ formdef.name }}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if workflow %}
|
{% if workflow %}
|
||||||
<li>{% trans "Workflow:" %} <a href="{{ workflow.get_admin_url }}">{{ workflow.name }}</a>
|
<li>{% trans "Workflow:" %} <a href="{{ workflow.get_admin_url }}">{{ workflow.name }}</a>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="section">
|
<div class="section">
|
||||||
<dl class="job-status">
|
<dl class="job-status">
|
||||||
<dt>{% trans job.label %}</dt>
|
<dt>{{ job.label }}</dt>
|
||||||
<dd><span class="afterjob" id="{{ job.id }}">{% trans job.status %}</span></dd>
|
<dd><span class="afterjob" id="{{ job.id }}">{% trans job.status %}</span></dd>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -20,7 +20,7 @@ from quixote import get_publisher
|
||||||
|
|
||||||
import wcs.qommon.storage as st
|
import wcs.qommon.storage as st
|
||||||
|
|
||||||
from .qommon import N_, _, get_cfg
|
from .qommon import _, get_cfg
|
||||||
from .qommon.misc import simplify
|
from .qommon.misc import simplify
|
||||||
from .qommon.storage import StorableObject
|
from .qommon.storage import StorableObject
|
||||||
from .qommon.substitution import Substitutions, invalidate_substitution_cache
|
from .qommon.substitution import Substitutions, invalidate_substitution_cache
|
||||||
|
@ -315,7 +315,9 @@ class User(StorableObject):
|
||||||
|
|
||||||
|
|
||||||
Substitutions.register(
|
Substitutions.register(
|
||||||
'session_user_display_name', category=N_('User'), comment=N_('Session User Display Name')
|
'session_user_display_name',
|
||||||
|
category=_('User'),
|
||||||
|
comment=_('Session User Display Name'),
|
||||||
)
|
)
|
||||||
Substitutions.register('session_user_email', category=N_('User'), comment=N_('Session User Email'))
|
Substitutions.register('session_user_email', category=_('User'), comment=_('Session User Email'))
|
||||||
Substitutions.register_dynamic_source(User)
|
Substitutions.register_dynamic_source(User)
|
||||||
|
|
|
@ -18,7 +18,7 @@ from quixote import get_publisher
|
||||||
|
|
||||||
from wcs.workflows import WorkflowStatusItem, register_item_class
|
from wcs.workflows import WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _, emails
|
from ..qommon import _, emails
|
||||||
from ..qommon.cron import CronJob
|
from ..qommon.cron import CronJob
|
||||||
from ..qommon.form import SingleSelectWidget, WidgetList
|
from ..qommon.form import SingleSelectWidget, WidgetList
|
||||||
from ..qommon.publisher import get_publisher_class
|
from ..qommon.publisher import get_publisher_class
|
||||||
|
@ -26,7 +26,7 @@ from ..qommon.storage import StorableObject
|
||||||
|
|
||||||
|
|
||||||
class AggregationEmailWorkflowStatusItem(WorkflowStatusItem):
|
class AggregationEmailWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Daily Summary Email')
|
description = _('Daily Summary Email')
|
||||||
key = 'aggregationemail'
|
key = 'aggregationemail'
|
||||||
category = 'interaction'
|
category = 'interaction'
|
||||||
ok_in_global_action = False
|
ok_in_global_action = False
|
||||||
|
|
|
@ -14,12 +14,12 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from wcs.qommon import N_
|
from wcs.qommon import _
|
||||||
from wcs.workflows import WorkflowStatusItem, register_item_class
|
from wcs.workflows import WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
|
|
||||||
class AnonymiseWorkflowStatusItem(WorkflowStatusItem):
|
class AnonymiseWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Anonymisation')
|
description = _('Anonymisation')
|
||||||
key = 'anonymise'
|
key = 'anonymise'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ from wcs.forms.common import FileDirectory, FormStatusPage
|
||||||
from wcs.portfolio import has_portfolio, push_document
|
from wcs.portfolio import has_portfolio, push_document
|
||||||
from wcs.workflows import AttachmentEvolutionPart, WorkflowStatusItem, register_item_class
|
from wcs.workflows import AttachmentEvolutionPart, WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
from ..qommon.errors import TraversalError
|
from ..qommon.errors import TraversalError
|
||||||
from ..qommon.form import (
|
from ..qommon.form import (
|
||||||
CheckboxWidget,
|
CheckboxWidget,
|
||||||
|
@ -87,7 +87,7 @@ def form_attachment(self):
|
||||||
|
|
||||||
|
|
||||||
class AddAttachmentWorkflowStatusItem(WorkflowStatusItem):
|
class AddAttachmentWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Attachment')
|
description = _('Attachment')
|
||||||
key = 'addattachment'
|
key = 'addattachment'
|
||||||
category = 'interaction'
|
category = 'interaction'
|
||||||
endpoint = False
|
endpoint = False
|
||||||
|
|
|
@ -23,7 +23,7 @@ from wcs.fields import WidgetField
|
||||||
from wcs.wf.profile import FieldNode
|
from wcs.wf.profile import FieldNode
|
||||||
from wcs.workflows import WorkflowStatusItem, register_item_class
|
from wcs.workflows import WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
from ..qommon.form import (
|
from ..qommon.form import (
|
||||||
CompositeWidget,
|
CompositeWidget,
|
||||||
ComputedExpressionWidget,
|
ComputedExpressionWidget,
|
||||||
|
@ -80,7 +80,7 @@ class SetBackofficeFieldsTableWidget(WidgetListAsTable):
|
||||||
|
|
||||||
|
|
||||||
class SetBackofficeFieldsWorkflowStatusItem(WorkflowStatusItem):
|
class SetBackofficeFieldsWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Backoffice Data')
|
description = _('Backoffice Data')
|
||||||
key = 'set-backoffice-fields'
|
key = 'set-backoffice-fields'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ class SetBackofficeFieldsWorkflowStatusItem(WorkflowStatusItem):
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
summary = _('Failed to convert %(class)s value to %(kind)s field (%(id)s)') % {
|
summary = _('Failed to convert %(class)s value to %(kind)s field (%(id)s)') % {
|
||||||
'class': type(new_value),
|
'class': type(new_value),
|
||||||
'kind': _(getattr(formdef_field, 'description', 'unknown')),
|
'kind': getattr(formdef_field, 'description', _('unknown')),
|
||||||
'id': field['field_id'],
|
'id': field['field_id'],
|
||||||
}
|
}
|
||||||
expression_dict = self.get_expression(field['value'])
|
expression_dict = self.get_expression(field['value'])
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
from quixote import get_publisher
|
from quixote import get_publisher
|
||||||
|
|
||||||
from wcs.carddef import CardDef
|
from wcs.carddef import CardDef
|
||||||
from wcs.qommon import N_
|
from wcs.qommon import _
|
||||||
from wcs.wf.create_formdata import CreateFormdataWorkflowStatusItem, LinkedFormdataEvolutionPart
|
from wcs.wf.create_formdata import CreateFormdataWorkflowStatusItem, LinkedFormdataEvolutionPart
|
||||||
from wcs.workflows import register_item_class
|
from wcs.workflows import register_item_class
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class LinkedCardDataEvolutionPart(LinkedFormdataEvolutionPart):
|
||||||
|
|
||||||
|
|
||||||
class CreateCarddataWorkflowStatusItem(CreateFormdataWorkflowStatusItem):
|
class CreateCarddataWorkflowStatusItem(CreateFormdataWorkflowStatusItem):
|
||||||
description = N_('Create Card Data')
|
description = _('Create Card Data')
|
||||||
key = 'create_carddata'
|
key = 'create_carddata'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
ok_in_global_action = True
|
ok_in_global_action = True
|
||||||
|
@ -35,10 +35,10 @@ class CreateCarddataWorkflowStatusItem(CreateFormdataWorkflowStatusItem):
|
||||||
formdef_class = CardDef
|
formdef_class = CardDef
|
||||||
evolution_part_class = LinkedCardDataEvolutionPart
|
evolution_part_class = LinkedCardDataEvolutionPart
|
||||||
|
|
||||||
formdef_label = N_('Card')
|
formdef_label = _('Card')
|
||||||
mappings_label = N_('Mappings to new card fields')
|
mappings_label = _('Mappings to new card fields')
|
||||||
varname_hint = N_('This is used to get linked card in expressions.')
|
varname_hint = _('This is used to get linked card in expressions.')
|
||||||
user_association_option_label = N_('User to associate to card')
|
user_association_option_label = _('User to associate to card')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_available(cls, workflow=None):
|
def is_available(cls, workflow=None):
|
||||||
|
|
|
@ -22,7 +22,7 @@ from quixote import get_publisher, get_request, get_session
|
||||||
from quixote.html import htmltext
|
from quixote.html import htmltext
|
||||||
|
|
||||||
from wcs.formdef import FormDef
|
from wcs.formdef import FormDef
|
||||||
from wcs.qommon import N_, _
|
from wcs.qommon import _
|
||||||
from wcs.qommon.form import (
|
from wcs.qommon.form import (
|
||||||
CheckboxWidget,
|
CheckboxWidget,
|
||||||
CompositeWidget,
|
CompositeWidget,
|
||||||
|
@ -204,7 +204,7 @@ class LazyFormDataLinks:
|
||||||
|
|
||||||
|
|
||||||
class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
|
class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('New Form Creation')
|
description = _('New Form Creation')
|
||||||
key = 'create_formdata'
|
key = 'create_formdata'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
support_substitution_variables = True
|
support_substitution_variables = True
|
||||||
|
@ -213,11 +213,11 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
|
||||||
evolution_part_class = LinkedFormdataEvolutionPart
|
evolution_part_class = LinkedFormdataEvolutionPart
|
||||||
|
|
||||||
formdef_slug = None
|
formdef_slug = None
|
||||||
formdef_label = N_('Form')
|
formdef_label = _('Form')
|
||||||
mappings_label = N_('Mappings to new form fields')
|
mappings_label = _('Mappings to new form fields')
|
||||||
accept_empty_value = False
|
accept_empty_value = False
|
||||||
varname_hint = N_('This is used to get linked forms in expressions.')
|
varname_hint = _('This is used to get linked forms in expressions.')
|
||||||
user_association_option_label = N_('User to associate to form')
|
user_association_option_label = _('User to associate to form')
|
||||||
|
|
||||||
draft = False
|
draft = False
|
||||||
backoffice_submission = False
|
backoffice_submission = False
|
||||||
|
@ -261,7 +261,7 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
|
||||||
form.add(
|
form.add(
|
||||||
SingleSelectWidget,
|
SingleSelectWidget,
|
||||||
'%sformdef_slug' % prefix,
|
'%sformdef_slug' % prefix,
|
||||||
title=_(self.formdef_label),
|
title=self.formdef_label,
|
||||||
value=self.formdef_slug,
|
value=self.formdef_slug,
|
||||||
options=list_forms,
|
options=list_forms,
|
||||||
)
|
)
|
||||||
|
@ -281,7 +281,7 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
|
||||||
form.add(
|
form.add(
|
||||||
RadiobuttonsWidget,
|
RadiobuttonsWidget,
|
||||||
'%suser_association_mode' % prefix,
|
'%suser_association_mode' % prefix,
|
||||||
title=_(self.user_association_option_label),
|
title=self.user_association_option_label,
|
||||||
options=[
|
options=[
|
||||||
(None, _('None'), 'none'),
|
(None, _('None'), 'none'),
|
||||||
('keep-user', _('Keep Current User'), 'keep-user'),
|
('keep-user', _('Keep Current User'), 'keep-user'),
|
||||||
|
@ -319,7 +319,7 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
|
||||||
widget = form.add(
|
widget = form.add(
|
||||||
MappingsWidget,
|
MappingsWidget,
|
||||||
'%smappings' % prefix,
|
'%smappings' % prefix,
|
||||||
title=_(self.mappings_label),
|
title=self.mappings_label,
|
||||||
accept_empty_value=self.accept_empty_value,
|
accept_empty_value=self.accept_empty_value,
|
||||||
to_formdef=formdef,
|
to_formdef=formdef,
|
||||||
value=self.mappings,
|
value=self.mappings,
|
||||||
|
@ -334,7 +334,7 @@ class CreateFormdataWorkflowStatusItem(WorkflowStatusItem):
|
||||||
'%svarname' % prefix,
|
'%svarname' % prefix,
|
||||||
title=_('Identifier'),
|
title=_('Identifier'),
|
||||||
value=self.varname,
|
value=self.varname,
|
||||||
hint=_(self.varname_hint),
|
hint=self.varname_hint,
|
||||||
advanced=not (bool(self.varname)),
|
advanced=not (bool(self.varname)),
|
||||||
)
|
)
|
||||||
if 'map_fields_by_varname' in parameters and formdef:
|
if 'map_fields_by_varname' in parameters and formdef:
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
from wcs.qommon.form import SingleSelectWidget
|
from wcs.qommon.form import SingleSelectWidget
|
||||||
from wcs.workflows import WorkflowStatusItem, register_item_class
|
from wcs.workflows import WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
|
|
||||||
MODE_INC = '1'
|
MODE_INC = '1'
|
||||||
MODE_DEC = '2'
|
MODE_DEC = '2'
|
||||||
|
@ -25,7 +25,7 @@ MODE_SET = '3'
|
||||||
|
|
||||||
|
|
||||||
class ModifyCriticalityWorkflowStatusItem(WorkflowStatusItem):
|
class ModifyCriticalityWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Criticality Levels')
|
description = _('Criticality Levels')
|
||||||
key = 'modify_criticality'
|
key = 'modify_criticality'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ from quixote.html import htmltext
|
||||||
from wcs.roles import get_user_roles
|
from wcs.roles import get_user_roles
|
||||||
from wcs.workflows import WorkflowStatusItem, XmlSerialisable, get_role_name, register_item_class
|
from wcs.workflows import WorkflowStatusItem, XmlSerialisable, get_role_name, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
from ..qommon.form import (
|
from ..qommon.form import (
|
||||||
CompositeWidget,
|
CompositeWidget,
|
||||||
RadiobuttonsWidget,
|
RadiobuttonsWidget,
|
||||||
|
@ -85,7 +85,7 @@ class RuleNode(XmlSerialisable):
|
||||||
|
|
||||||
|
|
||||||
class DispatchWorkflowStatusItem(WorkflowStatusItem):
|
class DispatchWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Function/Role Linking')
|
description = _('Function/Role Linking')
|
||||||
key = 'dispatch'
|
key = 'dispatch'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
from quixote import get_publisher
|
from quixote import get_publisher
|
||||||
|
|
||||||
from wcs.qommon import N_, _
|
from wcs.qommon import _
|
||||||
from wcs.wf.create_carddata import CreateCarddataWorkflowStatusItem
|
from wcs.wf.create_carddata import CreateCarddataWorkflowStatusItem
|
||||||
from wcs.wf.external_workflow import ExternalWorkflowGlobalAction
|
from wcs.wf.external_workflow import ExternalWorkflowGlobalAction
|
||||||
from wcs.workflows import register_item_class
|
from wcs.workflows import register_item_class
|
||||||
|
@ -27,8 +27,8 @@ class EditCarddataWorkflowStatusItem(CreateCarddataWorkflowStatusItem, ExternalW
|
||||||
key = 'edit_carddata'
|
key = 'edit_carddata'
|
||||||
mappings_label = _('Mappings to card fields')
|
mappings_label = _('Mappings to card fields')
|
||||||
accept_empty_value = True
|
accept_empty_value = True
|
||||||
automatic_targetting = N_('Action on linked cards')
|
automatic_targetting = _('Action on linked cards')
|
||||||
manual_targetting = N_('Specify the identifier of the card on which the action will be applied')
|
manual_targetting = _('Specify the identifier of the card on which the action will be applied')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_available(cls, workflow=None):
|
def is_available(cls, workflow=None):
|
||||||
|
|
|
@ -45,7 +45,7 @@ from wcs.workflows import (
|
||||||
template_on_formdata,
|
template_on_formdata,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..qommon import N_, _, ezt, force_str, get_logger, misc
|
from ..qommon import _, ezt, force_str, get_logger, misc
|
||||||
from ..qommon.form import (
|
from ..qommon.form import (
|
||||||
CheckboxWidget,
|
CheckboxWidget,
|
||||||
ComputedExpressionWidget,
|
ComputedExpressionWidget,
|
||||||
|
@ -220,7 +220,7 @@ class ExportToModelDirectory(Directory):
|
||||||
|
|
||||||
|
|
||||||
class ExportToModel(WorkflowStatusItem):
|
class ExportToModel(WorkflowStatusItem):
|
||||||
description = N_('Document Creation')
|
description = _('Document Creation')
|
||||||
key = 'export_to_model'
|
key = 'export_to_model'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
support_substitution_variables = True
|
support_substitution_variables = True
|
||||||
|
@ -268,7 +268,7 @@ class ExportToModel(WorkflowStatusItem):
|
||||||
return
|
return
|
||||||
if form.get_submit() == 'button%s' % self.id:
|
if form.get_submit() == 'button%s' % self.id:
|
||||||
if not evo.comment:
|
if not evo.comment:
|
||||||
evo.comment = _('Form exported in a model')
|
evo.comment = str(_('Form exported in a model'))
|
||||||
self.perform_real(formdata, evo)
|
self.perform_real(formdata, evo)
|
||||||
in_backoffice = get_request() and get_request().is_in_backoffice()
|
in_backoffice = get_request() and get_request().is_in_backoffice()
|
||||||
if self.attach_to_history:
|
if self.attach_to_history:
|
||||||
|
@ -351,7 +351,7 @@ class ExportToModel(WorkflowStatusItem):
|
||||||
widget_name,
|
widget_name,
|
||||||
value.base_filename,
|
value.base_filename,
|
||||||
)
|
)
|
||||||
hint = hint_prefix + hint
|
hint = hint_prefix + force_text(hint)
|
||||||
form.add(
|
form.add(
|
||||||
UploadWidget,
|
UploadWidget,
|
||||||
widget_name,
|
widget_name,
|
||||||
|
|
|
@ -18,7 +18,7 @@ from quixote import get_publisher
|
||||||
|
|
||||||
from wcs.carddef import CardDef
|
from wcs.carddef import CardDef
|
||||||
from wcs.formdef import FormDef
|
from wcs.formdef import FormDef
|
||||||
from wcs.qommon import N_, _
|
from wcs.qommon import _
|
||||||
from wcs.qommon.form import ComputedExpressionWidget, Form, RadiobuttonsWidget, SingleSelectWidget
|
from wcs.qommon.form import ComputedExpressionWidget, Form, RadiobuttonsWidget, SingleSelectWidget
|
||||||
from wcs.workflows import (
|
from wcs.workflows import (
|
||||||
Workflow,
|
Workflow,
|
||||||
|
@ -34,8 +34,8 @@ class ExternalWorkflowGlobalAction(WorkflowStatusItem):
|
||||||
description = _('External workflow')
|
description = _('External workflow')
|
||||||
key = 'external_workflow_global_action'
|
key = 'external_workflow_global_action'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
automatic_targetting = N_('Action on linked cards/forms')
|
automatic_targetting = _('Action on linked cards/forms')
|
||||||
manual_targetting = N_('Specify the identifier of the card/form on which the action will be applied')
|
manual_targetting = _('Specify the identifier of the card/form on which the action will be applied')
|
||||||
|
|
||||||
slug = None
|
slug = None
|
||||||
target_mode = None
|
target_mode = None
|
||||||
|
@ -101,8 +101,8 @@ class ExternalWorkflowGlobalAction(WorkflowStatusItem):
|
||||||
|
|
||||||
if 'target_mode' in parameters:
|
if 'target_mode' in parameters:
|
||||||
target_modes = [
|
target_modes = [
|
||||||
('all', _(self.automatic_targetting), 'all'),
|
('all', self.automatic_targetting, 'all'),
|
||||||
('manual', _(self.manual_targetting), 'manual'),
|
('manual', self.manual_targetting, 'manual'),
|
||||||
]
|
]
|
||||||
form.add(
|
form.add(
|
||||||
RadiobuttonsWidget,
|
RadiobuttonsWidget,
|
||||||
|
|
|
@ -26,7 +26,7 @@ from wcs.forms.common import FileDirectory
|
||||||
from wcs.forms.root import FormPage
|
from wcs.forms.root import FormPage
|
||||||
from wcs.workflows import RedisplayFormException, WorkflowStatusItem, register_item_class
|
from wcs.workflows import RedisplayFormException, WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
from ..qommon.form import SingleSelectWidget, VarnameWidget, WidgetList
|
from ..qommon.form import SingleSelectWidget, VarnameWidget, WidgetList
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,14 +85,14 @@ class WorkflowFormFieldsDirectory(FieldsDirectory):
|
||||||
|
|
||||||
|
|
||||||
class FormWorkflowStatusItem(WorkflowStatusItem):
|
class FormWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Form')
|
description = _('Form')
|
||||||
key = 'form'
|
key = 'form'
|
||||||
category = 'interaction'
|
category = 'interaction'
|
||||||
ok_in_global_action = False
|
ok_in_global_action = False
|
||||||
endpoint = False
|
endpoint = False
|
||||||
waitpoint = True
|
waitpoint = True
|
||||||
redirect_after_submit_url = 'fields/'
|
redirect_after_submit_url = 'fields/'
|
||||||
submit_button_label = N_('Submit and go to fields edition')
|
submit_button_label = _('Submit and go to fields edition')
|
||||||
|
|
||||||
by = []
|
by = []
|
||||||
formdef = None
|
formdef = None
|
||||||
|
|
|
@ -28,14 +28,14 @@ from quixote import get_publisher
|
||||||
|
|
||||||
from wcs.workflows import WorkflowStatusItem, register_item_class
|
from wcs.workflows import WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _, force_str, get_logger
|
from ..qommon import _, force_str, get_logger
|
||||||
from ..qommon.errors import ConnectionError
|
from ..qommon.errors import ConnectionError
|
||||||
from ..qommon.form import CheckboxWidget, ComputedExpressionWidget, RadiobuttonsWidget
|
from ..qommon.form import CheckboxWidget, ComputedExpressionWidget, RadiobuttonsWidget
|
||||||
from ..qommon.misc import http_get_page, normalize_geolocation
|
from ..qommon.misc import http_get_page, normalize_geolocation
|
||||||
|
|
||||||
|
|
||||||
class GeolocateWorkflowStatusItem(WorkflowStatusItem):
|
class GeolocateWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Geolocation')
|
description = _('Geolocation')
|
||||||
key = 'geolocate'
|
key = 'geolocate'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ from wcs.api import get_user_from_api_query_string, is_url_signed
|
||||||
from wcs.conditions import Condition
|
from wcs.conditions import Condition
|
||||||
from wcs.workflows import Workflow, WorkflowGlobalAction, WorkflowStatusJumpItem, register_item_class
|
from wcs.workflows import Workflow, WorkflowGlobalAction, WorkflowStatusJumpItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _, errors, force_str
|
from ..qommon import _, errors, force_str
|
||||||
from ..qommon.cron import CronJob
|
from ..qommon.cron import CronJob
|
||||||
from ..qommon.form import ComputedExpressionWidget, SingleSelectWidget, StringWidget, WidgetList
|
from ..qommon.form import ComputedExpressionWidget, SingleSelectWidget, StringWidget, WidgetList
|
||||||
from ..qommon.humantime import humanduration2seconds, seconds2humanduration, timewords
|
from ..qommon.humantime import humanduration2seconds, seconds2humanduration, timewords
|
||||||
|
@ -110,7 +110,7 @@ class TriggerDirectory(Directory):
|
||||||
|
|
||||||
|
|
||||||
class JumpWorkflowStatusItem(WorkflowStatusJumpItem):
|
class JumpWorkflowStatusItem(WorkflowStatusJumpItem):
|
||||||
description = N_('Automatic Jump')
|
description = _('Automatic Jump')
|
||||||
key = 'jump'
|
key = 'jump'
|
||||||
|
|
||||||
by = []
|
by = []
|
||||||
|
@ -167,7 +167,10 @@ class JumpWorkflowStatusItem(WorkflowStatusJumpItem):
|
||||||
reasons.append(_('timeout'))
|
reasons.append(_('timeout'))
|
||||||
|
|
||||||
if reasons:
|
if reasons:
|
||||||
return _('to %(name)s, %(reasons)s') % {'name': wf_status[0].name, 'reasons': ', '.join(reasons)}
|
return _('to %(name)s, %(reasons)s') % {
|
||||||
|
'name': wf_status[0].name,
|
||||||
|
'reasons': ', '.join([str(x) for x in reasons]),
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
return wf_status[0].name
|
return wf_status[0].name
|
||||||
|
|
||||||
|
|
|
@ -20,14 +20,14 @@ from quixote import get_publisher
|
||||||
|
|
||||||
from wcs.workflows import WorkflowStatusItem, register_item_class, template_on_formdata
|
from wcs.workflows import WorkflowStatusItem, register_item_class, template_on_formdata
|
||||||
|
|
||||||
from ..qommon import N_, _, get_logger
|
from ..qommon import _, get_logger
|
||||||
from ..qommon.form import ComputedExpressionWidget, SingleSelectWidget, StringWidget, TextWidget, WidgetList
|
from ..qommon.form import ComputedExpressionWidget, SingleSelectWidget, StringWidget, TextWidget, WidgetList
|
||||||
from ..qommon.template import TemplateError
|
from ..qommon.template import TemplateError
|
||||||
from .wscall import WebserviceCallStatusItem
|
from .wscall import WebserviceCallStatusItem
|
||||||
|
|
||||||
|
|
||||||
class SendNotificationWorkflowStatusItem(WebserviceCallStatusItem):
|
class SendNotificationWorkflowStatusItem(WebserviceCallStatusItem):
|
||||||
description = N_('User Notification')
|
description = _('User Notification')
|
||||||
key = 'notification'
|
key = 'notification'
|
||||||
category = 'interaction'
|
category = 'interaction'
|
||||||
support_substitution_variables = True
|
support_substitution_variables = True
|
||||||
|
|
|
@ -26,7 +26,7 @@ from quixote import get_publisher, get_request, get_response
|
||||||
from wcs.api_utils import MissingSecret, get_secret_and_orig, sign_url
|
from wcs.api_utils import MissingSecret, get_secret_and_orig, sign_url
|
||||||
from wcs.workflows import WorkflowStatusItem, XmlSerialisable, register_item_class
|
from wcs.workflows import WorkflowStatusItem, XmlSerialisable, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
from ..qommon.form import CompositeWidget, ComputedExpressionWidget, SingleSelectWidget, WidgetListAsTable
|
from ..qommon.form import CompositeWidget, ComputedExpressionWidget, SingleSelectWidget, WidgetListAsTable
|
||||||
from ..qommon.ident.idp import is_idp_managing_user_attributes
|
from ..qommon.ident.idp import is_idp_managing_user_attributes
|
||||||
from ..qommon.misc import JSONEncoder, http_patch_request
|
from ..qommon.misc import JSONEncoder, http_patch_request
|
||||||
|
@ -102,7 +102,7 @@ class FieldNode(XmlSerialisable):
|
||||||
|
|
||||||
|
|
||||||
class UpdateUserProfileStatusItem(WorkflowStatusItem):
|
class UpdateUserProfileStatusItem(WorkflowStatusItem):
|
||||||
description = N_('User Profile Update')
|
description = _('User Profile Update')
|
||||||
key = 'update_user_profile'
|
key = 'update_user_profile'
|
||||||
category = 'user-action'
|
category = 'user-action'
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ class UpdateUserProfileStatusItem(WorkflowStatusItem):
|
||||||
get_logger().error('failed to update profile for user %r', user)
|
get_logger().error('failed to update profile for user %r', user)
|
||||||
|
|
||||||
if get_request():
|
if get_request():
|
||||||
get_response().add_after_job(str(N_('Updating user profile')), after_job)
|
get_response().add_after_job(_('Updating user profile'), after_job)
|
||||||
else:
|
else:
|
||||||
after_job()
|
after_job()
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,12 @@
|
||||||
|
|
||||||
from wcs.workflows import WorkflowStatusItem, register_item_class
|
from wcs.workflows import WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
from ..qommon.form import ComputedExpressionWidget
|
from ..qommon.form import ComputedExpressionWidget
|
||||||
|
|
||||||
|
|
||||||
class RedirectToUrlWorkflowStatusItem(WorkflowStatusItem):
|
class RedirectToUrlWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Web Redirection')
|
description = _('Web Redirection')
|
||||||
key = 'redirect_to_url'
|
key = 'redirect_to_url'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
endpoint = False
|
endpoint = False
|
||||||
|
|
|
@ -26,7 +26,7 @@ from wcs.workflows import (
|
||||||
template_on_formdata,
|
template_on_formdata,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..qommon import N_, _, ezt, get_logger
|
from ..qommon import _, ezt, get_logger
|
||||||
from ..qommon.form import SingleSelectWidget, TextWidget, WidgetList
|
from ..qommon.form import SingleSelectWidget, TextWidget, WidgetList
|
||||||
from ..qommon.template import TemplateError
|
from ..qommon.template import TemplateError
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ class JournalEvolutionPart:
|
||||||
|
|
||||||
|
|
||||||
class RegisterCommenterWorkflowStatusItem(WorkflowStatusItem):
|
class RegisterCommenterWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('History Message')
|
description = _('History Message')
|
||||||
key = 'register-comment'
|
key = 'register-comment'
|
||||||
category = 'interaction'
|
category = 'interaction'
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,11 @@ from quixote import get_publisher, get_request, get_response, get_session
|
||||||
|
|
||||||
from wcs.workflows import AbortActionException, WorkflowStatusItem, register_item_class
|
from wcs.workflows import AbortActionException, WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
|
|
||||||
|
|
||||||
class RemoveWorkflowStatusItem(WorkflowStatusItem):
|
class RemoveWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Deletion')
|
description = _('Deletion')
|
||||||
key = 'remove'
|
key = 'remove'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
|
|
||||||
|
|
|
@ -20,11 +20,11 @@ from wcs.formdef import FormDef
|
||||||
from wcs.qommon.form import SingleSelectWidget, StringWidget, WidgetList, WysiwygTextWidget
|
from wcs.qommon.form import SingleSelectWidget, StringWidget, WidgetList, WysiwygTextWidget
|
||||||
from wcs.workflows import WorkflowStatusItem, register_item_class
|
from wcs.workflows import WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
|
|
||||||
|
|
||||||
class ResubmitWorkflowStatusItem(WorkflowStatusItem):
|
class ResubmitWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Resubmission')
|
description = _('Resubmission')
|
||||||
key = 'resubmit'
|
key = 'resubmit'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
endpoint = False
|
endpoint = False
|
||||||
|
|
|
@ -23,7 +23,7 @@ from wcs.api_utils import MissingSecret, get_secret_and_orig, sign_url
|
||||||
from wcs.roles import get_user_roles
|
from wcs.roles import get_user_roles
|
||||||
from wcs.workflows import WorkflowStatusItem, register_item_class
|
from wcs.workflows import WorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
from ..qommon import N_, _
|
from ..qommon import _
|
||||||
from ..qommon.form import SingleSelectWidgetWithOther
|
from ..qommon.form import SingleSelectWidgetWithOther
|
||||||
from ..qommon.ident.idp import is_idp_managing_user_attributes
|
from ..qommon.ident.idp import is_idp_managing_user_attributes
|
||||||
from ..qommon.misc import http_delete_request, http_post_request
|
from ..qommon.misc import http_delete_request, http_post_request
|
||||||
|
@ -47,7 +47,7 @@ def sign_ws_url(url):
|
||||||
|
|
||||||
|
|
||||||
class AddRoleWorkflowStatusItem(WorkflowStatusItem):
|
class AddRoleWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Role Addition')
|
description = _('Role Addition')
|
||||||
key = 'add_role'
|
key = 'add_role'
|
||||||
category = 'user-action'
|
category = 'user-action'
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ class AddRoleWorkflowStatusItem(WorkflowStatusItem):
|
||||||
get_logger().error('failed to add role %r to user %r', role, user)
|
get_logger().error('failed to add role %r to user %r', role, user)
|
||||||
|
|
||||||
if get_request():
|
if get_request():
|
||||||
get_response().add_after_job(str(N_('Adding role')), after_job)
|
get_response().add_after_job(_('Adding role'), after_job)
|
||||||
else:
|
else:
|
||||||
after_job()
|
after_job()
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ register_item_class(AddRoleWorkflowStatusItem)
|
||||||
|
|
||||||
|
|
||||||
class RemoveRoleWorkflowStatusItem(WorkflowStatusItem):
|
class RemoveRoleWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Role Removal')
|
description = _('Role Removal')
|
||||||
key = 'remove_role'
|
key = 'remove_role'
|
||||||
category = 'user-action'
|
category = 'user-action'
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ class RemoveRoleWorkflowStatusItem(WorkflowStatusItem):
|
||||||
get_logger().error('failed to remove role %r from user %r', role, user)
|
get_logger().error('failed to remove role %r from user %r', role, user)
|
||||||
|
|
||||||
if get_request():
|
if get_request():
|
||||||
get_response().add_after_job(str(N_('Removing role')), after_job)
|
get_response().add_after_job(_('Removing role'), after_job)
|
||||||
else:
|
else:
|
||||||
after_job()
|
after_job()
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,13 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
from wcs.qommon import N_
|
from wcs.qommon import _
|
||||||
|
|
||||||
from .jump import JumpWorkflowStatusItem, register_item_class
|
from .jump import JumpWorkflowStatusItem, register_item_class
|
||||||
|
|
||||||
|
|
||||||
class TimeoutWorkflowStatusItem(JumpWorkflowStatusItem):
|
class TimeoutWorkflowStatusItem(JumpWorkflowStatusItem):
|
||||||
description = N_('Change Status on Timeout')
|
description = _('Change Status on Timeout')
|
||||||
key = 'timeout'
|
key = 'timeout'
|
||||||
ok_in_global_action = False
|
ok_in_global_action = False
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ from wcs.workflows import (
|
||||||
)
|
)
|
||||||
from wcs.wscalls import call_webservice, get_app_error_code
|
from wcs.wscalls import call_webservice, get_app_error_code
|
||||||
|
|
||||||
from ..qommon import N_, _, force_str
|
from ..qommon import _, force_str
|
||||||
from ..qommon.errors import ConnectionError
|
from ..qommon.errors import ConnectionError
|
||||||
from ..qommon.form import (
|
from ..qommon.form import (
|
||||||
CheckboxWidget,
|
CheckboxWidget,
|
||||||
|
@ -110,7 +110,7 @@ class JournalWsCallErrorPart:
|
||||||
|
|
||||||
|
|
||||||
class WebserviceCallStatusItem(WorkflowStatusItem):
|
class WebserviceCallStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Webservice')
|
description = _('Webservice')
|
||||||
key = 'webservice_call'
|
key = 'webservice_call'
|
||||||
category = 'interaction'
|
category = 'interaction'
|
||||||
support_substitution_variables = True
|
support_substitution_variables = True
|
||||||
|
@ -248,7 +248,7 @@ class WebserviceCallStatusItem(WorkflowStatusItem):
|
||||||
attrs={
|
attrs={
|
||||||
'data-dynamic-display-child-of': '%smethod' % prefix,
|
'data-dynamic-display-child-of': '%smethod' % prefix,
|
||||||
'data-dynamic-display-value-in': '|'.join(
|
'data-dynamic-display-value-in': '|'.join(
|
||||||
[_(methods['POST']), _(methods['PUT']), _(methods['PATCH'])]
|
[str(_(methods['POST'])), str(_(methods['PUT'])), str(_(methods['PATCH']))]
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -262,7 +262,7 @@ class WebserviceCallStatusItem(WorkflowStatusItem):
|
||||||
attrs={
|
attrs={
|
||||||
'data-dynamic-display-child-of': '%smethod' % prefix,
|
'data-dynamic-display-child-of': '%smethod' % prefix,
|
||||||
'data-dynamic-display-value-in': '|'.join(
|
'data-dynamic-display-value-in': '|'.join(
|
||||||
[_(methods['POST']), _(methods['PUT']), _(methods['PATCH'])]
|
[str(_(methods['POST'])), str(_(methods['PUT'])), str(_(methods['PATCH']))]
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -36,7 +36,7 @@ from .fields import FileField
|
||||||
from .formdata import Evolution
|
from .formdata import Evolution
|
||||||
from .formdef import FormDef, FormdefImportError
|
from .formdef import FormDef, FormdefImportError
|
||||||
from .mail_templates import MailTemplate
|
from .mail_templates import MailTemplate
|
||||||
from .qommon import N_, _, emails, errors, ezt, force_str, get_cfg, get_logger, misc
|
from .qommon import _, emails, errors, ezt, force_str, get_cfg, get_logger, misc
|
||||||
from .qommon.form import (
|
from .qommon.form import (
|
||||||
CheckboxWidget,
|
CheckboxWidget,
|
||||||
ComputedExpressionWidget,
|
ComputedExpressionWidget,
|
||||||
|
@ -393,7 +393,7 @@ class Workflow(StorableObject):
|
||||||
StorableObject.__init__(self)
|
StorableObject.__init__(self)
|
||||||
self.name = name
|
self.name = name
|
||||||
self.possible_status = []
|
self.possible_status = []
|
||||||
self.roles = {'_receiver': _('Recipient')}
|
self.roles = {'_receiver': force_text(_('Recipient'))}
|
||||||
self.global_actions = []
|
self.global_actions = []
|
||||||
self.criticality_levels = []
|
self.criticality_levels = []
|
||||||
|
|
||||||
|
@ -401,7 +401,7 @@ class Workflow(StorableObject):
|
||||||
changed = False
|
changed = False
|
||||||
|
|
||||||
if 'roles' not in self.__dict__ or self.roles is None:
|
if 'roles' not in self.__dict__ or self.roles is None:
|
||||||
self.roles = {'_receiver': _('Recipient')}
|
self.roles = {'_receiver': force_text(_('Recipient'))}
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
for status in self.possible_status:
|
for status in self.possible_status:
|
||||||
|
@ -457,7 +457,7 @@ class Workflow(StorableObject):
|
||||||
form.rebuild()
|
form.rebuild()
|
||||||
|
|
||||||
if get_response():
|
if get_response():
|
||||||
get_response().add_after_job(N_('Reindexing cards and forms after workflow change'), update)
|
get_response().add_after_job(_('Reindexing cards and forms after workflow change'), update)
|
||||||
else:
|
else:
|
||||||
update()
|
update()
|
||||||
|
|
||||||
|
@ -670,7 +670,7 @@ class Workflow(StorableObject):
|
||||||
charset = get_publisher().site_charset
|
charset = get_publisher().site_charset
|
||||||
workflow = cls()
|
workflow = cls()
|
||||||
if tree.find('name') is None or not tree.find('name').text:
|
if tree.find('name') is None or not tree.find('name').text:
|
||||||
raise WorkflowImportError(N_('Missing name'))
|
raise WorkflowImportError(_('Missing name'))
|
||||||
|
|
||||||
# if the tree we get is actually a ElementTree for real, we get its
|
# if the tree we get is actually a ElementTree for real, we get its
|
||||||
# root element and go on happily.
|
# root element and go on happily.
|
||||||
|
@ -678,7 +678,7 @@ class Workflow(StorableObject):
|
||||||
tree = tree.getroot()
|
tree = tree.getroot()
|
||||||
|
|
||||||
if tree.tag != 'workflow':
|
if tree.tag != 'workflow':
|
||||||
raise WorkflowImportError(N_('Not a workflow'))
|
raise WorkflowImportError(_('Not a workflow'))
|
||||||
|
|
||||||
if include_id and tree.attrib.get('id'):
|
if include_id and tree.attrib.get('id'):
|
||||||
workflow.id = tree.attrib.get('id')
|
workflow.id = tree.attrib.get('id')
|
||||||
|
@ -807,18 +807,20 @@ class Workflow(StorableObject):
|
||||||
def get_default_workflow(cls):
|
def get_default_workflow(cls):
|
||||||
from .qommon.admin.emails import EmailsDirectory
|
from .qommon.admin.emails import EmailsDirectory
|
||||||
|
|
||||||
workflow = Workflow(name=_('Default'))
|
# force_text() is used on lazy gettext calls as the default workflow is used
|
||||||
|
# in tests as the basis for other ones and lazy gettext would fail pickling.
|
||||||
|
workflow = Workflow(name=force_text(_('Default')))
|
||||||
workflow.id = '_default'
|
workflow.id = '_default'
|
||||||
workflow.roles = {'_receiver': _('Recipient')}
|
workflow.roles = {'_receiver': force_text(_('Recipient'))}
|
||||||
just_submitted_status = workflow.add_status(_('Just Submitted'), 'just_submitted')
|
just_submitted_status = workflow.add_status(force_text(_('Just Submitted')), 'just_submitted')
|
||||||
just_submitted_status.visibility = ['_receiver']
|
just_submitted_status.visibility = ['_receiver']
|
||||||
new_status = workflow.add_status(_('New'), 'new')
|
new_status = workflow.add_status(force_text(_('New')), 'new')
|
||||||
new_status.colour = '66FF00'
|
new_status.colour = '66FF00'
|
||||||
rejected_status = workflow.add_status(_('Rejected'), 'rejected')
|
rejected_status = workflow.add_status(force_text(_('Rejected')), 'rejected')
|
||||||
rejected_status.colour = 'FF3300'
|
rejected_status.colour = 'FF3300'
|
||||||
accepted_status = workflow.add_status(_('Accepted'), 'accepted')
|
accepted_status = workflow.add_status(force_text(_('Accepted')), 'accepted')
|
||||||
accepted_status.colour = '66CCFF'
|
accepted_status.colour = '66CCFF'
|
||||||
finished_status = workflow.add_status(_('Finished'), 'finished')
|
finished_status = workflow.add_status(force_text(_('Finished')), 'finished')
|
||||||
finished_status.colour = 'CCCCCC'
|
finished_status.colour = 'CCCCCC'
|
||||||
|
|
||||||
commentable = CommentableWorkflowStatusItem()
|
commentable = CommentableWorkflowStatusItem()
|
||||||
|
@ -906,7 +908,7 @@ class Workflow(StorableObject):
|
||||||
|
|
||||||
accept = ChoiceWorkflowStatusItem()
|
accept = ChoiceWorkflowStatusItem()
|
||||||
accept.id = '_accept'
|
accept.id = '_accept'
|
||||||
accept.label = _('Accept')
|
accept.label = force_text(_('Accept'))
|
||||||
accept.by = ['_receiver']
|
accept.by = ['_receiver']
|
||||||
accept.status = accepted_status.id
|
accept.status = accepted_status.id
|
||||||
accept.parent = new_status
|
accept.parent = new_status
|
||||||
|
@ -914,7 +916,7 @@ class Workflow(StorableObject):
|
||||||
|
|
||||||
reject = ChoiceWorkflowStatusItem()
|
reject = ChoiceWorkflowStatusItem()
|
||||||
reject.id = '_reject'
|
reject.id = '_reject'
|
||||||
reject.label = _('Reject')
|
reject.label = force_text(_('Reject'))
|
||||||
reject.by = ['_receiver']
|
reject.by = ['_receiver']
|
||||||
reject.status = rejected_status.id
|
reject.status = rejected_status.id
|
||||||
reject.parent = new_status
|
reject.parent = new_status
|
||||||
|
@ -922,7 +924,7 @@ class Workflow(StorableObject):
|
||||||
|
|
||||||
finish = ChoiceWorkflowStatusItem()
|
finish = ChoiceWorkflowStatusItem()
|
||||||
finish.id = '_finish'
|
finish.id = '_finish'
|
||||||
finish.label = _('Finish')
|
finish.label = force_text(_('Finish'))
|
||||||
finish.by = ['_receiver']
|
finish.by = ['_receiver']
|
||||||
finish.status = finished_status.id
|
finish.status = finished_status.id
|
||||||
finish.parent = accepted_status
|
finish.parent = accepted_status
|
||||||
|
@ -1074,7 +1076,7 @@ class XmlSerialisable:
|
||||||
|
|
||||||
# if the roles are managed by the idp, don't try further.
|
# if the roles are managed by the idp, don't try further.
|
||||||
if get_publisher() and get_cfg('sp', {}).get('idp-manage-roles') is True:
|
if get_publisher() and get_cfg('sp', {}).get('idp-manage-roles') is True:
|
||||||
raise WorkflowImportError(N_('Unknown referenced role (%s)'), (value,))
|
raise WorkflowImportError(_('Unknown referenced role (%s)'), (value,))
|
||||||
|
|
||||||
# and if there's no match, create a new role
|
# and if there's no match, create a new role
|
||||||
role = get_publisher().role_class()
|
role = get_publisher().role_class()
|
||||||
|
@ -1916,7 +1918,7 @@ class WorkflowStatusItem(XmlSerialisable):
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
def render_as_line(self):
|
def render_as_line(self):
|
||||||
label = _(self.description)
|
label = self.description
|
||||||
details = self.get_line_details()
|
details = self.get_line_details()
|
||||||
if details:
|
if details:
|
||||||
label += ' (%s)' % details
|
label += ' (%s)' % details
|
||||||
|
@ -2199,7 +2201,7 @@ class WorkflowStatusItem(XmlSerialisable):
|
||||||
if not targets and formdata: # do not log in presentation context: formdata is needed
|
if not targets and formdata: # do not log in presentation context: formdata is needed
|
||||||
message = _(
|
message = _(
|
||||||
'reference to invalid status %(target)s in status %(status)s, ' 'action %(status_item)s'
|
'reference to invalid status %(target)s in status %(status)s, ' 'action %(status_item)s'
|
||||||
) % {'target': self.status, 'status': self.parent.name, 'status_item': _(self.description)}
|
) % {'target': self.status, 'status': self.parent.name, 'status_item': self.description}
|
||||||
get_publisher().record_error(message, formdata=formdata, status_item=self)
|
get_publisher().record_error(message, formdata=formdata, status_item=self)
|
||||||
|
|
||||||
return targets
|
return targets
|
||||||
|
@ -2212,9 +2214,9 @@ class WorkflowStatusItem(XmlSerialisable):
|
||||||
roles = self.parent.parent.render_list_of_roles(self.by)
|
roles = self.parent.parent.render_list_of_roles(self.by)
|
||||||
label += ' %s %s' % (_('by'), roles)
|
label += ' %s %s' % (_('by'), roles)
|
||||||
if getattr(self, 'status', None) == '_previous':
|
if getattr(self, 'status', None) == '_previous':
|
||||||
label += ' ' + _('(to last marker)')
|
label += ' ' + str(_('(to last marker)'))
|
||||||
if getattr(self, 'set_marker_on_status', False):
|
if getattr(self, 'set_marker_on_status', False):
|
||||||
label += ' ' + _('(and set marker)')
|
label += ' ' + str(_('(and set marker)'))
|
||||||
else:
|
else:
|
||||||
label = self.render_as_line()
|
label = self.render_as_line()
|
||||||
return label
|
return label
|
||||||
|
@ -2281,7 +2283,7 @@ class WorkflowStatusItem(XmlSerialisable):
|
||||||
value = xml_node_text(elem)
|
value = xml_node_text(elem)
|
||||||
mail_template = MailTemplate.get_by_slug(value)
|
mail_template = MailTemplate.get_by_slug(value)
|
||||||
if not mail_template:
|
if not mail_template:
|
||||||
raise WorkflowImportError(N_('Unknown referenced mail template (%s)'), (value,))
|
raise WorkflowImportError(_('Unknown referenced mail template (%s)'), (value,))
|
||||||
self.mail_template = value
|
self.mail_template = value
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -2471,7 +2473,7 @@ def register_item_class(klass):
|
||||||
|
|
||||||
|
|
||||||
class CommentableWorkflowStatusItem(WorkflowStatusItem):
|
class CommentableWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Comment')
|
description = _('Comment')
|
||||||
key = 'commentable'
|
key = 'commentable'
|
||||||
category = 'interaction'
|
category = 'interaction'
|
||||||
endpoint = False
|
endpoint = False
|
||||||
|
@ -2542,11 +2544,11 @@ class CommentableWorkflowStatusItem(WorkflowStatusItem):
|
||||||
super().add_parameters_widgets(form, parameters, prefix=prefix, formdef=formdef, **kwargs)
|
super().add_parameters_widgets(form, parameters, prefix=prefix, formdef=formdef, **kwargs)
|
||||||
if 'label' in parameters:
|
if 'label' in parameters:
|
||||||
if self.label is None:
|
if self.label is None:
|
||||||
self.label = _('Comment')
|
self.label = str(_('Comment'))
|
||||||
form.add(StringWidget, '%slabel' % prefix, size=40, title=_('Label'), value=self.label)
|
form.add(StringWidget, '%slabel' % prefix, size=40, title=_('Label'), value=self.label)
|
||||||
if 'button_label' in parameters:
|
if 'button_label' in parameters:
|
||||||
if self.button_label == 0:
|
if self.button_label == 0:
|
||||||
self.button_label = _('Add Comment')
|
self.button_label = str(_('Add Comment'))
|
||||||
form.add(
|
form.add(
|
||||||
StringWidget,
|
StringWidget,
|
||||||
'%sbutton_label' % prefix,
|
'%sbutton_label' % prefix,
|
||||||
|
@ -2610,7 +2612,7 @@ register_item_class(CommentableWorkflowStatusItem)
|
||||||
|
|
||||||
|
|
||||||
class ChoiceWorkflowStatusItem(WorkflowStatusJumpItem):
|
class ChoiceWorkflowStatusItem(WorkflowStatusJumpItem):
|
||||||
description = N_('Manual Jump')
|
description = _('Manual Jump')
|
||||||
key = 'choice'
|
key = 'choice'
|
||||||
endpoint = False
|
endpoint = False
|
||||||
waitpoint = True
|
waitpoint = True
|
||||||
|
@ -2642,7 +2644,7 @@ class ChoiceWorkflowStatusItem(WorkflowStatusJumpItem):
|
||||||
if self.label and to_status:
|
if self.label and to_status:
|
||||||
more = ''
|
more = ''
|
||||||
if self.set_marker_on_status:
|
if self.set_marker_on_status:
|
||||||
more += ' ' + _('(and set marker)')
|
more += ' ' + str(_('(and set marker)'))
|
||||||
if self.by:
|
if self.by:
|
||||||
return _('"%(label)s", to %(to)s, by %(by)s%(more)s') % {
|
return _('"%(label)s", to %(to)s, by %(by)s%(more)s') % {
|
||||||
'label': self.get_label(),
|
'label': self.get_label(),
|
||||||
|
@ -2749,7 +2751,7 @@ register_item_class(ChoiceWorkflowStatusItem)
|
||||||
|
|
||||||
|
|
||||||
class JumpOnSubmitWorkflowStatusItem(WorkflowStatusJumpItem):
|
class JumpOnSubmitWorkflowStatusItem(WorkflowStatusJumpItem):
|
||||||
description = N_('On Submit Jump')
|
description = _('On Submit Jump')
|
||||||
key = 'jumponsubmit'
|
key = 'jumponsubmit'
|
||||||
ok_in_global_action = False
|
ok_in_global_action = False
|
||||||
|
|
||||||
|
@ -2777,7 +2779,7 @@ register_item_class(JumpOnSubmitWorkflowStatusItem)
|
||||||
|
|
||||||
|
|
||||||
class SendmailWorkflowStatusItem(WorkflowStatusItem):
|
class SendmailWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Email')
|
description = _('Email')
|
||||||
key = 'sendmail'
|
key = 'sendmail'
|
||||||
category = 'interaction'
|
category = 'interaction'
|
||||||
support_substitution_variables = True
|
support_substitution_variables = True
|
||||||
|
@ -3061,7 +3063,7 @@ def template_on_context(context=None, template=None, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
class SendSMSWorkflowStatusItem(WorkflowStatusItem):
|
class SendSMSWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('SMS')
|
description = _('SMS')
|
||||||
key = 'sendsms'
|
key = 'sendsms'
|
||||||
category = 'interaction'
|
category = 'interaction'
|
||||||
support_substitution_variables = True
|
support_substitution_variables = True
|
||||||
|
@ -3129,7 +3131,7 @@ register_item_class(SendSMSWorkflowStatusItem)
|
||||||
|
|
||||||
|
|
||||||
class DisplayMessageWorkflowStatusItem(WorkflowStatusItem):
|
class DisplayMessageWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Alert')
|
description = _('Alert')
|
||||||
key = 'displaymsg'
|
key = 'displaymsg'
|
||||||
category = 'interaction'
|
category = 'interaction'
|
||||||
support_substitution_variables = True
|
support_substitution_variables = True
|
||||||
|
@ -3150,7 +3152,7 @@ class DisplayMessageWorkflowStatusItem(WorkflowStatusItem):
|
||||||
parts.append(_('with actions'))
|
parts.append(_('with actions'))
|
||||||
if self.to:
|
if self.to:
|
||||||
parts.append(_('for %s') % self.render_list_of_roles(self.to))
|
parts.append(_('for %s') % self.render_list_of_roles(self.to))
|
||||||
return ', '.join(parts)
|
return ', '.join([str(x) for x in parts])
|
||||||
|
|
||||||
def get_message(self, filled, position='top'):
|
def get_message(self, filled, position='top'):
|
||||||
if not (self.message and self.position == position and filled.is_for_current_user(self.to)):
|
if not (self.message and self.position == position and filled.is_for_current_user(self.to)):
|
||||||
|
@ -3234,7 +3236,7 @@ register_item_class(DisplayMessageWorkflowStatusItem)
|
||||||
|
|
||||||
|
|
||||||
class RedirectToStatusWorkflowStatusItem(WorkflowStatusItem):
|
class RedirectToStatusWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Status Page Redirection')
|
description = _('Status Page Redirection')
|
||||||
key = 'redirectstatus'
|
key = 'redirectstatus'
|
||||||
ok_in_global_action = False
|
ok_in_global_action = False
|
||||||
|
|
||||||
|
@ -3262,7 +3264,7 @@ class RedirectToStatusWorkflowStatusItem(WorkflowStatusItem):
|
||||||
|
|
||||||
|
|
||||||
class EditableWorkflowStatusItem(WorkflowStatusItem):
|
class EditableWorkflowStatusItem(WorkflowStatusItem):
|
||||||
description = N_('Edition')
|
description = _('Edition')
|
||||||
key = 'editable'
|
key = 'editable'
|
||||||
category = 'formdata-action'
|
category = 'formdata-action'
|
||||||
endpoint = False
|
endpoint = False
|
||||||
|
|
Loading…
Reference in New Issue