Skip to content

Commit 0f49254

Browse files
committed
Added download QR code page to docx and pdf files to be signed
1 parent f7c5959 commit 0f49254

File tree

4 files changed

+262
-35
lines changed

4 files changed

+262
-35
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ Changelog
4848
[chris-adam]
4949
- Replaced default actions bar by actionspanel for iconified categories.
5050
[chris-adam]
51+
- Added download QR code page to docx and pdf files to be signed.
52+
[chris-adam]
5153

5254
3.0 (2021-09-30)
5355
----------------

imio/dms/mail/adapters.py

Lines changed: 119 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
from collective.dms.scanbehavior.behaviors.behaviors import IScanFields
1717
from collective.documentgenerator import _ as _dg
1818
from collective.documentgenerator.utils import convert_and_save_file
19+
from collective.documentgenerator.utils import convert_odt
1920
from collective.documentgenerator.utils import get_original_template
2021
from collective.documentgenerator.utils import odfsplit
2122
from collective.documentgenerator.utils import update_dict_with_validation
23+
from collective.documentviewer.convert import Converter
2224
from collective.iconifiedcategory.adapter import CategorizedObjectInfoAdapter
2325
from collective.iconifiedcategory.utils import get_category_object
2426
from collective.iconifiedcategory.utils import update_categorized_elements
@@ -50,6 +52,7 @@
5052
from imio.helpers.content import uuidToCatalogBrain
5153
from imio.helpers.content import uuidToObject
5254
from imio.helpers.emailer import validate_email_address
55+
from imio.helpers.pdf import merge_pdf
5356
from imio.helpers.workflow import do_transitions
5457
from imio.pm.wsclient.interfaces import ISendableAnnexesToPM
5558
from imio.prettylink.adapters import PrettyLinkAdapter
@@ -61,6 +64,7 @@
6164
from plone.app.contentmenu.menu import FactoriesSubMenuItem as OrigFactoriesSubMenuItem
6265
from plone.app.contentmenu.menu import WorkflowMenu as OrigWorkflowMenu
6366
from plone.app.contenttypes.indexers import _unicode_save_string_concat
67+
from plone.dexterity.utils import createContentInContainer
6468
from plone.indexer import indexer
6569
from plone.namedfile.file import NamedBlobFile
6670
from plone.registry.interfaces import IRegistry
@@ -1617,8 +1621,11 @@ def approve_file(self, afile, userid, values=None, transition=None, c_a=None):
16171621
message=_(
16181622
u"The file '${file}' has been approved by ${user}. However, there is/are yet ${nb} files "
16191623
u"to approve on this mail.",
1620-
mapping={"file": safe_unicode(afile.Title()), "user": safe_unicode(fullname),
1621-
"nb": len(yet_to_approve)},
1624+
mapping={
1625+
"file": safe_unicode(afile.Title()),
1626+
"user": safe_unicode(fullname),
1627+
"nb": len(yet_to_approve),
1628+
},
16221629
),
16231630
request=request,
16241631
type="info",
@@ -1635,8 +1642,10 @@ def approve_file(self, afile, userid, values=None, transition=None, c_a=None):
16351642
self.context.reindexObjectSecurity() # to update local roles from adapter
16361643
message += u"Next approval number is ${nb}."
16371644
api.portal.show_message(
1638-
message=_(message, mapping={"file": safe_unicode(afile.Title()), "user": safe_unicode(fullname),
1639-
"nb": c_a + 1}),
1645+
message=_(
1646+
message,
1647+
mapping={"file": safe_unicode(afile.Title()), "user": safe_unicode(fullname), "nb": c_a + 1},
1648+
),
16401649
request=request,
16411650
type="info",
16421651
)
@@ -1714,6 +1723,36 @@ def unapprove_file(self, afile, signer_userid):
17141723

17151724
self.start_approval_process()
17161725

1726+
def _render_download_template_to_pdf(self, uid):
1727+
"""Render the download subtemplate (QR code page) to PDF bytes.
1728+
1729+
:param uid: uid of the document to generate the download link for
1730+
:return: PDF bytes, or empty string on failure
1731+
"""
1732+
try:
1733+
download_template = api.portal.get().templates.om.get("download_barcode")
1734+
download_url, _short_uid = get_file_download_url(uid, short_uid=get_suid_from_uuid(uid))
1735+
helper_view = getMultiAdapter(
1736+
(self.context, self.context.REQUEST),
1737+
name="document_generation_helper_view",
1738+
)
1739+
helper_view.pod_template = download_template.UID()
1740+
helper_view.output_format = "pdf"
1741+
gen_context = {
1742+
"context": self.context,
1743+
"portal": api.portal.get(),
1744+
"view": helper_view,
1745+
"download_barcode": generate_barcode(download_url).read(),
1746+
"download_url": download_url,
1747+
"max_download_date": get_max_download_date(None, adate=datetime.date.today()),
1748+
"render_download_barcode": True,
1749+
}
1750+
template_file = NamedBlobFile(download_template.get_file().data, filename=u"download_template.odt")
1751+
return convert_odt(template_file, fmt="pdf", gen_context=gen_context)
1752+
except Exception:
1753+
logger.exception("Could not render download template to PDF")
1754+
return ""
1755+
17171756
def _create_pdf_file(self, orig_fobj, nbf, f_title, f_uid, file_index, session_file_uids):
17181757
"""Create a pdf version file.
17191758
@@ -1725,33 +1764,75 @@ def _create_pdf_file(self, orig_fobj, nbf, f_title, f_uid, file_index, session_f
17251764
:param session_file_uids: list to append created pdf file uids
17261765
:return: created pdf file object
17271766
"""
1767+
1768+
def update_esign_attributes(pdf_file, orig_fobj):
1769+
"""Set required attributes on a newly created PDF file object."""
1770+
pdf_file.to_sign = True
1771+
pdf_file.to_approve = False
1772+
pdf_file.approved = orig_fobj.approved
1773+
update_categorized_elements(
1774+
self.context,
1775+
pdf_file,
1776+
get_category_object(self.context, pdf_file.content_category),
1777+
limited=True,
1778+
sort=False,
1779+
logging=True,
1780+
)
1781+
17281782
new_filename = u"{}.pdf".format(f_title)
17291783
if nbf.contentType == "application/pdf":
17301784
pdf_file = orig_fobj
1785+
new_uid = uuid.uuid4().hex
1786+
download_page = self._render_download_template_to_pdf(new_uid)
1787+
if download_page:
1788+
merged = merge_pdf(nbf.data, download_page)
1789+
file_object = NamedBlobFile(merged, filename=safe_unicode(new_filename))
1790+
pdf_file = createContentInContainer(
1791+
self.context,
1792+
orig_fobj.portal_type,
1793+
title=safe_unicode(new_filename),
1794+
file=file_object,
1795+
content_category=orig_fobj.content_category,
1796+
scan_id=orig_fobj.scan_id,
1797+
conv_from_uid=f_uid,
1798+
**{"_plone.uuid": new_uid}
1799+
)
1800+
annot = IAnnotations(pdf_file)
1801+
annot["documentgenerator"] = {"conv_from_uid": f_uid}
1802+
update_esign_attributes(pdf_file, orig_fobj)
17311803
elif nbf.contentType in get_allowed_omf_content_types(esign=True):
17321804
gen_context = {}
17331805
new_uid = uuid.uuid4().hex
1734-
download_url, s_uid = get_file_download_url(new_uid, short_uid=get_suid_from_uuid(new_uid))
1806+
download_url, _s_uid = get_file_download_url(new_uid, short_uid=get_suid_from_uuid(new_uid))
17351807
orig_template = get_original_template(orig_fobj)
1808+
doc_cb_download_added = False
17361809
if orig_template and nbf.contentType == "application/vnd.oasis.opendocument.text": # own document
1737-
helper_view = getMultiAdapter((self.context, self.context.REQUEST),
1738-
name='document_generation_helper_view')
1810+
helper_view = getMultiAdapter(
1811+
(self.context, self.context.REQUEST), name="document_generation_helper_view"
1812+
)
17391813
helper_view.pod_template = orig_template.UID()
17401814
helper_view.output_format = "pdf"
17411815
gen_context = {"context": self.context, "portal": api.portal.get(), "view": helper_view}
17421816
# update_dict_with_validation(gen_context, self._get_context_variables(pod_template),
17431817
# _("Error when merging context_variables in generation context"))
1744-
merge_templates = [dic["template"] for dic in orig_template.merge_templates
1745-
if dic["pod_context_name"] == "doc_cb_download"]
1818+
merge_templates = [
1819+
dic["template"]
1820+
for dic in orig_template.merge_templates
1821+
if dic["pod_context_name"] == "doc_cb_download"
1822+
]
17461823
if merge_templates:
17471824
download_template = uuidToObject(merge_templates[0])
17481825
if download_template:
17491826
gen_context["doc_cb_download"] = download_template
1827+
doc_cb_download_added = True
17501828
update_dict_with_validation(
17511829
gen_context,
1752-
{"download_barcode": generate_barcode(download_url).read(), "download_url": download_url,
1753-
"max_download_date": get_max_download_date(None, adate=datetime.date.today()),
1754-
"render_download_barcode": True},
1830+
{
1831+
"download_barcode": generate_barcode(download_url).read(),
1832+
"download_url": download_url,
1833+
"max_download_date": get_max_download_date(None, adate=datetime.date.today()),
1834+
"render_download_barcode": True,
1835+
},
17551836
_dg("Error when merging 'download_barcode' in generation context"),
17561837
)
17571838

@@ -1772,21 +1853,18 @@ def _create_pdf_file(self, orig_fobj, nbf, f_title, f_uid, file_index, session_f
17721853
gen_context=gen_context,
17731854
)
17741855
# we must set attribute after creation
1775-
pdf_file.to_sign = True
1776-
pdf_file.to_approve = False
1777-
pdf_file.approved = orig_fobj.approved
1778-
update_categorized_elements(
1779-
self.context,
1780-
pdf_file,
1781-
get_category_object(self.context, pdf_file.content_category),
1782-
limited=True,
1783-
sort=False,
1784-
logging=True,
1785-
)
1856+
update_esign_attributes(pdf_file, orig_fobj)
1857+
1858+
# For non-ODT files (e.g. DOC, DOCX), the subtemplate cannot be merged during conversion.
1859+
# Render it to PDF separately and append it to the converted PDF.
1860+
if not doc_cb_download_added:
1861+
download_page = self._render_download_template_to_pdf(new_uid)
1862+
if download_page:
1863+
merged = merge_pdf(pdf_file.file.data, download_page)
1864+
pdf_file.file = NamedBlobFile(merged, filename=pdf_file.file.filename)
1865+
Converter(pdf_file)() # Refresh pdf preview
17861866
else:
1787-
raise NotImplementedError(
1788-
"Cannot convert file of type '{}' to pdf for signing.".format(nbf.contentType)
1789-
)
1867+
raise NotImplementedError("Cannot convert file of type '{}' to pdf for signing.".format(nbf.contentType))
17901868
pdf_uid = pdf_file.UID()
17911869
self.pdf_files_uids[file_index].append(pdf_uid)
17921870
# we rename the pdf filename to include pdf uid. So after the file is later consumed, we can retrieve object
@@ -1856,21 +1934,28 @@ def add_mail_files_to_session(self):
18561934
signers.append((signer, email, name, label))
18571935
watcher_users = api.user.get_users(groupname="esign_watchers")
18581936
watcher_emails = [user.getProperty("email") for user in watcher_users]
1859-
session_id, session = add_files_to_session(signers, session_file_uids, bool(self.context.seal),
1860-
title=_("[ia.docs] Session {sign_id}"),
1861-
watchers=watcher_emails)
1937+
session_id, _session = add_files_to_session(
1938+
signers,
1939+
session_file_uids,
1940+
bool(self.context.seal),
1941+
title=_("[ia.docs] Session {sign_id}"),
1942+
watchers=watcher_emails,
1943+
)
18621944
self.annot["session_id"] = session_id
18631945
session_len = len(session_file_uids)
18641946
if session_len > 1:
1865-
return True, _("${count} files added to session number ${session_id}",
1866-
mapping={"count": session_len, "session_id": session_id})
1947+
return True, _(
1948+
"${count} files added to session number ${session_id}",
1949+
mapping={"count": session_len, "session_id": session_id},
1950+
)
18671951
else:
1868-
return True, _("${count} file added to session number ${session_id}",
1869-
mapping={"count": session_len, "session_id": session_id})
1952+
return True, _(
1953+
"${count} file added to session number ${session_id}",
1954+
mapping={"count": session_len, "session_id": session_id},
1955+
)
18701956

18711957

18721958
class DmsCategorizedObjectInfoAdapter(CategorizedObjectInfoAdapter):
1873-
18741959
def get_infos(self, category, limited=False):
18751960
base_infos = super(DmsCategorizedObjectInfoAdapter, self).get_infos(category, limited=limited)
18761961
base_infos["scan_id"] = getattr(self.obj, "scan_id", None)

0 commit comments

Comments
 (0)