templates: use a template to render formdata history (#17601)

This commit is contained in:
Frédéric Péters 2017-07-13 11:29:45 +02:00
parent 98b74edd3f
commit db4d477898
8 changed files with 118 additions and 105 deletions

View File

@ -8,3 +8,4 @@ recursive-include data/themes/default/ *.html *.css *.png *.gif *.jpg *.js *.ezt
recursive-include data/themes/alto/ *.html *.css *.png *.gif *.jpg *.js *.ezt *.xml
recursive-include data/vendor/ *.dat
recursive-include wcs/qommon/static/ *.css *.png *.gif *.jpg *.js *.eot *.svg *.ttf *.woff
recursive-include wcs/templates *.html

View File

@ -521,30 +521,38 @@ def test_register_comment(pub):
item = RegisterCommenterWorkflowStatusItem()
item.perform(formdata)
formdata.evolution[-1]._display_parts = None
assert formdata.evolution[-1].display_parts()[-1] == ''
item.comment = 'Hello world'
item.perform(formdata)
formdata.evolution[-1]._display_parts = None
assert formdata.evolution[-1].display_parts()[-1] == '<p>Hello world</p>'
item.comment = '<div>Hello world</div>'
item.perform(formdata)
formdata.evolution[-1]._display_parts = None
assert formdata.evolution[-1].display_parts()[-1] == '<div>Hello world</div>'
item.comment = '[test]'
item.perform(formdata)
formdata.evolution[-1]._display_parts = None
assert formdata.evolution[-1].display_parts()[-1] == '<p>[test]</p>'
item.comment = '[bar]'
item.perform(formdata)
formdata.evolution[-1]._display_parts = None
formdata.evolution[-1]._display_parts = None
assert formdata.evolution[-1].display_parts()[-1] == '<p>Foobar</p>'
item.comment = '[foo]'
item.perform(formdata)
formdata.evolution[-1]._display_parts = None
assert formdata.evolution[-1].display_parts()[-1] == '<p>1 &lt; 3</p>'
item.comment = '<div>[foo]</div>'
item.perform(formdata)
formdata.evolution[-1]._display_parts = None
assert formdata.evolution[-1].display_parts()[-1] == '<div>1 &lt; 3</div>'
def test_register_comment_attachment(pub):
@ -561,6 +569,7 @@ def test_register_comment_attachment(pub):
item = RegisterCommenterWorkflowStatusItem()
item.perform(formdata)
formdata.evolution[-1]._display_parts = None
assert formdata.evolution[-1].display_parts()[-1] == ''
if os.path.exists(os.path.join(get_publisher().app_dir, 'attachments')):

View File

@ -143,17 +143,29 @@ class Evolution(object):
return self._formdata
def get_author_name(self):
user_id = self.who
if self.who == '_submitter':
user_id = self.formdata.user_id
try:
return get_publisher().user_class.get(user_id).display_name
except KeyError:
return None
def get_author_qualification(self):
if self.who == '_submitter' and not self.formdata.is_submitter(get_request().user):
return _('Original Submitter')
else:
return get_publisher().user_class.get(self.who).display_name
return None
def add_part(self, part):
if not self.parts:
self.parts = []
self.parts.append(part)
_display_parts = None # cache
def display_parts(self):
if self._display_parts is not None:
return self._display_parts
if not self.parts:
return []
@ -162,7 +174,8 @@ class Evolution(object):
if not hasattr(p, 'view'):
continue
l.append(p.view())
return l
self._display_parts = l
return self._display_parts
def get_json_export_dict(self, user, anonymise=False):
data = {
@ -194,8 +207,27 @@ class Evolution(object):
odict = self.__dict__.copy()
if odict.has_key('_formdata'):
del odict['_formdata']
if odict.has_key('_display_parts'):
del odict['_display_parts']
return odict
@property
def datetime(self):
return datetime.datetime(*self.time[:6])
def is_hidden(self):
if self.status:
wf_status = self.formdata.get_status(self.status)
if wf_status and not wf_status.is_visible(self.formdata, get_request().user):
return True
return False
def get_status(self):
return self.formdata.get_status(status=self.status)
def get_status_label(self):
return self.formdata.get_status_label(status=self.status)
class FormData(StorableObject):
_names = 'XX'
@ -451,6 +483,22 @@ class FormData(StorableObject):
return wf_status
return None
def get_visible_evolution_parts(self):
last_seen_status = None
last_seen_author = None
for evolution_part in self.evolution or []:
if evolution_part.is_hidden():
continue
if (evolution_part.status is None or last_seen_status == evolution_part.status) and (
evolution_part.who is None or last_seen_author == evolution_part.who):
# don't include empty evolution parts if status and author
# didn't change.
if not evolution_part.comment and not evolution_part.display_parts():
continue
last_seen_status = evolution_part.status or last_seen_status
last_seen_author = evolution_part.who or last_seen_author
yield evolution_part
def get_workflow_form(self, user):
wf_status = self.get_status()
if not wf_status:

View File

@ -25,6 +25,8 @@ from wcs.api_utils import get_user_from_api_query_string, is_url_signed
from wcs.fields import WidgetField, FileField
from wcs.workflows import EditableWorkflowStatusItem
from django.template import RequestContext
from qommon import _
from qommon import template
from qommon import get_logger
@ -94,6 +96,8 @@ class FormStatusPage(Directory):
_q_extra_exports = []
form_page_class = None
history_templates = ['wcs/formdata_history.html']
def html_top(self, title = None):
template.html_top(title = title, default_org = _('Forms'))
@ -251,108 +255,9 @@ class FormStatusPage(Directory):
return
if not self.formdef.is_user_allowed_read_status_and_history(get_request().user, self.filled):
return
r = TemplateIO(html=True)
r += htmltext('<div class="bo-block" id="evolution-log">')
r += htmltext('<h2 class="foldable">%s</h2>') % _('Log')
r += htmltext('<ul id="evolutions">')
hidden = False
previous_status = None
for evo in self.filled.evolution:
status_css_class = ''
if evo.status:
wf_status = self.filled.get_status(evo.status)
if wf_status:
status_css_class = wf_status.extra_css_class
if wf_status and not wf_status.is_visible(self.filled, get_request().user):
hidden = True
else:
hidden = False
if hidden:
continue
evo_author = None
evo_author_more = None
klass = 'msg-system'
if evo.who:
if evo.who == '_submitter':
klass = 'msg-in'
evo_author = _('Original Submitter')
if get_request().user and self.filled.is_submitter(get_request().user):
evo_author = get_request().user.display_name
elif self.filled.user_id:
try:
evo_author = get_publisher().user_class.get(self.filled.user_id).display_name
evo_author_more = _('(Original Submitter)')
except KeyError:
pass
else:
klass = 'msg-out'
try:
evo_author = evo.get_author_name()
except KeyError:
pass
status_block = TemplateIO(html=True)
if evo.status:
status_block += htmltext('<div class="evolution-metadata">')
status_block += htmltext('<span class="status">%s</span> '
) % self.filled.get_status_label(evo.status)
status_block += htmltext('<span class="time">%s</span> '
) % misc.localstrftime(evo.time)
status_block += htmltext('</div>') # <-- .evolution-metadata -->
parts = TemplateIO(html=True)
if evo_author:
parts += htmltext('<span class="user">%s') % evo_author
if evo_author_more:
parts += htmltext(' <span>%s</span>') % evo_author_more
parts += htmltext('</span>')
if not evo.status:
parts += htmltext('<span class="time">%s</span> ') % misc.localstrftime(evo.time)
if evo.comment:
if evo.comment.startswith(str('#pre')):
parts += htmltext('<div class="comment"><pre>%s</pre></div>') % evo.comment[4:]
else:
parts += htmltext('<div class="comment">')
parts += htmltext('<p>')
parts += htmltext('\n').join(
[(x or htmltext('</p><p>')) for x in evo.comment.splitlines()])
parts += htmltext('</p>')
parts += htmltext('</div>')
for t in evo.display_parts():
parts += t
parts_value = parts.getvalue()
if not (parts_value or evo.status != previous_status):
continue
if evo.status:
previous_status = evo.status
try:
status_colour = self.filled.get_status(previous_status).colour
except AttributeError:
status_colour = 'ffffff'
fg_colour = misc.get_foreground_colour(status_colour)
r += htmltext('<li class="%s %s">' % (klass, status_css_class))
r += htmltext('<span class="item" style="background: #%s; color: %s;"></span>' %
(status_colour, fg_colour))
r += htmltext('<div>')
r += status_block.getvalue()
if parts_value:
r += htmltext('<div class="msg">')
r += htmltext(parts_value)
r += htmltext('</div>')
r += htmltext('</div>')
r += htmltext('</li>')
r += htmltext('</ul>')
r += htmltext('</div>') # .bo-block #evolution-log
return r.getvalue()
context = RequestContext(get_request(), {'formdata': self.filled})
return template.render(self.history_templates, context)
def check_receiver(self):
session = get_session()

View File

@ -1577,6 +1577,7 @@ class RootDirectory(AccessControlled, Directory):
class PublicFormStatusPage(FormStatusPage):
_q_exports_orig = ['', 'download', 'status']
form_page_class = FormPage
history_templates = ['wcs/front/formdata_history.html', 'wcs/formdata_history.html']
def __init__(self, *args, **kwargs):
FormStatusPage.__init__(self, *args, **kwargs)

View File

@ -19,6 +19,8 @@ import os
import glob
import xml.etree.ElementTree as ET
from django.template.loader import render_to_string
from quixote import get_session, get_request, get_response, get_publisher
from quixote.directory import Directory
from quixote.util import StaticDirectory, StaticFile
@ -405,3 +407,7 @@ def decorate(body, response):
template.generate(fd, vars)
return fd.getvalue()
def render(template_name, context):
return htmltext(render_to_string(template_name, context).encode('utf-8'))

View File

@ -0,0 +1,39 @@
{% load i18n %}
<div class="bo-block" id="evolution-log">
<h2 class="foldable">{% trans "Log" %}</h2>
<ul id="evolutions">
{% for evolution in formdata.get_visible_evolution_parts %}
{% with status=evolution.get_status display_parts=evolution.display_parts %}
<li class="{% if evolution.who == '_submitter' %}msg-in{% elif evolution.who %}msg-out{% else %}msg-system{% endif %} {{ status.extra_css_class }}">
<span class="item" style="background: #{{ status.colour }}; color: {{ status.get_contrast_color}}"></span>
<div>
{% if evolution.status %}
<div class="evolution-metadata">
<span class="status">{{evolution.get_status_label}}</span>
<span class="time">{{evolution.datetime}}</span>
</div>
{% endif %}
<div class="msg">
{% if evolution.who %}
<span class="user">{{evolution.get_author_name}}
<span>{% if evolution.get_author_qualification %}({{evolution.get_author_qualification}}){% endif %}</span>
</span>
{% endif %}
{% if not evolution.status %}
<span class="time">{{evolution.datetime}}</span>
{% endif %}
{% if evolution.comment %}
<div class="comment">
{{evolution.comment|linebreaks}}
</div>
{% endif %}
{% for part in display_parts %}
{{part|safe}}
{% endfor %}
</div>
</div>
</li>
{% endwith %}
{% endfor %}
</ul>
</div>

View File

@ -29,7 +29,7 @@ import uuid
from quixote import get_request, redirect
from qommon import _
from qommon.misc import C_, get_as_datetime, file_digest
from qommon.misc import C_, get_as_datetime, file_digest, get_foreground_colour
from qommon.storage import StorableObject, atomic_write
from qommon.form import *
from qommon.humantime import seconds2humanduration
@ -1411,6 +1411,10 @@ class WorkflowStatus(object):
waitpoint = item.waitpoint or waitpoint
return bool(endpoint or waitpoint)
def get_contrast_color(self):
colour = self.colour or 'ffffff'
return misc.get_foreground_colour(colour)
def __getstate__(self):
odict = self.__dict__.copy()
if odict.has_key('parent'):