Skip to content

Commit

Permalink
Merge pull request #222 from frappe/for-release-1-12
Browse files Browse the repository at this point in the history
  • Loading branch information
surajshetty3416 authored Oct 15, 2024
2 parents 925830a + 476c10c commit 83e05ae
Show file tree
Hide file tree
Showing 169 changed files with 4,388 additions and 2,057 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ repos:
.*node_modules.*|
.*boilerplate.*|
.*src.*.js|
builder/public/js/identify.js|
)$
ci:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<h2>Frappe Builder</h2>
<p>Crafting Web Pages Made Effortless!</p>

![Frappe Builder](https://github.com/frappe/builder/assets/13928957/e39f1057-4b60-4d1f-b8e9-049668738da6)
![Frappe Builder](https://github.com/user-attachments/assets/e906545e-101e-4d55-8a25-2c4f6380ea5e)
[Web page design credit](https://www.figma.com/community/file/949266436474872912)
</div>

Expand Down
155 changes: 155 additions & 0 deletions builder/api.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import json
import os
from io import BytesIO
from urllib.parse import unquote

import frappe
import requests
from frappe.apps import get_apps as get_permitted_apps
from frappe.core.doctype.file.file import get_local_image
from frappe.core.doctype.file.utils import delete_file
from frappe.integrations.utils import make_post_request
from frappe.model.document import Document
from frappe.utils.caching import redis_cache
from frappe.utils.telemetry import POSTHOG_HOST_FIELD, POSTHOG_PROJECT_FIELD
from PIL import Image

from builder.builder.doctype.builder_page.builder_page import BuilderPageRenderer


@frappe.whitelist()
Expand Down Expand Up @@ -52,6 +64,132 @@ def get_posthog_settings():
}


@frappe.whitelist()
def get_page_preview_html(page: str, **kwarg) -> str:
# to load preview without publishing
frappe.form_dict.update(kwarg)
renderer = BuilderPageRenderer(path="")
renderer.docname = page
renderer.doctype = "Builder Page"
frappe.flags.show_preview = True
frappe.local.no_cache = 1
renderer.init_context()
response = renderer.render()
page = frappe.get_cached_doc("Builder Page", page)
frappe.enqueue_doc(
page.doctype,
page.name,
"generate_page_preview_image",
html=str(response.data, "utf-8"),
queue="short",
)
return response


@frappe.whitelist()
def upload_builder_asset():
from frappe.handler import upload_file

image_file = upload_file()
if image_file.file_url.endswith((".png", ".jpeg", ".jpg")) and frappe.get_cached_value(
"Builder Settings", None, "auto_convert_images_to_webp"
):
convert_to_webp(file_doc=image_file)
return image_file


@frappe.whitelist()
def convert_to_webp(image_url: str | None = None, file_doc: Document | None = None) -> str:
"""BETA: Convert image to webp format"""

CONVERTIBLE_IMAGE_EXTENSIONS = ["png", "jpeg", "jpg"]

def can_convert_image(extn):
return extn.lower() in CONVERTIBLE_IMAGE_EXTENSIONS

def get_extension(filename):
return filename.split(".")[-1].lower()

def convert_and_save_image(image, path):
image.save(path, "WEBP")
return path

def update_file_doc_with_webp(file_doc, image, extn):
webp_path = file_doc.get_full_path().replace(extn, "webp")
convert_and_save_image(image, webp_path)
delete_file(file_doc.get_full_path())
file_doc.file_url = f"{file_doc.file_url.replace(extn, 'webp')}"
file_doc.save()
return file_doc.file_url

def create_new_webp_file_doc(file_url, image, extn):
files = frappe.get_all("File", filters={"file_url": file_url}, fields=["name"], limit=1)
if files:
_file = frappe.get_doc("File", files[0].name)
webp_path = _file.get_full_path().replace(extn, "webp")
convert_and_save_image(image, webp_path)
new_file = frappe.copy_doc(_file)
new_file.file_name = f"{_file.file_name.replace(extn, 'webp')}"
new_file.file_url = f"{_file.file_url.replace(extn, 'webp')}"
new_file.save()
return new_file.file_url
return file_url

def handle_image_from_url(image_url):
image_url = unquote(image_url)
response = requests.get(image_url)
image = Image.open(BytesIO(response.content))
filename = image_url.split("/")[-1]
extn = get_extension(filename)
if can_convert_image(extn):
_file = frappe.get_doc(
{
"doctype": "File",
"file_name": f"{filename.replace(extn, 'webp')}",
"file_url": f"/files/{filename.replace(extn, 'webp')}",
}
)
webp_path = _file.get_full_path()
convert_and_save_image(image, webp_path)
_file.save()
return _file.file_url
return image_url

if not image_url and not file_doc:
return ""

if file_doc:
if file_doc.file_url.startswith("/files"):
image, filename, extn = get_local_image(file_doc.file_url)
if can_convert_image(extn):
return update_file_doc_with_webp(file_doc, image, extn)
return file_doc.file_url

if image_url.startswith("/files"):
image, filename, extn = get_local_image(image_url)
if can_convert_image(extn):
return create_new_webp_file_doc(image_url, image, extn)
return image_url

if image_url.startswith("/builder_assets"):
image_path = os.path.abspath(frappe.get_app_path("builder", "www", image_url.lstrip("/")))
image_path = image_path.replace("_", "-")
image_path = image_path.replace("/builder-assets", "/builder_assets")

image = Image.open(image_path)
extn = get_extension(image_path)
if can_convert_image(extn):
webp_path = image_path.replace(extn, "webp")
convert_and_save_image(image, webp_path)
return image_url.replace(extn, "webp")
return image_url

if image_url.startswith("http"):
return handle_image_from_url(image_url)

return image_url


def check_app_permission():
if frappe.session.user == "Administrator":
return True
Expand All @@ -60,3 +198,20 @@ def check_app_permission():
return True

return False


@frappe.whitelist()
@redis_cache()
def get_apps():
apps = get_permitted_apps()
app_list = [
{
"name": "frappe",
"logo": "/assets/builder/images/desk.png",
"title": "Desk",
"route": "/app",
}
]
app_list += filter(lambda app: app.get("name") != "builder", apps)

return app_list
17 changes: 17 additions & 0 deletions builder/builder/builder_block_template/blockquote/blockquote.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"block": "{\"attributes\": {},\"baseStyles\": {\"display\": \"flex\",\"flexDirection\": \"column\",\"flexShrink\": 0,\"left\": \"auto\",\"overflow\": \"hidden\",\"position\": \"static\",\"top\": \"auto\",\"width\": \"100%\"},\"blockId\": \"udqa5zyxm\",\"children\": [{\"attributes\": {},\"baseStyles\": {\"fontSize\": \"16px\",\"height\": \"fit-content\",\"lineHeight\": \"1.4\",\"minWidth\": \"10px\",\"position\": \"static\",\"width\": \"fit-content\"},\"blockId\": \"98f44tdju\",\"children\": [],\"classes\": [],\"customAttributes\": {},\"dataKey\": null,\"element\": \"p\",\"innerHTML\": \"<p>Words can be like X-rays, if you use them properly\\u2014they\\u2019ll go through anything. You read and you\\u2019re pierced.</p>\",\"mobileStyles\": {},\"rawStyles\": {},\"tabletStyles\": {}},{\"attributes\": {},\"baseStyles\": {\"display\": \"flex\",\"flexDirection\": \"row\",\"flexShrink\": 0,\"gap\": \"6px\",\"height\": \"fit-content\",\"overflow\": \"hidden\",\"position\": \"relative\",\"width\": \"fit-content\"},\"blockId\": \"ztvga4ewc\",\"blockName\": \"container\",\"children\": [{\"attributes\": {},\"baseStyles\": {\"height\": \"fit-content\",\"width\": \"fit-content\"},\"blockId\": \"pxv3ojq7t\",\"children\": [],\"classes\": [],\"customAttributes\": {},\"dataKey\": null,\"element\": \"div\",\"innerHTML\": \"<p>\\u2014Aldous Huxley,\",\"mobileStyles\": {},\"originalElement\": \"__raw_html__\",\"rawStyles\": {},\"tabletStyles\": {}},{\"attributes\": {},\"baseStyles\": {\"background\": \"#e2e2e2\",\"display\": \"flex\",\"flexDirection\": \"column\",\"flexShrink\": 0,\"overflow\": \"hidden\",\"position\": \"static\"},\"blockId\": \"bglu90k42\",\"children\": [],\"classes\": [],\"customAttributes\": {},\"dataKey\": null,\"element\": \"cite\",\"innerHTML\": \"<p>Brave New World</p>\",\"mobileStyles\": {},\"rawStyles\": {},\"tabletStyles\": {}}],\"classes\": [],\"customAttributes\": {},\"dataKey\": null,\"element\": \"div\",\"mobileStyles\": {},\"rawStyles\": {},\"tabletStyles\": {}}],\"classes\": [],\"customAttributes\": {\"cite\": \"https://www.huxley.net/bnw/four.html\"},\"dataKey\": null,\"element\": \"blockquote\",\"mobileStyles\": {},\"rawStyles\": {},\"tabletStyles\": {}}",
"category": "Typography",
"creation": "2024-09-03 23:17:52.830414",
"docstatus": 0,
"doctype": "Block Template",
"idx": 0,
"modified": "2024-09-19 13:10:00.947270",
"modified_by": "Administrator",
"name": "Blockquote",
"order": 13,
"owner": "Administrator",
"preview": "/builder_assets/Blockquote/blockquote.png",
"preview_height": 1,
"preview_width": 1,
"template_name": "Blockquote"
}
7 changes: 5 additions & 2 deletions builder/builder/builder_block_template/button/button.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
{
"block": "{\"blockId\":\"4ur2l2mc6\",\"children\":[{\"blockId\":\"3z5gwjl2f\",\"children\":[],\"baseStyles\":{\"color\":\"var(--neutral-white, #FFF)\",\"fontSize\":\"14px\",\"fontWeight\":\"420\",\"height\":\"fit-content\",\"left\":\"auto\",\"letterSpacing\":\"0.28px\",\"lineHeight\":\"115%\",\"minWidth\":\"30px\",\"position\":\"static\",\"top\":\"auto\",\"width\":\"fit-content\"},\"rawStyles\":{},\"mobileStyles\":{},\"tabletStyles\":{},\"attributes\":{},\"classes\":[\"__text_block__\"],\"dataKey\":null,\"element\":\"p\",\"innerHTML\":\"<p>Button</p>\",\"customAttributes\":{}}],\"baseStyles\":{\"background\":\"#171717\",\"borderRadius\":\"4px\",\"display\":\"flex\",\"flexDirection\":\"column\",\"height\":\"fit-content\",\"padding\":\"6px 8px\",\"width\":\"fit-content\"},\"rawStyles\":{\"flex-shrink\":\"0\",\"hover:background\":\"#383838\",\"transition\":\"all 0.1s ease-out\"},\"mobileStyles\":{},\"tabletStyles\":{},\"attributes\":{\"href\":\"#\"},\"classes\":[\"__text_block__\"],\"dataKey\":null,\"blockName\":\"container\",\"element\":\"a\",\"customAttributes\":{}}",
"block": "{\"attributes\": {\"href\": \"#\"},\"baseStyles\": {\"alignItems\": \"center\",\"background\": \"#171717\",\"borderRadius\": \"4px\",\"display\": \"flex\",\"flexDirection\": \"column\",\"height\": \"fit-content\",\"justifyContent\": \"center\",\"padding\": \"6px 8px\",\"width\": \"fit-content\"},\"blockId\": \"4ur2l2mc6\",\"blockName\": \"button-link\",\"children\": [{\"attributes\": {},\"baseStyles\": {\"color\": \"var(--neutral-white, #FFF)\",\"fontSize\": \"14px\",\"fontWeight\": \"420\",\"height\": \"fit-content\",\"left\": \"auto\",\"letterSpacing\": \"0.28px\",\"lineHeight\": \"115%\",\"minWidth\": \"30px\",\"position\": \"static\",\"top\": \"auto\",\"width\": \"fit-content\"},\"blockId\": \"3z5gwjl2f\",\"children\": [],\"classes\": [\"__text_block__\"],\"customAttributes\": {},\"dataKey\": null,\"element\": \"p\",\"innerHTML\": \"<p>Button</p>\",\"mobileStyles\": {},\"rawStyles\": {},\"tabletStyles\": {}}],\"classes\": [\"__text_block__\"],\"customAttributes\": {},\"dataKey\": null,\"editable\": false,\"element\": \"a\",\"mobileStyles\": {},\"rawStyles\": {\"flex-shrink\": \"0\",\"hover:background\": \"#383838\",\"transition\": \"all 0.1s ease-out\"},\"tabletStyles\": {}}",
"category": "Basic",
"creation": "2024-07-10 14:44:08.816465",
"docstatus": 0,
"doctype": "Block Template",
"idx": 0,
"modified": "2024-07-12 09:34:22.716694",
"modified": "2024-09-19 13:08:10.275886",
"modified_by": "Administrator",
"name": "Button",
"order": 1,
"owner": "Administrator",
"preview": "/builder_assets/Button/button.png",
"preview_height": 1,
"preview_width": 1,
"template_name": "Button"
}
4 changes: 2 additions & 2 deletions builder/builder/builder_block_template/embed/embed.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"block": "{\"blockId\":\"1vw78v0md\",\"children\":[],\"baseStyles\":{\"height\":\"fit-content\",\"width\":\"fit-content\"},\"rawStyles\":{},\"mobileStyles\":{},\"tabletStyles\":{},\"attributes\":{},\"classes\":[],\"dataKey\":null,\"blockName\":\"Embed HTML\",\"element\":\"div\",\"innerHTML\":\"<div style=\\\"color:#8e8e8e;background:#f4f4f4;display:grid;width:500px;height:155px;place-items:center;font-size:16px;\\\">\\n <p>Paste HTML or double click to edit</p>\\n</div>\",\"originalElement\":\"__raw_html__\",\"customAttributes\":{}}",
"category": "Basic",
"category": "Advanced",
"creation": "2024-07-10 17:07:00.407251",
"docstatus": 0,
"doctype": "Block Template",
"idx": 0,
"modified": "2024-07-12 09:34:18.897182",
"modified": "2024-09-03 22:16:29.411961",
"modified_by": "Administrator",
"name": "Embed",
"owner": "Administrator",
Expand Down
17 changes: 17 additions & 0 deletions builder/builder/builder_block_template/footer_1/footer_1.json

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions builder/builder/builder_block_template/form_1/form_1.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
{
"block": "{\"blockId\":\"2e45tti0c\",\"children\":[{\"blockId\":\"86oxik4s9\",\"children\":[],\"baseStyles\":{\"display\":\"flex\",\"flexDirection\":\"column\",\"flexShrink\":0,\"overflow\":\"hidden\",\"position\":\"static\",\"width\":\"100%\",\"height\":\"32px\",\"borderRadius\":\"4px\",\"borderColor\":\"#c9c9c9\",\"borderWidth\":\"1px\",\"borderStyle\":\"solid\",\"fontSize\":\"14px\"},\"rawStyles\":{},\"mobileStyles\":{},\"tabletStyles\":{},\"attributes\":{\"placeholder\":\"Full Name\"},\"classes\":[],\"dataKey\":null,\"element\":\"input\",\"customAttributes\":{\"name\":\"full_name\",\"required\":\"true\"}},{\"blockId\":\"wzbvukqlo\",\"children\":[],\"baseStyles\":{\"display\":\"flex\",\"flexDirection\":\"column\",\"flexShrink\":0,\"overflow\":\"hidden\",\"position\":\"static\",\"width\":\"100%\",\"height\":\"32px\",\"borderRadius\":\"4px\",\"borderColor\":\"#c9c9c9\",\"borderWidth\":\"1px\",\"borderStyle\":\"solid\",\"fontSize\":\"14px\"},\"rawStyles\":{},\"mobileStyles\":{},\"tabletStyles\":{},\"attributes\":{\"placeholder\":\"Email\"},\"classes\":[],\"dataKey\":null,\"element\":\"input\",\"customAttributes\":{\"name\":\"email\",\"required\":\"true\"}},{\"blockId\":\"4rclutff4\",\"children\":[],\"baseStyles\":{\"display\":\"flex\",\"flexDirection\":\"column\",\"flexShrink\":0,\"overflow\":\"hidden\",\"position\":\"static\",\"top\":\"auto\",\"left\":\"auto\",\"width\":\"100%\",\"height\":\"150px\",\"borderRadius\":\"4px\",\"borderColor\":\"#c9c9c9\",\"borderWidth\":\"1px\",\"borderStyle\":\"solid\",\"fontSize\":\"14px\"},\"rawStyles\":{},\"mobileStyles\":{},\"tabletStyles\":{},\"attributes\":{\"placeholder\":\"Message\"},\"classes\":[],\"dataKey\":null,\"element\":\"textarea\",\"customAttributes\":{\"name\":\"message\",\"required\":\"true\"}},{\"blockId\":\"uxry21e1i\",\"children\":[{\"blockId\":\"yjglepmnh\",\"children\":[],\"baseStyles\":{\"color\":\"var(--neutral-white, #FFF)\",\"fontSize\":\"14px\",\"fontWeight\":\"420\",\"height\":\"fit-content\",\"left\":\"auto\",\"letterSpacing\":\"0.28px\",\"lineHeight\":\"115%\",\"minWidth\":\"30px\",\"position\":\"static\",\"top\":\"auto\",\"width\":\"fit-content\"},\"rawStyles\":{},\"mobileStyles\":{},\"tabletStyles\":{},\"attributes\":{},\"classes\":[\"__text_block__\"],\"dataKey\":null,\"element\":\"p\",\"innerHTML\":\"<p>Submit</p>\",\"customAttributes\":{}}],\"baseStyles\":{\"background\":\"#171717\",\"borderRadius\":\"4px\",\"display\":\"flex\",\"flexDirection\":\"column\",\"height\":\"fit-content\",\"padding\":\"6px 8px\",\"width\":\"100%\",\"justifyContent\":\"flex-start\",\"alignItems\":\"center\"},\"rawStyles\":{\"flex-shrink\":\"0\",\"hover:background\":\"#383838\",\"transition\":\"all 0.1s ease-out\"},\"mobileStyles\":{},\"tabletStyles\":{},\"attributes\":{},\"classes\":[\"\"],\"dataKey\":null,\"element\":\"button\",\"customAttributes\":{}}],\"baseStyles\":{\"display\":\"flex\",\"flexDirection\":\"column\",\"flexShrink\":0,\"overflow\":\"hidden\",\"position\":\"static\",\"top\":\"auto\",\"left\":\"auto\",\"width\":\"100%\",\"paddingLeft\":\"20px\",\"paddingRight\":\"20px\",\"paddingBottom\":\"20px\",\"paddingTop\":\"20px\",\"maxWidth\":\"400px\",\"gap\":\"15px\"},\"rawStyles\":{},\"mobileStyles\":{},\"tabletStyles\":{},\"attributes\":{},\"classes\":[],\"dataKey\":null,\"element\":\"form\",\"customAttributes\":{}}",
"category": "Basic",
"block": "{\"attributes\": {},\"baseStyles\": {\"display\": \"flex\",\"flexDirection\": \"column\",\"flexShrink\": 0,\"gap\": \"15px\",\"left\": \"auto\",\"maxWidth\": \"400px\",\"overflow\": \"hidden\",\"paddingBottom\": \"20px\",\"paddingLeft\": \"20px\",\"paddingRight\": \"20px\",\"paddingTop\": \"20px\",\"position\": \"static\",\"top\": \"auto\",\"width\": \"100%\"},\"blockId\": \"2e45tti0c\",\"children\": [{\"attributes\": {\"placeholder\": \"Full Name\"},\"baseStyles\": {\"borderColor\": \"#c9c9c9\",\"borderRadius\": \"4px\",\"borderStyle\": \"solid\",\"borderWidth\": \"1px\",\"display\": \"flex\",\"flexDirection\": \"column\",\"flexShrink\": 0,\"fontSize\": \"14px\",\"height\": \"32px\",\"overflow\": \"hidden\",\"position\": \"static\",\"width\": \"100%\"},\"blockId\": \"86oxik4s9\",\"children\": [],\"classes\": [],\"customAttributes\": {\"name\": \"full_name\",\"required\": \"true\"},\"dataKey\": null,\"element\": \"input\",\"mobileStyles\": {},\"rawStyles\": {},\"tabletStyles\": {}},{\"attributes\": {\"placeholder\": \"Email\"},\"baseStyles\": {\"borderColor\": \"#c9c9c9\",\"borderRadius\": \"4px\",\"borderStyle\": \"solid\",\"borderWidth\": \"1px\",\"display\": \"flex\",\"flexDirection\": \"column\",\"flexShrink\": 0,\"fontSize\": \"14px\",\"height\": \"32px\",\"overflow\": \"hidden\",\"position\": \"static\",\"width\": \"100%\"},\"blockId\": \"wzbvukqlo\",\"children\": [],\"classes\": [],\"customAttributes\": {\"name\": \"email_id\",\"required\": \"true\"},\"dataKey\": null,\"element\": \"input\",\"mobileStyles\": {},\"rawStyles\": {},\"tabletStyles\": {}},{\"attributes\": {\"placeholder\": \"Message\"},\"baseStyles\": {\"borderColor\": \"#c9c9c9\",\"borderRadius\": \"4px\",\"borderStyle\": \"solid\",\"borderWidth\": \"1px\",\"display\": \"flex\",\"flexDirection\": \"column\",\"flexShrink\": 0,\"fontSize\": \"14px\",\"height\": \"150px\",\"left\": \"auto\",\"overflow\": \"hidden\",\"position\": \"static\",\"top\": \"auto\",\"width\": \"100%\"},\"blockId\": \"4rclutff4\",\"children\": [],\"classes\": [],\"customAttributes\": {\"name\": \"message\",\"required\": \"true\"},\"dataKey\": null,\"element\": \"textarea\",\"mobileStyles\": {},\"rawStyles\": {},\"tabletStyles\": {}},{\"attributes\": {},\"baseStyles\": {\"alignItems\": \"center\",\"background\": \"#171717\",\"borderRadius\": \"4px\",\"display\": \"flex\",\"flexDirection\": \"column\",\"height\": \"fit-content\",\"justifyContent\": \"flex-start\",\"padding\": \"6px 8px\",\"width\": \"100%\"},\"blockId\": \"uxry21e1i\",\"children\": [{\"attributes\": {},\"baseStyles\": {\"color\": \"var(--neutral-white, #FFF)\",\"fontSize\": \"14px\",\"fontWeight\": \"420\",\"height\": \"fit-content\",\"left\": \"auto\",\"letterSpacing\": \"0.28px\",\"lineHeight\": \"115%\",\"minWidth\": \"30px\",\"position\": \"static\",\"top\": \"auto\",\"width\": \"fit-content\"},\"blockId\": \"yjglepmnh\",\"children\": [],\"classes\": [\"__text_block__\"],\"customAttributes\": {},\"dataKey\": null,\"element\": \"p\",\"innerHTML\": \"<p>Submit</p>\",\"mobileStyles\": {},\"rawStyles\": {},\"tabletStyles\": {}}],\"classes\": [\"\"],\"customAttributes\": {},\"dataKey\": null,\"element\": \"button\",\"mobileStyles\": {},\"rawStyles\": {\"flex-shrink\": \"0\",\"hover:background\": \"#383838\",\"transition\": \"all 0.1s ease-out\"},\"tabletStyles\": {}}],\"classes\": [],\"customAttributes\": {},\"dataKey\": null,\"element\": \"form\",\"mobileStyles\": {},\"rawStyles\": {},\"tabletStyles\": {}}",
"category": "Basic Forms",
"creation": "2024-07-11 23:34:22.558908",
"docstatus": 0,
"doctype": "Block Template",
"idx": 0,
"modified": "2024-08-05 10:09:12.451684",
"modified": "2024-09-19 13:10:03.603174",
"modified_by": "Administrator",
"name": "Form 1",
"order": 14,
"owner": "Administrator",
"preview": "/builder_assets/Form 1/form-1.png",
"preview_height": 1,
"preview_width": 1,
"template_name": "Form 1"
}
Loading

0 comments on commit 83e05ae

Please sign in to comment.