294 lines
13 KiB
Python
294 lines
13 KiB
Python
# w.c.s. - web application for online forms
|
|
# Copyright (C) 2005-2022 Entr'ouvert
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import datetime
|
|
import json
|
|
import os
|
|
import re
|
|
|
|
from quixote import get_publisher, get_request, get_response, redirect
|
|
from quixote.directory import Directory
|
|
|
|
from wcs.data_sources import NamedDataSource
|
|
from wcs.formdef import get_formdefs_of_all_kinds
|
|
from wcs.qommon import _, ezt, template
|
|
from wcs.qommon.afterjobs import AfterJob
|
|
from wcs.wf.export_to_model import UploadValidationError
|
|
from wcs.workflows import Workflow
|
|
from wcs.wscalls import NamedWsCall
|
|
|
|
|
|
class DeprecationsDirectory(Directory):
|
|
do_not_call_in_templates = True
|
|
_q_exports = ['', 'scan']
|
|
|
|
def _q_index(self):
|
|
report_path = os.path.join(get_publisher().app_dir, 'deprecations.json')
|
|
if not os.path.exists(report_path):
|
|
# create report if necessary
|
|
return self.scan()
|
|
|
|
get_response().set_title(_('Deprecations Report'))
|
|
get_response().breadcrumb.append(('deprecations/', _('Deprecations Report')))
|
|
|
|
context = {'has_sidebar': False, 'view': self}
|
|
with open(report_path) as fd:
|
|
context['report'] = json.load(fd)
|
|
context['report']['report_lines'].sort(key=lambda x: x['category'])
|
|
return template.QommonTemplateResponse(
|
|
templates=['wcs/backoffice/deprecations.html'], context=context, is_django_native=True
|
|
)
|
|
|
|
def scan(self):
|
|
job = get_response().add_after_job(
|
|
DeprecationsScanAfterJob(
|
|
label=_('Scanning for deprecations'),
|
|
user_id=get_request().user.id,
|
|
return_url='/backoffice/studio/deprecations/',
|
|
)
|
|
)
|
|
job.store()
|
|
return redirect(job.get_processing_url())
|
|
|
|
@property
|
|
def titles(self):
|
|
return {
|
|
'ezt': _('EZT text'),
|
|
'jsonp': _('JSONP data source'),
|
|
'python-condition': _('Python condition'),
|
|
'python-expression': _('Python expression'),
|
|
'python-prefill': _('Python prefill'),
|
|
'python-data-source': _('Python data source'),
|
|
'rtf': _('RTF Documents'),
|
|
'script': _('Filesystem Script'),
|
|
'fields': _('Obsolete field types'),
|
|
'actions': _('Obsolete action types'),
|
|
}
|
|
|
|
@property
|
|
def short_docs(self):
|
|
return {
|
|
'ezt': _('Use Django templates.'),
|
|
'jsonp': _('Use JSON sources with id and query parameters.'),
|
|
'python-condition': _('Use Django condition.'),
|
|
'python-expression': _('Use Django templates.'),
|
|
'python-prefill': _('Use Django templates.'),
|
|
'python-data-source': _('Use cards.'),
|
|
'rtf': _('Use OpenDocument format.'),
|
|
'script': _('Use a dedicated template tags application.'),
|
|
'fields': _('Use block fields to replace tables and ranked order fields.'),
|
|
'actions': '',
|
|
}
|
|
|
|
@property
|
|
def help_urls(self):
|
|
return {
|
|
'ezt': 'https://doc-publik.entrouvert.com/admin-fonctionnel/elements-deprecies/',
|
|
'jsonp': 'https://doc-publik.entrouvert.com/admin-fonctionnel/elements-deprecies/',
|
|
'python-condition': 'https://doc-publik.entrouvert.com/admin-fonctionnel/elements-deprecies/',
|
|
'python-expression': 'https://doc-publik.entrouvert.com/admin-fonctionnel/elements-deprecies/',
|
|
'python-prefill': 'https://doc-publik.entrouvert.com/admin-fonctionnel/elements-deprecies/',
|
|
'python-data-source': 'https://doc-publik.entrouvert.com/admin-fonctionnel/elements-deprecies/',
|
|
'rtf': 'https://doc-publik.entrouvert.com/admin-fonctionnel/elements-deprecies/',
|
|
'script': 'https://doc-publik.entrouvert.com/admin-fonctionnel/elements-deprecies/',
|
|
'fields': 'https://doc-publik.entrouvert.com/admin-fonctionnel/elements-deprecies/',
|
|
'actions': 'https://doc-publik.entrouvert.com/admin-fonctionnel/elements-deprecies/',
|
|
}
|
|
|
|
|
|
class DeprecationsScanAfterJob(AfterJob):
|
|
def done_action_url(self):
|
|
return self.kwargs['return_url']
|
|
|
|
def done_action_label(self):
|
|
return _('Go to deprecation report')
|
|
|
|
def done_button_attributes(self):
|
|
return {'data-redirect-auto': 'true'}
|
|
|
|
def execute(self):
|
|
self.report_lines = []
|
|
formdefs = get_formdefs_of_all_kinds()
|
|
workflows = Workflow.select(ignore_errors=True, ignore_migration=True)
|
|
named_data_sources = NamedDataSource.select(ignore_errors=True, ignore_migration=True)
|
|
named_ws_calls = NamedWsCall.select(ignore_errors=True, ignore_migration=True)
|
|
# extra step to build report file
|
|
self.total_count = len(formdefs) + len(workflows) + len(named_data_sources) + len(named_ws_calls) + 1
|
|
self.store()
|
|
|
|
for formdef in formdefs:
|
|
for field in formdef.fields or []:
|
|
location_label = _('%(name)s / Field "%(label)s"') % {
|
|
'name': formdef.name,
|
|
'label': field.ellipsized_label,
|
|
}
|
|
url = formdef.get_field_admin_url(field)
|
|
self.check_data_source(
|
|
getattr(field, 'data_source', None), location_label=location_label, url=url
|
|
)
|
|
prefill = getattr(field, 'prefill', None)
|
|
if prefill:
|
|
if prefill.get('type') == 'formula':
|
|
self.add_report_line(
|
|
location_label=location_label,
|
|
url=url,
|
|
category='python-prefill',
|
|
)
|
|
else:
|
|
self.check_string(
|
|
prefill.get('value'), location_label=location_label, url=url, python_check=False
|
|
)
|
|
if field.type == 'page':
|
|
for condition in field.get_conditions():
|
|
if condition and condition.get('type') == 'python':
|
|
self.add_report_line(
|
|
location_label=location_label,
|
|
url=url,
|
|
category='python-condition',
|
|
)
|
|
break
|
|
if field.type in ('title', 'subtitle', 'comment'):
|
|
self.check_string(field.label, location_label=location_label, url=url, python_check=False)
|
|
if field.type in ('table', 'table-select', 'tablerows', 'ranked-items'):
|
|
self.add_report_line(
|
|
location_label=location_label,
|
|
url=url,
|
|
category='fields',
|
|
)
|
|
|
|
self.increment_count()
|
|
|
|
for workflow in workflows:
|
|
for action in workflow.get_all_items():
|
|
location_label = '%s / %s' % (workflow.name, action.description)
|
|
url = action.get_admin_url()
|
|
for string in action.get_computed_strings():
|
|
self.check_string(string, location_label=location_label, url=url)
|
|
if getattr(action, 'condition', None):
|
|
if action.condition.get('type') == 'python':
|
|
self.add_report_line(
|
|
location_label=location_label,
|
|
url=url,
|
|
category='python-condition',
|
|
css_class='important' if (action.key == 'jump' and action.timeout) else '',
|
|
)
|
|
if action.key == 'export_to_model':
|
|
try:
|
|
kind = action.model_file_validation(action.model_file)
|
|
except UploadValidationError:
|
|
pass
|
|
else:
|
|
if kind == 'rtf':
|
|
self.add_report_line(location_label=location_label, url=url, category='rtf')
|
|
if action.key in ('aggregationemail',):
|
|
self.add_report_line(location_label=location_label, url=url, category='actions')
|
|
if action.key in ('register-comment', 'sendmail'):
|
|
for attachment in getattr(action, 'attachments', None) or []:
|
|
if attachment and not ('{%' in attachment or '{{' in attachment):
|
|
self.add_report_line(
|
|
location_label=location_label, url=url, category='python-expression'
|
|
)
|
|
break
|
|
|
|
for global_action in workflow.global_actions or []:
|
|
location_label = '%s / %s' % (workflow.name, _('trigger in %s') % global_action.name)
|
|
for trigger in global_action.triggers or []:
|
|
url = '%striggers/%s/' % (global_action.get_admin_url(), trigger.id)
|
|
if trigger.key == 'timeout' and trigger.anchor == 'python':
|
|
self.add_report_line(
|
|
location_label=location_label, url=url, category='python-expression'
|
|
)
|
|
break
|
|
|
|
self.increment_count()
|
|
|
|
for named_data_source in named_data_sources:
|
|
location_label = _('%(title)s "%(name)s"') % {
|
|
'title': _('Data source'),
|
|
'name': named_data_source.name,
|
|
}
|
|
url = named_data_source.get_admin_url()
|
|
|
|
self.check_data_source(
|
|
getattr(named_data_source, 'data_source', None), location_label=location_label, url=url
|
|
)
|
|
self.increment_count()
|
|
|
|
for named_ws_call in named_ws_calls:
|
|
location_label = _('%(title)s "%(name)s"') % {
|
|
'title': _('Webservice'),
|
|
'name': named_ws_call.name,
|
|
}
|
|
url = named_ws_call.get_admin_url()
|
|
for string in named_ws_call.get_computed_strings():
|
|
self.check_string(string, location_label=location_label, url=url)
|
|
self.increment_count()
|
|
|
|
self.build_report_file()
|
|
self.increment_count()
|
|
|
|
def check_data_source(self, data_source, location_label, url):
|
|
if not data_source:
|
|
return
|
|
if data_source.get('type') == 'jsonp':
|
|
self.add_report_line(
|
|
location_label=location_label,
|
|
url=url,
|
|
category='jsonp',
|
|
)
|
|
if data_source.get('type') == 'formula':
|
|
self.add_report_line(
|
|
location_label=location_label,
|
|
url=url,
|
|
category='python-data-source',
|
|
)
|
|
if data_source.get('type') == 'json':
|
|
self.check_string(data_source.get('value'), location_label, url, python_check=False)
|
|
|
|
def check_string(self, string, location_label, url, python_check=True):
|
|
if not isinstance(string, str):
|
|
return
|
|
if python_check and string.startswith('='): # python expression
|
|
self.add_report_line(location_label=location_label, url=url, category='python-expression')
|
|
else:
|
|
if template.Template(string).format == 'ezt':
|
|
try:
|
|
ezt.Template().parse(string)
|
|
except ezt.EZTException:
|
|
pass
|
|
else:
|
|
if not re.match(r'\[[^]]*[A-Z][^]]*\]', string):
|
|
# don't warn on leading [] expression if it has uppercases,
|
|
# this typically happens as initial "tag" in an email subjet.
|
|
self.add_report_line(location_label=location_label, url=url, category='ezt')
|
|
if re.findall(r'\Wscript\.\w', string):
|
|
self.add_report_line(location_label=location_label, url=url, category='script')
|
|
|
|
def add_report_line(self, **kwargs):
|
|
if kwargs not in self.report_lines:
|
|
self.report_lines.append(kwargs)
|
|
|
|
def build_report_file(self):
|
|
with open(os.path.join(get_publisher().app_dir, 'deprecations.json'), 'w') as fd:
|
|
json.dump(
|
|
{
|
|
'now': datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
|
|
'report_lines': self.report_lines,
|
|
},
|
|
fd,
|
|
indent=2,
|
|
)
|