backoffice: remove user view (#56534)
This commit is contained in:
parent
c05735d66d
commit
e704234efb
|
@ -3750,112 +3750,6 @@ def test_menu_json(pub):
|
|||
assert resp.headers['content-type'] == 'application/javascript'
|
||||
|
||||
|
||||
def test_per_user_view(pub):
|
||||
if not pub.is_using_postgresql():
|
||||
pytest.skip('this requires SQL')
|
||||
return
|
||||
|
||||
user = create_user(pub)
|
||||
create_environment(pub)
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/management/').follow()
|
||||
assert 'Per User View' not in resp.text
|
||||
|
||||
if not pub.site_options.has_section('options'):
|
||||
pub.site_options.add_section('options')
|
||||
pub.site_options.set('options', 'per-user-view', 'true')
|
||||
with open(os.path.join(pub.app_dir, 'site-options.cfg'), 'w') as fd:
|
||||
pub.site_options.write(fd)
|
||||
|
||||
resp = app.get('/backoffice/management/').follow()
|
||||
assert 'Per User View' in resp.text
|
||||
resp = resp.click(' User View')
|
||||
assert 'Use the search field on the right' in resp.text
|
||||
|
||||
resp.form['q'] = 'admin'
|
||||
resp = resp.form.submit()
|
||||
|
||||
assert resp.text.count('<tr') == 2 # header + user
|
||||
|
||||
form_class = FormDef.get_by_urlname('form-title').data_class()
|
||||
to_match = []
|
||||
for formdata in form_class.select():
|
||||
if formdata.data['1'] in ('FOO BAR 30', 'FOO BAR 33'):
|
||||
formdata.user_id = user.id
|
||||
formdata.store()
|
||||
to_match.append('/management/form-title/%s/' % formdata.id)
|
||||
|
||||
resp = app.get('/backoffice/management/users/%s/' % user.id)
|
||||
for item in to_match:
|
||||
assert item in resp.text
|
||||
count_li = resp.text.count('<li')
|
||||
|
||||
# check list items are displayed, without links, if we cannot view them.
|
||||
formdef = FormDef.get_by_urlname('form-title')
|
||||
formdef.workflow_roles['_receiver'] = 'XXX'
|
||||
formdef.store()
|
||||
formdef.data_class().rebuild_security()
|
||||
|
||||
resp = app.get('/backoffice/management/users/%s/' % user.id)
|
||||
for item in to_match:
|
||||
assert item not in resp.text # not linked
|
||||
assert resp.text.count('<li') == count_li
|
||||
|
||||
# mark formdef so it's not listed in per-user view
|
||||
formdef.skip_from_360_view = True
|
||||
formdef.store()
|
||||
|
||||
# check formdatas are no longer part of the page
|
||||
resp = app.get('/backoffice/management/users/%s/' % user.id)
|
||||
assert resp.text.count('<li') == (count_li - 2)
|
||||
|
||||
|
||||
def test_per_user_view_tracking_code(pub, emails, sms_mocking):
|
||||
if not pub.is_using_postgresql():
|
||||
pytest.skip('this requires SQL')
|
||||
return
|
||||
|
||||
user = create_user(pub)
|
||||
create_environment(pub)
|
||||
app = login(get_app(pub))
|
||||
|
||||
formdef = FormDef.get_by_urlname('form-title')
|
||||
form_class = formdef.data_class()
|
||||
number31 = [x for x in form_class.select() if x.data['1'] == 'FOO BAR 30'][0]
|
||||
number31.user_id = user.id
|
||||
number31.store()
|
||||
|
||||
resp = app.get('/backoffice/management/users/%s/' % user.id)
|
||||
assert 'Send tracking code' not in resp.text
|
||||
|
||||
formdef.enable_tracking_codes = True
|
||||
formdef.store()
|
||||
user_view_resp = app.get('/backoffice/management/users/%s/' % user.id)
|
||||
assert 'Send tracking code' in user_view_resp.text
|
||||
|
||||
pub.cfg['sms'] = {}
|
||||
pub.write_cfg()
|
||||
resp = user_view_resp.click('Send tracking code')
|
||||
assert 'sms' not in resp.form.fields
|
||||
assert resp.form['email'].value == user.email
|
||||
|
||||
resp = resp.form.submit()
|
||||
resp = resp.follow()
|
||||
assert emails.count() == 1
|
||||
assert emails.get('Tracking Code reminder')['email_rcpt'] == [user.email]
|
||||
assert form_class.get(number31.id).tracking_code in emails.get('Tracking Code reminder')['payload']
|
||||
|
||||
pub.cfg['sms'] = {'passerelle_url': 'xx', 'sender': 'xx'}
|
||||
pub.write_cfg()
|
||||
resp = user_view_resp.click('Send tracking code', index=0)
|
||||
resp.form['method'].value = 'SMS'
|
||||
resp.form['sms'].value = '0123456789'
|
||||
resp = resp.form.submit()
|
||||
resp = resp.follow()
|
||||
assert sms_mocking.sms[-1]['destinations'] == ['0123456789']
|
||||
assert form_class.get(number31.id).tracking_code in sms_mocking.sms[-1]['text']
|
||||
|
||||
|
||||
def test_backoffice_resume_folded(pub):
|
||||
create_user(pub)
|
||||
create_environment(pub)
|
||||
|
|
|
@ -32,7 +32,6 @@ from quixote.html import TemplateIO, htmlescape, htmltext
|
|||
from quixote.http_request import parse_query
|
||||
|
||||
from wcs.admin.forms import UpdateDigestAfterJob
|
||||
from wcs.admin.settings import UserFieldsFormDef
|
||||
from wcs.api_utils import get_user_from_api_query_string
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.categories import Category
|
||||
|
@ -45,9 +44,7 @@ from wcs.roles import logged_users_role
|
|||
from wcs.variables import LazyFieldVar
|
||||
from wcs.workflows import ActionsTracingEvolutionPart, WorkflowStatusItem, item_classes, template_on_formdata
|
||||
|
||||
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.menu import command_icon
|
||||
from ..qommon import _, errors, ezt, force_str, get_cfg, get_logger, misc, ngettext, ods
|
||||
from ..qommon.afterjobs import AfterJob
|
||||
from ..qommon.backoffice.listing import pagination_links
|
||||
from ..qommon.backoffice.menu import html_top
|
||||
|
@ -55,7 +52,6 @@ from ..qommon.evalutils import make_datetime
|
|||
from ..qommon.form import (
|
||||
CheckboxWidget,
|
||||
DateWidget,
|
||||
EmailWidget,
|
||||
Form,
|
||||
HiddenWidget,
|
||||
HtmlWidget,
|
||||
|
@ -74,7 +70,6 @@ from ..qommon.storage import (
|
|||
Equal,
|
||||
FtsMatch,
|
||||
GreaterOrEqual,
|
||||
ILike,
|
||||
Intersects,
|
||||
LessOrEqual,
|
||||
NotEqual,
|
||||
|
@ -145,339 +140,8 @@ def geojson_formdatas(formdatas, geoloc_key='base', fields=None):
|
|||
return geojson
|
||||
|
||||
|
||||
class SendCodeFormdefDirectory(Directory):
|
||||
formdef = None
|
||||
|
||||
def __init__(self, formdef):
|
||||
self.formdef = formdef
|
||||
|
||||
def _q_lookup(self, component):
|
||||
html_top('management', _('Management'))
|
||||
formdata = self.formdef.data_class().get(component)
|
||||
|
||||
submitter_email = formdata.formdef.get_submitter_email(formdata)
|
||||
mail_subject = EmailsDirectory.get_subject('tracking-code-reminder')
|
||||
mail_body = EmailsDirectory.get_body('tracking-code-reminder')
|
||||
|
||||
form = Form()
|
||||
form.add(TextWidget, 'text', title=_('Message'), required=True, cols=60, rows=5, value=mail_body)
|
||||
form.add(EmailWidget, 'email', title=_('Email'), required=False, value=submitter_email)
|
||||
sms_class = None
|
||||
if get_publisher().use_sms_feature:
|
||||
sms_class = sms.SMS.get_sms_class()
|
||||
if sms_class:
|
||||
form.add(StringWidget, 'sms', title=_('SMS Number'), required=False)
|
||||
form.add(
|
||||
RadiobuttonsWidget,
|
||||
'method',
|
||||
options=[('email', _('Email')), ('sms', _('SMS'))],
|
||||
value='email',
|
||||
required=True,
|
||||
extra_css_class='widget-inline-radio',
|
||||
)
|
||||
form.add_submit('submit', _('Send'))
|
||||
form.add_submit('cancel', _('Cancel'))
|
||||
|
||||
if not form.is_submitted() or form.has_errors():
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<h2>%s</h2>') % _('Send tracking code')
|
||||
r += form.render()
|
||||
return r.getvalue()
|
||||
|
||||
if not formdata.tracking_code:
|
||||
tracking_code = get_publisher().tracking_code_class()
|
||||
tracking_code.formdata = formdata # this stores both objects
|
||||
|
||||
message = form.get_widget('text').parse()
|
||||
data = {
|
||||
'form_tracking_code': formdata.tracking_code,
|
||||
'tracking_code': formdata.tracking_code,
|
||||
'email': form.get_widget('email').parse(),
|
||||
}
|
||||
data.update(self.formdef.get_substitution_variables(minimal=True))
|
||||
|
||||
if sms_class and form.get_widget('method').parse() == 'sms':
|
||||
# send sms
|
||||
sitename = get_cfg('misc', {}).get('sitename') or 'w.c.s.'
|
||||
sms_cfg = get_cfg('sms', {})
|
||||
sender = sms_cfg.get('sender', sitename)[:11]
|
||||
message = Template(message).render(data)
|
||||
try:
|
||||
sms_class.send(sender, [form.get_widget('sms').parse()], message)
|
||||
except errors.SMSError as e:
|
||||
get_logger().error(e)
|
||||
get_session().message = ('info', _('SMS with tracking code sent to the user'))
|
||||
else:
|
||||
# send mail
|
||||
emails.template_email(
|
||||
mail_subject, message, mail_body_data=data, email_rcpt=form.get_widget('email').parse()
|
||||
)
|
||||
get_session().message = ('info', _('Email with tracking code sent to the user'))
|
||||
|
||||
return redirect('../..')
|
||||
|
||||
|
||||
class SendCodeDirectory(Directory):
|
||||
def _q_lookup(self, component):
|
||||
try:
|
||||
formdef = FormDef.get_by_urlname(component)
|
||||
if not formdef.enable_tracking_codes:
|
||||
raise errors.TraversalError()
|
||||
return SendCodeFormdefDirectory(formdef)
|
||||
except KeyError:
|
||||
raise errors.TraversalError()
|
||||
|
||||
|
||||
class UserViewDirectory(Directory):
|
||||
_q_exports = ['', 'sendcode']
|
||||
|
||||
sendcode = SendCodeDirectory()
|
||||
user = None
|
||||
|
||||
def __init__(self, user):
|
||||
self.user = user
|
||||
|
||||
def _q_index(self):
|
||||
get_response().breadcrumb.append(('%s/' % self.user.id, self.user.display_name))
|
||||
html_top('management', _('Management'))
|
||||
# display list of open formdata for the user
|
||||
formdefs = [x for x in FormDef.select(lightweight=True) if not x.skip_from_360_view]
|
||||
user_roles = set([logged_users_role().id] + get_request().user.get_roles())
|
||||
criterias = [
|
||||
Equal('is_at_endpoint', False),
|
||||
Equal('user_id', str(self.user.id)),
|
||||
Contains('formdef_id', [x.id for x in formdefs]),
|
||||
]
|
||||
from wcs import sql
|
||||
|
||||
formdatas = sql.AnyFormData.select(criterias, order_by='receipt_time')
|
||||
|
||||
criterias = [
|
||||
Equal('is_at_endpoint', False),
|
||||
Equal('user_id', str(self.user.id)),
|
||||
Intersects('concerned_roles_array', user_roles),
|
||||
]
|
||||
viewable_formdatas = sql.AnyFormData.select(criterias)
|
||||
viewable_formdatas_ids = {}
|
||||
for viewable_formdata in viewable_formdatas:
|
||||
viewable_formdatas_ids[(viewable_formdata.formdef.id, viewable_formdata.id)] = True
|
||||
|
||||
r = TemplateIO(html=True)
|
||||
r += get_session().display_message()
|
||||
|
||||
r += htmltext('<div class="bo-block">')
|
||||
r += htmltext('<h2>%s</h2>') % self.user.display_name
|
||||
formdef = UserFieldsFormDef()
|
||||
r += htmltext('<div class="form">')
|
||||
for field in formdef.get_all_fields():
|
||||
if not hasattr(field, 'get_view_value'):
|
||||
continue
|
||||
value = self.user.form_data.get(field.id)
|
||||
if not value:
|
||||
continue
|
||||
r += htmltext('<div class="title">')
|
||||
r += field.label
|
||||
r += htmltext('</div>')
|
||||
r += htmltext('<div class="StringWidget content">')
|
||||
r += field.get_view_value(value)
|
||||
r += htmltext('</div>')
|
||||
r += htmltext('</div>')
|
||||
r += htmltext('</div>')
|
||||
|
||||
if formdatas:
|
||||
categories = {}
|
||||
formdata_by_category = {}
|
||||
for formdata in formdatas:
|
||||
if formdata.formdef.category_id not in categories:
|
||||
categories[formdata.formdef.category_id] = formdata.formdef.category
|
||||
formdata_by_category[formdata.formdef.category_id] = []
|
||||
formdata_by_category[formdata.formdef.category_id].append(formdata)
|
||||
cats = list(categories.values())
|
||||
Category.sort_by_position(cats)
|
||||
for cat in cats:
|
||||
r += htmltext('<div class="bo-block">')
|
||||
if len(cats) > 1:
|
||||
if cat is None:
|
||||
r += htmltext('<h2>%s</h2>') % _('Misc')
|
||||
cat_formdatas = formdata_by_category[None]
|
||||
else:
|
||||
r += htmltext('<h2>%s</h2>') % cat.name
|
||||
cat_formdatas = formdata_by_category[cat.id]
|
||||
else:
|
||||
cat_formdatas = formdatas
|
||||
r += htmltext('<ul class="biglist c-360-user-view">')
|
||||
for formdata in cat_formdatas:
|
||||
status_label = formdata.get_status_label()
|
||||
submit_date = misc.strftime(misc.date_format(), formdata.receipt_time)
|
||||
formdata_key_id = (formdata.formdef.id, formdata.id)
|
||||
if formdata_key_id in viewable_formdatas_ids:
|
||||
r += htmltext(
|
||||
'<li><a href="%s">%s, '
|
||||
'<span class="datetime">%s</span> '
|
||||
'<span class="status">(%s)</span></a>'
|
||||
% (
|
||||
formdata.get_url(backoffice=True),
|
||||
formdata.formdef.name,
|
||||
submit_date,
|
||||
status_label,
|
||||
)
|
||||
)
|
||||
else:
|
||||
r += htmltext(
|
||||
'<li><span>%s, '
|
||||
'<span class="datetime">%s</span> '
|
||||
'<span class="status">(%s)</span></span>'
|
||||
% (formdata.formdef.name, submit_date, status_label)
|
||||
)
|
||||
if formdata.formdef.enable_tracking_codes:
|
||||
r += htmltext('<p class="commands">')
|
||||
r += command_icon(
|
||||
'sendcode/%s/%s' % (formdata.formdef.url_name, formdata.id),
|
||||
'export',
|
||||
label=_('Send tracking code'),
|
||||
popup=True,
|
||||
)
|
||||
r += htmltext('</p>')
|
||||
r += htmltext('</ul>')
|
||||
r += htmltext('</div>')
|
||||
r += htmltext('</div>')
|
||||
|
||||
return r.getvalue()
|
||||
|
||||
|
||||
class UsersViewDirectory(Directory):
|
||||
_q_exports = ['']
|
||||
|
||||
def _q_traverse(self, path):
|
||||
if not get_publisher().is_using_postgresql():
|
||||
raise errors.TraversalError()
|
||||
get_response().breadcrumb.append(('users', _('Per User View')))
|
||||
return super()._q_traverse(path)
|
||||
|
||||
def get_search_sidebar(self, offset=None, limit=None, order_by=None):
|
||||
get_response().add_javascript(['wcs.listing.js'])
|
||||
|
||||
r = TemplateIO(html=True)
|
||||
r += htmltext('<form id="listing-settings" action=".">')
|
||||
if offset or limit:
|
||||
if not offset:
|
||||
offset = 0
|
||||
r += htmltext('<input type="hidden" name="offset" value="%s"/>') % offset
|
||||
if limit:
|
||||
r += htmltext('<input type="hidden" name="limit" value="%s"/>') % limit
|
||||
|
||||
if order_by is None:
|
||||
order_by = ''
|
||||
r += htmltext('<input type="hidden" name="order_by" value="%s"/>') % order_by
|
||||
|
||||
r += htmltext('<h3>%s</h3>') % _('Search')
|
||||
if get_request().form.get('q'):
|
||||
q = force_text(get_request().form.get('q'))
|
||||
r += htmltext('<input name="q" value="%s">') % force_str(q)
|
||||
else:
|
||||
r += htmltext('<input name="q">')
|
||||
r += htmltext('<input type="submit" value="%s"/>') % _('Search')
|
||||
|
||||
return r.getvalue()
|
||||
|
||||
def _q_index(self):
|
||||
html_top('management', _('Management'))
|
||||
r = TemplateIO(html=True)
|
||||
|
||||
limit = misc.get_int_or_400(
|
||||
get_request().form.get('limit', get_publisher().get_site_option('default-page-size')) or 20
|
||||
)
|
||||
offset = misc.get_int_or_400(get_request().form.get('offset', 0))
|
||||
order_by = misc.get_order_by_or_400(get_request().form.get('order_by', None)) or 'name'
|
||||
query = get_request().form.get('q')
|
||||
|
||||
get_response().filter['sidebar'] = self.get_search_sidebar(
|
||||
limit=limit, offset=offset, order_by=order_by
|
||||
)
|
||||
|
||||
if not query:
|
||||
r += htmltext('<div id="listing">')
|
||||
r += htmltext('<div class="big-msg-info">')
|
||||
r += htmltext('<p>%s</p>') % _('Use the search field on the right ' 'to look for an user.')
|
||||
r += htmltext('</div>')
|
||||
r += htmltext('</div>')
|
||||
if get_request().form.get('ajax') == 'true':
|
||||
get_response().filter = {'raw': True}
|
||||
return r.getvalue()
|
||||
|
||||
formdef = UserFieldsFormDef()
|
||||
criteria_fields = [
|
||||
ILike('name', query),
|
||||
ILike('ascii_name', misc.simplify(query, ' ')),
|
||||
ILike('email', query),
|
||||
]
|
||||
for field in formdef.get_all_fields():
|
||||
if field.type in ('string', 'text', 'email'):
|
||||
criteria_fields.append(ILike('f%s' % field.id, query))
|
||||
if get_publisher().is_using_postgresql():
|
||||
criteria_fields.append(FtsMatch(query))
|
||||
criterias = [Or(criteria_fields)]
|
||||
users = get_publisher().user_class.select(
|
||||
order_by=order_by, clause=criterias, limit=limit, offset=offset
|
||||
)
|
||||
total_count = get_publisher().user_class.count(criterias)
|
||||
|
||||
users_cfg = get_cfg('users', {})
|
||||
include_name_column = not (users_cfg.get('field_name'))
|
||||
include_email_column = not (users_cfg.get('field_email'))
|
||||
r += htmltext('<div id="listing">')
|
||||
r += htmltext('<table class="main">')
|
||||
r += htmltext('<thead>')
|
||||
r += htmltext('<tr>')
|
||||
if include_name_column:
|
||||
r += htmltext('<th data-field-sort-key="name"><span>%s</span></th>') % _('Name')
|
||||
if include_email_column:
|
||||
r += htmltext('<th data-field-sort-key="email"><span>%s</span></th>') % _('Email')
|
||||
for field in formdef.get_all_fields():
|
||||
if field.include_in_listing:
|
||||
r += htmltext('<th data-field-sort-key="f%s"><span>%s</span></th>') % (field.id, field.label)
|
||||
r += htmltext('</tr>')
|
||||
r += htmltext('</thead>')
|
||||
r += htmltext('<tbody>')
|
||||
|
||||
for user in users:
|
||||
r += htmltext('<tr data-link="%s/">') % user.id
|
||||
if include_name_column:
|
||||
r += htmltext('<td>%s</td>') % (user.name or '')
|
||||
if include_email_column:
|
||||
r += htmltext('<td>%s</td>') % (user.email or '')
|
||||
for field in formdef.get_all_fields():
|
||||
if field.include_in_listing:
|
||||
r += htmltext('<td>%s</td>') % (user.form_data.get(field.id) or '')
|
||||
r += htmltext('</tr>')
|
||||
|
||||
r += htmltext('</tbody>')
|
||||
r += htmltext('</table>')
|
||||
|
||||
if get_publisher().is_using_postgresql():
|
||||
r += pagination_links(offset, limit, total_count)
|
||||
|
||||
r += htmltext('</div>')
|
||||
|
||||
if get_request().form.get('ajax') == 'true':
|
||||
get_response().filter = {'raw': True}
|
||||
return r.getvalue()
|
||||
|
||||
return r.getvalue()
|
||||
|
||||
def _q_lookup(self, component):
|
||||
try:
|
||||
user = get_publisher().user_class.get(component)
|
||||
return UserViewDirectory(user)
|
||||
except KeyError:
|
||||
pass
|
||||
raise errors.TraversalError()
|
||||
|
||||
|
||||
class ManagementDirectory(Directory):
|
||||
_q_exports = ['', 'forms', 'listing', 'statistics', 'lookup', 'count', 'users', 'geojson', 'map']
|
||||
|
||||
users = UsersViewDirectory()
|
||||
_q_exports = ['', 'forms', 'listing', 'statistics', 'lookup', 'count', 'geojson', 'map']
|
||||
|
||||
def add_breadcrumb(self):
|
||||
get_response().breadcrumb.append(('management/', _('Management')))
|
||||
|
@ -544,8 +208,6 @@ class ManagementDirectory(Directory):
|
|||
and get_publisher().get_site_option('postgresql_views') != 'false'
|
||||
):
|
||||
r += htmltext('<a href="listing">%s</a>') % _('Global View')
|
||||
if get_publisher().has_site_option('per-user-view'):
|
||||
r += htmltext(' <a href="users/">%s</a>') % _('Per User View')
|
||||
for formdef in formdefs:
|
||||
if formdef.geolocations:
|
||||
r += htmltext(' <a href="map">%s</a>') % _('Map View')
|
||||
|
|
Loading…
Reference in New Issue