Skip to content

Commit 9422aad

Browse files
committed
Updated tests
1 parent a5c9ed2 commit 9422aad

File tree

4 files changed

+317
-7
lines changed

4 files changed

+317
-7
lines changed

imio/dms/mail/browser/templates/dmsoutgoingmail_view.pt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
<!-- This is a copy of collective/dms/basecontent/browser/templates/dmsdocument_view.pt -->
2+
<!-- Only the @@signing_actions_panel has been added -->
13
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
24
xmlns:tal="http://xml.zope.org/namespaces/tal"
35
xmlns:metal="http://xml.zope.org/namespaces/metal"
@@ -58,7 +60,8 @@
5860
tal:attributes="id python:''.join((group.prefix, 'groups.', group.__name__)).replace('.', '-')">
5961
<legend>
6062
<span tal:content="group/label" style="padding-right: 5px"></span>
61-
<tal:widget tal:replace="structure context/@@signing_actions_panel"/>
63+
<tal:widget tal:condition="python:getattr(group, '__name__', '') == 'signing'"
64+
tal:replace="structure context/@@signing_actions_panel"/>
6265
</legend>
6366
<tal:block tal:repeat="widget group/widgets/values">
6467
<tal:widget tal:replace="structure widget/@@ploneform-render-widget"/>

imio/dms/mail/browser/templates/fieldset_actions_panel_edit.pt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
<!-- This is a copy of imio/actionspanel/browser/actions_panel_edit.pt -->
2+
<!-- Only the anchor fieldset has been added -->
13
<td class="noPadding" i18n:domain="plone" tal:define="dummy view/saveHasActions;
24
useIcons view/useIcons;">
35
<tal:comment replace="nothing">Icon</tal:comment>

imio/dms/mail/browser/views.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -692,9 +692,9 @@ def _uid_to_link(self, uid):
692692
obj = uuidToObject(uid, unrestricted=True)
693693
if obj is None:
694694
return u"<span title='not found'>{}</span>".format(safe_unicode(uid))
695-
url = obj.absolute_url()
696-
title = escape(safe_unicode(getattr(obj, 'title', '') or uid))
695+
url = escape(obj.absolute_url(), quote=True)
697696
path = escape(u"/".join(obj.getPhysicalPath()))
697+
title = escape(safe_unicode(getattr(obj, "title", "") or path))
698698
return u"<a href='{}' title='{}'>{}</a>".format(url, path, title)
699699

700700
def _render_value(self, value, indent=u""):
@@ -705,7 +705,8 @@ def _render_value(self, value, indent=u""):
705705
return u"{}"
706706
lines = [u"{"]
707707
for k, v in sorted(value.items()):
708-
lines.append(u"{}{!r}: {},".format(inner, k, self._render_value(v, inner)))
708+
key = escape(safe_unicode(pprint.pformat(k)))
709+
lines.append(u"{}{}: {},".format(inner, key, self._render_value(v, inner)))
709710
lines.append(u"{}}}".format(indent))
710711
return u"\n".join(lines)
711712
elif isinstance(value, (list, tuple)):
@@ -716,14 +717,15 @@ def _render_value(self, value, indent=u""):
716717
lines.append(u"{}{},".format(inner, self._render_value(item, inner)))
717718
lines.append(u"{}]".format(indent))
718719
return u"\n".join(lines)
719-
elif isinstance(value, basestring) and re.match(r'^[0-9a-f]{32}$', value):
720+
elif isinstance(value, basestring) and re.match(r"^[0-9a-f]{32}$", value):
720721
# Looks like a UUID
721722
return self._uid_to_link(value)
722723
else:
723-
return safe_unicode(pprint.pformat(value))
724+
return escape(safe_unicode(pprint.pformat(value)))
724725

725726
@property
726727
def approval_annot_html(self):
728+
"""Renders approval annot in HTML"""
727729
approval = IOMApproval(self.context)
728730
native = persistent_to_native(approval.annot)
729731
return self._render_value(native)
@@ -738,10 +740,11 @@ def esign_sessions(self):
738740
annot = get_session_annotation()
739741
result = []
740742
for session_id in session_ids:
741-
session = annot["sessions"].get(session_id)
743+
session = annot.get("sessions", {}).get(session_id)
742744
if session is not None:
743745
result.append((session_id, persistent_to_native(session)))
744746
return sorted(result)
745747

746748
def esign_session_html(self, session_data):
749+
"""Renders esign session annot in HTML"""
747750
return self._render_value(session_data)

imio/dms/mail/tests/test_views.py

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
11
# -*- coding: utf-8 -*-
22
"""Test views."""
33

4+
from AccessControl import Unauthorized
5+
from collective.dms.mailcontent.dmsmail import internalReferenceOutgoingMailDefaultValue
6+
from collective.iconifiedcategory.utils import calculate_category_id
47
from collective.MockMailHost.MockMailHost import MockMailHost
58
from datetime import datetime
9+
from HTMLParser import HTMLParser
610
from imio.dms.mail import PERIODS
11+
from imio.dms.mail import PRODUCT_DIR
712
from imio.dms.mail.browser.views import parse_query
13+
from imio.dms.mail.browser.views import SigningAnnotationInfoView
14+
from imio.dms.mail.interfaces import IOMApproval
815
from imio.dms.mail.testing import change_user
916
from imio.dms.mail.testing import DMSMAIL_INTEGRATION_TESTING
17+
from imio.dms.mail.utils import DummyView
18+
from imio.dms.mail.utils import sub_create
19+
from imio.esign.config import set_registry_file_url
20+
from imio.esign.utils import get_session_annotation
1021
from imio.helpers.content import get_object
1122
from imio.helpers.content import richtextval
1223
from imio.helpers.emailer import get_mail_host
24+
from imio.helpers.test_helpers import ImioTestHelpers
1325
from plone import api
26+
from plone.app.testing import login
27+
from plone.dexterity.utils import createContentInContainer
28+
from plone.namedfile.file import NamedBlobFile
29+
from Products.CMFPlone.utils import safe_unicode
30+
from z3c.relationfield import RelationValue
31+
from zope.component import getUtility
1432
from zope.i18n import translate
33+
from zope.intid import IIntIds
1534

1635
import json
1736
import unittest
@@ -297,3 +316,286 @@ def test_call(self):
297316
self.assertIn(u">012/34.56.79<", rendered)
298317
self.assertIn(u">Rue Léon Morel, 1<", rendered)
299318
self.assertIn(u">5032 Isnes<", rendered)
319+
320+
321+
class TestSigningAnnotationInfoView(unittest.TestCase, ImioTestHelpers):
322+
"""Test SigningAnnotationInfoView"""
323+
324+
layer = DMSMAIL_INTEGRATION_TESTING
325+
326+
def setUp(self):
327+
self.portal = self.layer["portal"]
328+
change_user(self.portal)
329+
self.om1 = get_object(oid="reponse1", ptype="dmsoutgoingmail")
330+
self.view = SigningAnnotationInfoView(self.om1, self.portal.REQUEST)
331+
self.pf = self.portal["contacts"]["personnel-folder"]
332+
self.pgof = self.portal["contacts"]["plonegroup-organization"]
333+
334+
def _setup_esign_omail(self):
335+
"""Create a new outgoing mail with esign enabled and two files."""
336+
login(self.layer["app"], "admin")
337+
self.portal.portal_setup.runImportStepFromProfile(
338+
"profile-imio.dms.mail:singles", "imiodmsmail-activate-esigning", run_dependencies=False
339+
)
340+
set_registry_file_url("https://downloads.files.com")
341+
intids = getUtility(IIntIds)
342+
params = {
343+
"title": u"Courrier test esign",
344+
"internal_reference_no": internalReferenceOutgoingMailDefaultValue(
345+
DummyView(self.portal, self.portal.REQUEST)
346+
),
347+
"mail_type": "courrier",
348+
"treating_groups": self.pgof["direction-generale"]["grh"].UID(),
349+
"recipients": [RelationValue(intids.getId(self.portal["contacts"]["jeancourant"]))],
350+
"assigned_user": "agent",
351+
"sender": self.portal["contacts"]["jeancourant"]["agent-electrabel"].UID(),
352+
"send_modes": u"post",
353+
"signers": [
354+
{
355+
"number": 1,
356+
"signer": self.pf["dirg"]["directeur-general"].UID(),
357+
"approvings": [u"_themself_"],
358+
"editor": True,
359+
},
360+
{
361+
"number": 2,
362+
"signer": self.pf["bourgmestre"]["bourgmestre"].UID(),
363+
"approvings": [u"_themself_"],
364+
"editor": False,
365+
},
366+
],
367+
"esign": True,
368+
}
369+
omail = sub_create(self.portal["outgoing-mail"], "dmsoutgoingmail", datetime.now(), "om-esign", **params)
370+
filename = u"Réponse salle.odt"
371+
ct = self.portal["annexes_types"]["outgoing_dms_files"]["outgoing-dms-file"]
372+
files = []
373+
for i in range(2):
374+
with open("%s/batchimport/toprocess/outgoing-mail/%s" % (PRODUCT_DIR, filename), "rb") as fo:
375+
file_object = NamedBlobFile(fo.read(), filename=filename)
376+
files.append(
377+
createContentInContainer(
378+
omail,
379+
"dmsommainfile",
380+
id="file%s" % i,
381+
scan_id="012999900000601",
382+
file=file_object,
383+
content_category=calculate_category_id(ct),
384+
)
385+
)
386+
return omail, files, IOMApproval(omail)
387+
388+
def _approve_all_files(self, omail, files, approval):
389+
"""Approve all files through the full two-signer approval process."""
390+
pw = self.portal.portal_workflow
391+
pw.doActionFor(omail, "propose_to_approve")
392+
approval.approve_file(files[0], "dirg", transition="propose_to_be_signed")
393+
approval.approve_file(files[1], "dirg", transition="propose_to_be_signed")
394+
approval.approve_file(files[1], "bourgmestre", transition="propose_to_be_signed")
395+
approval.approve_file(files[0], "bourgmestre", transition="propose_to_be_signed")
396+
397+
def test_call(self):
398+
with self.assertRaises(Unauthorized):
399+
self.view()
400+
login(self.portal.aq_parent, "admin")
401+
self.assertIsInstance(self.view(), basestring)
402+
403+
def test_render_value(self):
404+
# Dict
405+
self.assertEqual(self.view._render_value({}), u"{}")
406+
self.assertEqual(
407+
self.view._render_value({"key": "val"}),
408+
u"{\n &#x27;key&#x27;: &#x27;val&#x27;,\n}",
409+
)
410+
411+
# Indentation: nested value increases indent level
412+
self.assertEqual(
413+
self.view._render_value({"key": ["a"]}),
414+
u"{\n &#x27;key&#x27;: [\n &#x27;a&#x27;,\n ],\n}",
415+
)
416+
417+
# List
418+
self.assertEqual(self.view._render_value([]), u"[]")
419+
self.assertEqual(
420+
self.view._render_value(["a", "b"]),
421+
u"[\n &#x27;a&#x27;,\n &#x27;b&#x27;,\n]",
422+
)
423+
424+
# Tuple
425+
self.assertEqual(self.view._render_value(()), u"[]")
426+
427+
# String
428+
self.assertEqual(self.view._render_value(u"hello"), u"u&#x27;hello&#x27;")
429+
430+
# UID not found
431+
fake_uid = u"a" * 32
432+
self.assertEqual(
433+
self.view._render_value(fake_uid),
434+
u"<span title='not found'>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</span>",
435+
)
436+
437+
# UID found
438+
self.assertEqual(
439+
self.view._render_value(self.om1.UID()),
440+
u"<a href='http://nohost/plone/outgoing-mail/202608/reponse1' title='/plone/outgoing-mail/202608/reponse1'>R\xe9ponse 1</a>",
441+
)
442+
443+
def test_uid_to_link(self):
444+
uid = self.om1.UID()
445+
result = self.view._uid_to_link(uid)
446+
self.assertIn(u"<a href=", result)
447+
self.assertIn(self.om1.absolute_url(), result)
448+
449+
result = self.view._uid_to_link(u"a" * 32)
450+
self.assertIn(u"<span", result)
451+
self.assertIn(u"not found", result)
452+
453+
def test_approval_and_esign_sessions(self):
454+
"""Test approval_annot_html and esign_session_html after a true approval process."""
455+
omail, files, approval = self._setup_esign_omail()
456+
self._approve_all_files(omail, files, approval)
457+
458+
view = SigningAnnotationInfoView(omail, self.portal.REQUEST)
459+
460+
# approval annot html
461+
self.assertEqual(
462+
HTMLParser().unescape(view.approval_annot_html),
463+
u"""{{
464+
'approval': [
465+
[
466+
{{
467+
'approved_by': 'dirg',
468+
'approved_on': {},
469+
'status': 'a',
470+
}},
471+
{{
472+
'approved_by': 'dirg',
473+
'approved_on': {},
474+
'status': 'a',
475+
}},
476+
],
477+
[
478+
{{
479+
'approved_by': 'bourgmestre',
480+
'approved_on': {},
481+
'status': 'a',
482+
}},
483+
{{
484+
'approved_by': 'bourgmestre',
485+
'approved_on': {},
486+
'status': 'a',
487+
}},
488+
],
489+
],
490+
'approvers': [
491+
[
492+
'dirg',
493+
],
494+
[
495+
'bourgmestre',
496+
],
497+
],
498+
'current_nb': -1,
499+
'editors': [
500+
True,
501+
False,
502+
],
503+
'files': [
504+
<a href='http://nohost/plone/outgoing-mail/202608/om-esign/file0' title='/plone/outgoing-mail/202608/om-esign/file0'>Réponse salle.odt</a>,
505+
<a href='http://nohost/plone/outgoing-mail/202608/om-esign/file1' title='/plone/outgoing-mail/202608/om-esign/file1'>Réponse salle.odt</a>,
506+
],
507+
'pdf_files': [
508+
[
509+
<a href='http://nohost/plone/outgoing-mail/202608/om-esign/reponse-salle.pdf' title='/plone/outgoing-mail/202608/om-esign/reponse-salle.pdf'>Réponse salle.pdf</a>,
510+
],
511+
[
512+
<a href='http://nohost/plone/outgoing-mail/202608/om-esign/reponse-salle-1.pdf' title='/plone/outgoing-mail/202608/om-esign/reponse-salle-1.pdf'>Réponse salle.pdf</a>,
513+
],
514+
],
515+
'session_ids': [
516+
0,
517+
],
518+
'signers': [
519+
[
520+
'dirg',
521+
u'Maxime DG',
522+
u'Directeur G\\xe9n\\xe9ral',
523+
],
524+
[
525+
'bourgmestre',
526+
u'Paul BM',
527+
u'Bourgmestre',
528+
],
529+
],
530+
}}""".format(
531+
repr(approval.annot["approval"][0][0]["approved_on"]),
532+
repr(approval.annot["approval"][0][1]["approved_on"]),
533+
repr(approval.annot["approval"][1][0]["approved_on"]),
534+
repr(approval.annot["approval"][1][1]["approved_on"]),
535+
),
536+
)
537+
538+
# esign essions property
539+
esign_sessions = view.esign_sessions
540+
self.assertEqual(len(esign_sessions), 1)
541+
esign_session = esign_sessions[0]
542+
self.assertIsInstance(esign_session, tuple)
543+
self.assertEqual(esign_session[0], 0)
544+
545+
# esign session html
546+
self.assertEqual(
547+
HTMLParser().unescape(view.esign_session_html(esign_session[1])),
548+
u"""{{
549+
'acroform': True,
550+
'client_id': '0129999',
551+
'discriminators': [],
552+
'files': [
553+
{{
554+
'context_uid': <a href='http://nohost/plone/outgoing-mail/{folder_name}/om-esign' title='/plone/outgoing-mail/{folder_name}/om-esign'>Courrier test esign</a>,
555+
'filename': u'R\\xe9ponse salle__{pdf1_uid}.pdf',
556+
'scan_id': '012999900000601',
557+
'status': '',
558+
'title': u'R\\xe9ponse salle.pdf',
559+
'uid': <a href='http://nohost/plone/outgoing-mail/{folder_name}/om-esign/reponse-salle.pdf' title='/plone/outgoing-mail/{folder_name}/om-esign/reponse-salle.pdf'>Réponse salle.pdf</a>,
560+
}},
561+
{{
562+
'context_uid': <a href='http://nohost/plone/outgoing-mail/{folder_name}/om-esign' title='/plone/outgoing-mail/{folder_name}/om-esign'>Courrier test esign</a>,
563+
'filename': u'R\\xe9ponse salle__{pdf2_uid}.pdf',
564+
'scan_id': '012999900000601',
565+
'status': '',
566+
'title': u'R\\xe9ponse salle.pdf',
567+
'uid': <a href='http://nohost/plone/outgoing-mail/{folder_name}/om-esign/reponse-salle-1.pdf' title='/plone/outgoing-mail/{folder_name}/om-esign/reponse-salle-1.pdf'>Réponse salle.pdf</a>,
568+
}},
569+
],
570+
'last_update': {last_update},
571+
'returns': [],
572+
'seal': False,
573+
'sign_id': '012999900000',
574+
'sign_url': None,
575+
'signers': [
576+
{{
577+
'email': 'dirg@macommune.be',
578+
'fullname': u'Maxime DG',
579+
'position': u'Directeur G\\xe9n\\xe9ral',
580+
'status': '',
581+
'userid': 'dirg',
582+
}},
583+
{{
584+
'email': 'bourgmestre@macommune.be',
585+
'fullname': u'Paul BM',
586+
'position': u'Bourgmestre',
587+
'status': '',
588+
'userid': 'bourgmestre',
589+
}},
590+
],
591+
'size': 54660,
592+
'state': 'draft',
593+
'title': u'[ia.docs] Session 012999900000',
594+
'watchers': [],
595+
}}""".format(
596+
pdf1_uid=api.content.get(omail.absolute_url_path() + "/reponse-salle.pdf").UID(),
597+
pdf2_uid=api.content.get(omail.absolute_url_path() + "/reponse-salle-1.pdf").UID(),
598+
folder_name=omail.__parent__.__name__,
599+
last_update=repr(get_session_annotation()["sessions"][0]["last_update"]),
600+
),
601+
)

0 commit comments

Comments
 (0)