Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 40 additions & 36 deletions docassemble/AssemblyLine/al_document.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import re
import os
import json
import mimetypes
import os
import re
from html import escape
from typing import Any, Dict, List, Literal, Union, Callable, Optional
from docassemble.base.util import (
Address,
Expand Down Expand Up @@ -175,6 +177,14 @@ def html_safe_str(the_string: str) -> str:
return re.sub(r"[^A-Za-z0-9]+", "_", the_string)


def _javascript_href(function_name: str, *args: Any) -> str:
"""Build a JavaScript URL that is safe to use in an HTML href attribute."""
return escape(
f"javascript:{function_name}({','.join(json.dumps(arg) for arg in args)})",
quote=True,
)


def table_row(title: str, button_htmls: List[str] = []) -> str:
"""
Generate an HTML row string for an AL document-styled table.
Expand Down Expand Up @@ -2408,10 +2418,11 @@ def send_email_table_row(
al_email_input_id = "_ignore_al_doc_email_" + name
al_send_button_id = "al_send_email_button_" + name

javascript_string = (
f"javascript:aldocument_send_action("
f"'{self.attr_name('send_email_action_event')}',"
f"'{al_wants_editable_input_id}','{al_email_input_id}')"
javascript_string = _javascript_href(
"aldocument_send_action",
str(self.attr_name("send_email_action_event")),
al_wants_editable_input_id,
al_email_input_id,
)

# Label "email" and input field for the 1st column of the table row
Expand Down Expand Up @@ -2474,23 +2485,23 @@ def send_button_to_html(
name = html_safe_str(self.instanceName) + random_suffix()
al_send_button_id = "al_send_email_to_button_" + name

formats_value: Optional[Union[str, List[str]]]
if isinstance(preferred_formats, (list, tuple)):
formats_js = "[" + ",".join(f"'{fmt}'" for fmt in preferred_formats) + "]"
formats_value = [str(fmt) for fmt in preferred_formats]
elif preferred_formats:
formats_js = f"'{preferred_formats}'"
formats_value = str(preferred_formats)
else:
formats_js = "null"

javascript_string = (
f"javascript:aldocument_send_to_action("
f"'{self.attr_name('send_email_to_action_event')}',"
f"'{editable}',"
f"'{email}',"
f"'{al_send_button_id}',"
f"'{template_name}',"
f"'{key}',"
f"{formats_js}"
f")"
formats_value = None

javascript_string = _javascript_href(
"aldocument_send_to_action",
str(self.attr_name("send_email_to_action_event")),
str(editable),
str(email),
al_send_button_id,
str(template_name),
str(key),
formats_value,
)
send_button = action_button_html(
javascript_string,
Expand Down Expand Up @@ -2560,22 +2571,15 @@ def send_button_html(
al_email_input_id = "_ignore_al_doc_email_" + name
al_send_button_id = "al_send_email_button_" + name

if isinstance(preferred_formats, (list, tuple)):
formats_js = "[" + ",".join(f"'{fmt}'" for fmt in preferred_formats) + "]"
elif preferred_formats:
formats_js = f"'{preferred_formats}'"
else:
formats_js = "null"

javascript_string = (
f"javascript:aldocument_send_action("
f"'{self.attr_name('send_email_action_event')}',"
f"'{al_wants_editable_input_id}',"
f"'{al_email_input_id}',"
f"'{template_name}',"
f"'{key}',"
f"{formats_js}"
")"
formats_value = [str(fmt) for fmt in preferred_formats]
javascript_string = _javascript_href(
"aldocument_send_action",
str(self.attr_name("send_email_action_event")),
al_wants_editable_input_id,
al_email_input_id,
str(template_name),
str(key),
formats_value,
)

# Container of whole email section with header
Expand Down
31 changes: 30 additions & 1 deletion docassemble/AssemblyLine/test_al_document.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,37 @@
# do not pre-load

import json
import unittest
from html import unescape
from docassemble.base.util import DAFile, DATemplate
from .al_document import ALDocument, ALDocumentBundle, ALAddendumField
from .al_document import ALDocument, ALDocumentBundle, ALAddendumField, _javascript_href


class TestJavascriptHref(unittest.TestCase):
def test_escapes_javascript_strings_inside_html_href(self):
result = _javascript_href(
"aldocument_send_action",
"template_request.requestee_bundles['4167e36be7f04794ad30770e865afe68']",
"_ignore_al_wants_editable_bundle",
"_ignore_al_doc_email_bundle",
)

self.assertEqual(
result,
"javascript:aldocument_send_action("
""template_request.requestee_bundles['4167e36be7f04794ad30770e865afe68']","
""_ignore_al_wants_editable_bundle","
""_ignore_al_doc_email_bundle")",
)

def test_preserves_javascript_value_types(self):
result = unescape(_javascript_href("send", "None", None, ["pdf", "docx"]))
args_json = result.removeprefix("javascript:send(").removesuffix(")")

self.assertEqual(
json.loads(f"[{args_json}]"),
["None", None, ["pdf", "docx"]],
)


class test_dont_assume_pdf(unittest.TestCase):
Expand Down
Loading