wcs/wcs/backoffice/cards.py

310 lines
12 KiB
Python

# -*- coding: utf-8 -*-
#
# w.c.s. - web application for online forms
# Copyright (C) 2005-2019 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
from quixote import get_publisher, get_response, get_session, redirect
from quixote.directory import Directory
from quixote.html import TemplateIO, htmltext
from ..qommon import _, N_
from ..qommon.misc import C_
from ..qommon.storage import NotEqual, Null
from wcs.carddef import CardDef
from wcs.categories import CardDefCategory
from wcs.roles import Role
from wcs.workflows import Workflow
from wcs.admin.categories import CardDefCategoriesDirectory
from wcs.admin.forms import FormsDirectory, FormDefPage, FormDefUI, html_top, OptionsDirectory
from wcs.admin.logged_errors import LoggedErrorsDirectory
from wcs.admin import utils
class CardDefUI(FormDefUI):
formdef_class = CardDef
category_class = CardDefCategory
class CardDefOptionsDirectory(OptionsDirectory):
category_class = CardDefCategory
category_empty_choice = N_('Select a category for this card model')
class CardDefPage(FormDefPage):
formdef_class = CardDef
formdef_export_prefix = 'card'
formdef_ui_class = CardDefUI
formdef_default_workflow = '_carddef_default'
options_directory_class = CardDefOptionsDirectory
delete_message = N_('You are about to irrevocably delete this card model.')
delete_title = N_('Deleting Card Model:')
overwrite_message = N_(
'You can replace this card model by uploading a file '
'or by pointing to a form URL.')
overwrite_success_message = N_(
'The card model has been successfully overwritten. '
'Do note it kept its existing address and role and workflow parameters.')
def html_top(self, title):
return html_top('cards', title)
def _q_index(self):
self.html_top(title=self.formdef.name)
r = TemplateIO(html=True)
get_response().filter['sidebar'] = self.get_sidebar()
get_response().add_javascript(['jquery.js', 'widget_list.js', 'qommon.wysiwyg.js'])
r += htmltext('<div id="appbar">')
r += htmltext('<h2>%s</h2>') % self.formdef.name
if not self.formdef.is_readonly():
r += htmltext('<span class="actions">')
r += htmltext('<a rel="popup" href="title">%s</a>') % _('change title')
r += htmltext('</span>')
r += htmltext('</div>')
r += utils.last_modification_block(obj=self.formdef)
r += get_session().display_message()
def add_option_line(link, label, current_value):
return htmltext(
'<li><a rel="popup" href="%(link)s">'
'<span class="label">%(label)s</span> '
'<span class="value">%(current_value)s</span>'
'</a></li>' % {
'link': link,
'label': label,
'current_value': current_value})
r += htmltext('<div class="bo-block">')
r += htmltext('<h3>%s</h3>') % _('Information')
r += htmltext('<ul class="biglist optionslist">')
r += add_option_line(
'options/category', _('Category'),
self.formdef.category_id and self.formdef.category and
self.formdef.category.name or C_('category|None'))
r += htmltext('</ul>')
r += htmltext('</div>')
r += htmltext('<div class="splitcontent-left">')
r += htmltext('<div class="bo-block">')
r += htmltext('<h3>%s</h3>') % _('Workflow')
r += htmltext('<ul class="biglist optionslist">')
if get_publisher().get_backoffice_root().is_accessible('workflows'):
# custom option line to also include a link to the workflow itself.
r += htmltext(
'<li><a rel="popup" href="%(link)s">'
'<span class="label">%(label)s</span> '
'<span class="value offset">%(current_value)s</span>'
'</a>'
'<a class="extra-link" title="%(title)s" href="%(workflow_url)s">↗</a>'
'</li>' % {
'link': 'workflow',
'label': _('Workflow'),
'title': _('Open workflow page'),
'workflow_url': self.formdef.workflow.get_admin_url(),
'current_value': self.formdef.workflow.name or '-'})
else:
r += add_option_line('workflow', _('Workflow'),
self.formdef.workflow and self.formdef.workflow.name or '-')
if self.formdef.workflow_id:
pristine_workflow = Workflow.get(self.formdef.workflow_id)
if pristine_workflow.variables_formdef:
r += add_option_line('workflow-variables', _('Options'), '')
r += add_option_line('backoffice-submission-roles',
_('Creation Role'),
self._get_roles_label('backoffice_submission_roles'))
if self.formdef.workflow.roles:
if not self.formdef.workflow_roles:
self.formdef.workflow_roles = {}
for (wf_role_id, wf_role_label) in self.formdef.workflow.roles.items():
role_id = self.formdef.workflow_roles.get(wf_role_id)
if role_id:
try:
role = Role.get(role_id)
role_label = role.name
except KeyError:
# removed role ?
role_label = _('Unknown role (%s)') % role_id
else:
role_label = '-'
r += add_option_line('role/%s' % wf_role_id,
wf_role_label, role_label)
r += htmltext('</ul>')
r += htmltext('</div>')
r += htmltext('</div>')
r += htmltext('<div class="splitcontent-right">')
r += htmltext('<div class="bo-block">')
r += htmltext('<h3>%s</h3>') % _('Options')
r += htmltext('<ul class="biglist optionslist">')
r += add_option_line('options/geolocations',
_('Geolocation'),
self.formdef.geolocations and
C_('geolocation|Enabled') or C_('geolocation|Disabled'))
if self.formdef.digest_template:
digest_template_status = C_('template|Custom')
else:
digest_template_status = C_('template|None')
r += add_option_line('options/templates',
_('Digest Template'), digest_template_status)
if self.formdef.user_support == 'optional':
user_support_status = C_('user_support|Optional')
else:
user_support_status = C_('user_support|No')
r += add_option_line('options/user_support', _('User support'), user_support_status)
r += htmltext('</ul>')
r += htmltext('</div>')
r += htmltext('</div>')
r += htmltext('<div class="bo-block clear">')
r += htmltext('<h3 class="clear">%s <span class="change">(<a href="fields/">%s</a>)</span></h3>') % (
_('Fields'), _('edit'))
r += self.get_preview()
r += htmltext('</div>')
formdefs = list(self.formdef.usage_in_formdefs())
if formdefs:
formdefs.sort(key=lambda x: x.name.lower())
r += htmltext('<div class="bo-block">')
r += htmltext('<h3>%s</h3>') % _('Forms')
r += htmltext('<p>%s ') % _('This card model is used as data source in the following forms:')
r += htmltext(', ').join([htmltext('<a href="%s">%s</a>') % (x.get_admin_url(), x.name) for x in formdefs])
r += htmltext('</p>')
r += htmltext('</div>')
return r.getvalue()
def duplicate(self):
response = super(CardDefPage, self).duplicate()
self.formdefui.formdef.disabled = False
self.formdefui.formdef.store()
return response
def get_sidebar(self):
r = TemplateIO(html=True)
if self.formdef.is_readonly():
r += htmltext('<div class="infonotice"><p>%s</p></div>') % _('This card model is readonly.')
return r.getvalue()
r += htmltext('<ul id="sidebar-actions">')
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete')
r += htmltext('<li><a href="duplicate">%s</a></li>') % _('Duplicate')
r += htmltext('<li><a rel="popup" href="overwrite">%s</a></li>') % _(
'Overwrite with new import')
r += htmltext('<li><a href="export">%s</a></li>') % _('Export')
if get_publisher().snapshot_class:
r += htmltext('<li><a rel="popup" href="history/save">%s</a></li>') % _('Save snapshot')
r += htmltext('<li><a href="history/">%s</a></li>') % _('History')
r += htmltext('</ul>')
r += LoggedErrorsDirectory.errors_block(formdef_class=self.formdef_class, formdef_id=self.formdef.id)
return r.getvalue()
def get_check_count_before_deletion_message(self):
if not get_publisher().is_using_postgresql():
return None
criterias = [
NotEqual('status', 'draft'),
Null('anonymised'),
]
if self.formdef.data_class().count(criterias):
return _('Deletion is not possible as there are cards.')
class CardsDirectory(FormsDirectory):
_q_exports = ['', 'new', ('import', 'p_import'), 'categories']
category_class = CardDefCategory
categories = CardDefCategoriesDirectory()
formdef_class = CardDef
formdef_page_class = CardDefPage
formdef_ui_class = CardDefUI
top_title = N_('Card Models')
import_title = N_('Import Card Model')
import_submit_label = N_('Import Card Model')
import_paragraph = N_(
'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_success_message = N_(
'This card model has been successfully imported. ')
import_error_message = N_(
'Imported card model contained errors and has been automatically fixed, '
'you should nevertheless check everything is ok. ')
def html_top(self, title):
return html_top('cards', title)
def _q_traverse(self, path):
get_response().breadcrumb.append(('cards/', _('Card Models')))
return Directory._q_traverse(self, path)
def form_actions(self):
r = TemplateIO(html=True)
r += htmltext('<div id="appbar">')
r += htmltext('<h2>%s</h2>') % _('Card Models')
r += htmltext('<span class="actions">')
r += htmltext('<a href="categories/">%s</a>') % _('Categories')
r += htmltext('<a href="import" rel="popup">%s</a>') % _('Import')
r += htmltext('<a class="new-item" href="new" rel="popup">%s</a>') % _('New Card Model')
r += htmltext('</span>')
r += htmltext('</div>')
return r.getvalue()
def new(self):
get_response().breadcrumb.append(('new', _('New')))
formdefui = self.formdef_ui_class(None)
form = formdefui.new_form_ui()
if form.get_widget('cancel').parse():
return redirect('.')
if form.is_submitted() and not form.has_errors():
try:
formdef = formdefui.submit_form(form)
formdef.disabled = False
formdef.store()
except ValueError:
pass
else:
return redirect(str(formdef.id) + '/')
self.html_top(title=_('New Card Model'))
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % _('New Card Model')
r += form.render()
return r.getvalue()
def import_submit(self, form):
response = super(CardsDirectory, self).import_submit(form)
if self.imported_formdef:
self.imported_formdef.disabled = False
self.imported_formdef.store()
return response
def _q_lookup(self, component):
return self.formdef_page_class(component)