wcs/wcs/admin/wscalls.py

288 lines
10 KiB
Python

# w.c.s. - web application for online forms
# Copyright (C) 2005-2016 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 xml.etree.ElementTree as ET
from quixote import get_publisher
from quixote import get_response
from quixote import get_session
from quixote import redirect
from quixote.directory import Directory
from quixote.html import TemplateIO
from quixote.html import htmltext
from wcs.admin import utils
from wcs.backoffice.snapshots import SnapshotsDirectory
from wcs.qommon import _
from wcs.qommon import errors
from wcs.qommon import force_str
from wcs.qommon import misc
from wcs.qommon import template
from wcs.qommon.backoffice.menu import html_top
from wcs.qommon.form import CheckboxWidget
from wcs.qommon.form import FileWidget
from wcs.qommon.form import Form
from wcs.qommon.form import HtmlWidget
from wcs.qommon.form import StringWidget
from wcs.qommon.form import TextWidget
from wcs.wscalls import NamedWsCall
from wcs.wscalls import WsCallRequestWidget
class NamedWsCallUI:
def __init__(self, wscall):
self.wscall = wscall
if self.wscall is None:
self.wscall = NamedWsCall()
def get_form(self):
form = Form(enctype='multipart/form-data', advanced_label=_('Additional options'))
form.add(StringWidget, 'name', title=_('Name'), required=True, size=30, value=self.wscall.name)
form.add(
TextWidget, 'description', title=_('Description'), cols=40, rows=5, value=self.wscall.description
)
if self.wscall.slug:
form.add(
StringWidget,
'slug',
value=self.wscall.slug,
title=_('Identifier'),
hint=_('Beware it is risky to change it'),
required=True,
advanced=True,
)
form.add(WsCallRequestWidget, 'request', value=self.wscall.request, title=_('Request'), required=True)
form.add(
CheckboxWidget,
'notify_on_errors',
title=_('Notify on errors'),
value=self.wscall.notify_on_errors,
)
form.add(
CheckboxWidget,
'record_on_errors',
title=_('Record on errors'),
value=self.wscall.record_on_errors if self.wscall.slug else True,
)
if not self.wscall.is_readonly():
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
return form
def submit_form(self, form):
name = form.get_widget('name').parse()
if self.wscall.slug:
slug = form.get_widget('slug').parse()
else:
slug = None
for wscall in NamedWsCall.select():
if wscall.id == self.wscall.id:
continue
if name == wscall.name:
form.get_widget('name').set_error(_('This name is already used.'))
if slug == wscall.slug:
form.get_widget('slug').set_error(_('This value is already used.'))
if form.has_errors():
raise ValueError()
self.wscall.name = name
self.wscall.description = form.get_widget('description').parse()
self.wscall.notify_on_errors = form.get_widget('notify_on_errors').parse()
self.wscall.record_on_errors = form.get_widget('record_on_errors').parse()
self.wscall.request = form.get_widget('request').parse()
if self.wscall.slug:
self.wscall.slug = slug
self.wscall.store()
class NamedWsCallPage(Directory):
_q_exports = [
'',
'edit',
'delete',
'export',
('history', 'snapshots_dir'),
]
def __init__(self, component, instance=None):
try:
self.wscall = instance or NamedWsCall.get(component)
except KeyError:
raise errors.TraversalError()
self.wscall_ui = NamedWsCallUI(self.wscall)
get_response().breadcrumb.append((component + '/', self.wscall.name))
self.snapshots_dir = SnapshotsDirectory(self.wscall)
def get_sidebar(self):
r = TemplateIO(html=True)
if self.wscall.is_readonly():
r += htmltext('<div class="infonotice"><p>%s</p></div>') % _('This webservice call is readonly.')
r += utils.snapshot_info_block(snapshot=self.wscall.snapshot_object)
r += htmltext('<ul id="sidebar-actions">')
if not self.wscall.is_readonly():
r += htmltext('<li><a href="export">%s</a></li>') % _('Export')
r += htmltext('<li><a href="delete" rel="popup">%s</a></li>') % _('Delete')
if get_publisher().snapshot_class:
r += htmltext('<li><a rel="popup" href="history/save">%s</a></li>') % _('Save snapshot')
r += htmltext('<li><a href="history/">%s</a></li>') % _('History')
r += htmltext('</ul>')
return r.getvalue()
def _q_index(self):
html_top('wscalls', title=self.wscall.name)
get_response().filter['sidebar'] = self.get_sidebar()
return template.QommonTemplateResponse(
templates=['wcs/backoffice/wscall.html'], context={'view': self, 'wscall': self.wscall}
)
def edit(self):
form = self.wscall_ui.get_form()
if form.get_submit() == 'cancel':
return redirect('.')
if form.get_submit() == 'submit' and not form.has_errors():
try:
self.wscall_ui.submit_form(form)
except ValueError:
pass
else:
return redirect('../%s/' % self.wscall.id)
get_response().breadcrumb.append(('edit', _('Edit')))
html_top('wscalls', title=_('Edit webservice call'))
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % _('Edit webservice call')
r += form.render()
return r.getvalue()
def delete(self):
form = Form(enctype='multipart/form-data')
form.widgets.append(
HtmlWidget('<p>%s</p>' % _('You are about to irrevocably delete this webservice call.'))
)
form.add_submit('delete', _('Delete'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
get_response().breadcrumb.append(('delete', _('Delete')))
html_top('wscalls', title=_('Delete webservice call'))
r = TemplateIO(html=True)
r += htmltext('<h2>%s %s</h2>') % (_('Deleting webservice call:'), self.wscall.name)
r += form.render()
return r.getvalue()
else:
self.wscall.remove_self()
return redirect('..')
def export(self):
x = self.wscall.export_to_xml(include_id=True)
misc.indent_xml(x)
response = get_response()
response.set_content_type('application/x-wcs-wscall')
response.set_header('content-disposition', 'attachment; filename=wscall-%s.wcs' % self.wscall.slug)
return '<?xml version="1.0"?>\n' + force_str(ET.tostring(x))
class NamedWsCallsDirectory(Directory):
_q_exports = ['', 'new', ('import', 'p_import')]
def _q_traverse(self, path):
get_response().breadcrumb.append(('wscalls/', _('Webservice Calls')))
return super()._q_traverse(path)
def _q_index(self):
html_top('wscalls', title=_('Webservice Calls'))
return template.QommonTemplateResponse(
templates=['wcs/backoffice/wscalls.html'],
context={'view': self, 'wscalls': NamedWsCall.select(order_by='name')},
)
def new(self):
get_response().breadcrumb.append(('new', _('New')))
wscall_ui = NamedWsCallUI(None)
form = wscall_ui.get_form()
if form.get_widget('cancel').parse():
return redirect('.')
if form.get_submit() == 'submit' and not form.has_errors():
try:
wscall_ui.submit_form(form)
except ValueError:
pass
else:
return redirect('.')
html_top('wscalls', title=_('New webservice call'))
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % _('New webservice call')
r += form.render()
return r.getvalue()
def _q_lookup(self, component):
return NamedWsCallPage(component)
def p_import(self):
form = Form(enctype='multipart/form-data')
import_title = _('Import webservice call')
form.add(FileWidget, 'file', title=_('File'), required=True)
form.add_submit('submit', import_title)
form.add_submit('cancel', _('Cancel'))
if form.get_submit() == 'cancel':
return redirect('.')
if form.is_submitted() and not form.has_errors():
try:
return self.import_submit(form)
except ValueError:
pass
get_response().breadcrumb.append(('import', _('Import')))
html_top('wscalls', title=import_title)
r = TemplateIO(html=True)
r += htmltext('<h2>%s</h2>') % import_title
r += htmltext('<p>%s</p>') % _('You can install a new webservice call by uploading a file.')
r += form.render()
return r.getvalue()
def import_submit(self, form):
fp = form.get_widget('file').parse().fp
error = False
try:
wscall = NamedWsCall.import_from_xml(fp)
get_session().message = ('info', _('This webservice call has been successfully imported.'))
except ValueError:
error = True
if error:
form.set_error('file', _('Invalid File'))
raise ValueError()
try:
# check slug unicity
NamedWsCall.get(wscall.slug, ignore_migration=True)
except KeyError:
pass
else:
wscall.slug = None # a new one will be set in .store()
wscall.store()
return redirect('%s/' % wscall.id)