feat(letterhead): introduce custom_css field to move styling out of html fields and to prevent scripts in html fields

This commit is contained in:
Shllokkk 2026-04-23 16:17:50 +05:30
parent 343d55a4a7
commit 0ab6840d1d
2 changed files with 45 additions and 28 deletions

View file

@ -7,29 +7,33 @@
"document_type": "Setup", "document_type": "Setup",
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"letter_head_for",
"letter_head_name", "letter_head_name",
"module", "module",
"source", "source",
"footer_source", "footer_source",
"column_break_3", "column_break_3",
"letter_head_for",
"standard", "standard",
"disabled", "disabled",
"is_default", "is_default",
"letter_head_image_section", "letter_head_image_section",
"align",
"image", "image",
"column_break_dpzk",
"image_height", "image_height",
"image_width", "image_width",
"align", "footer_image_section",
"footer_align",
"footer_image",
"column_break_kfvf",
"footer_image_height",
"footer_image_width",
"header_section", "header_section",
"content", "content",
"footer_section", "footer_section",
"footer", "footer",
"footer_image_section", "style_section",
"footer_image", "custom_css",
"footer_image_height",
"footer_image_width",
"footer_align",
"scripts_section", "scripts_section",
"header_script", "header_script",
"footer_script", "footer_script",
@ -47,7 +51,6 @@
"unique": 1 "unique": 1
}, },
{ {
"depends_on": "letter_head_name",
"fieldname": "source", "fieldname": "source",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Letter Head Based On", "label": "Letter Head Based On",
@ -59,7 +62,6 @@
}, },
{ {
"default": "0", "default": "0",
"depends_on": "letter_head_name",
"fieldname": "disabled", "fieldname": "disabled",
"fieldtype": "Check", "fieldtype": "Check",
"in_list_view": 1, "in_list_view": 1,
@ -69,7 +71,6 @@
}, },
{ {
"default": "0", "default": "0",
"depends_on": "letter_head_name",
"fieldname": "is_default", "fieldname": "is_default",
"fieldtype": "Check", "fieldtype": "Check",
"in_list_view": 1, "in_list_view": 1,
@ -79,25 +80,25 @@
"search_index": 1 "search_index": 1
}, },
{ {
"depends_on": "eval:doc.letter_head_name && doc.source === 'Image'", "depends_on": "eval:doc.source === 'Image'",
"fieldname": "letter_head_image_section", "fieldname": "letter_head_image_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Letter Head Image" "label": "Letter Head Image"
}, },
{ {
"depends_on": "eval:doc.letter_head_name && doc.source === 'Image'", "depends_on": "eval: doc.source === 'Image'",
"fieldname": "image", "fieldname": "image",
"fieldtype": "Attach Image", "fieldtype": "Attach Image",
"label": "Image" "label": "Image"
}, },
{ {
"depends_on": "eval:doc.source==='HTML' && doc.letter_head_name", "depends_on": "eval:doc.source==='HTML'",
"fieldname": "header_section", "fieldname": "header_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Header" "label": "Header"
}, },
{ {
"depends_on": "eval:!doc.__islocal && doc.source==='HTML'", "depends_on": "eval: doc.source==='HTML'",
"description": "Letter Head in HTML", "description": "Letter Head in HTML",
"fieldname": "content", "fieldname": "content",
"fieldtype": "HTML Editor", "fieldtype": "HTML Editor",
@ -107,13 +108,13 @@
"oldfieldtype": "Text Editor" "oldfieldtype": "Text Editor"
}, },
{ {
"depends_on": "eval:doc.footer_source==='HTML' && doc.letter_head_name", "depends_on": "eval:doc.footer_source==='HTML'",
"fieldname": "footer_section", "fieldname": "footer_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Footer" "label": "Footer"
}, },
{ {
"depends_on": "eval:!doc.__islocal", "depends_on": "eval: doc.footer_source==='HTML'",
"description": "Footer will display correctly only in PDF", "description": "Footer will display correctly only in PDF",
"fieldname": "footer", "fieldname": "footer",
"fieldtype": "HTML Editor", "fieldtype": "HTML Editor",
@ -138,7 +139,7 @@
"label": "Image Width (px)" "label": "Image Width (px)"
}, },
{ {
"depends_on": "eval:doc.footer_source==='Image' && doc.letter_head_name", "depends_on": "eval:doc.footer_source==='Image'",
"fieldname": "footer_image_section", "fieldname": "footer_image_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Footer Image" "label": "Footer Image"
@ -165,22 +166,20 @@
"options": "Left\nRight\nCenter" "options": "Left\nRight\nCenter"
}, },
{ {
"default": "HTML",
"depends_on": "letter_head_name",
"fieldname": "footer_source", "fieldname": "footer_source",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Footer Based On", "label": "Footer Based On",
"options": "Image\nHTML" "options": "Image\nHTML"
}, },
{ {
"depends_on": "eval:!doc.__islocal && doc.source==='HTML'", "depends_on": "eval: doc.source==='HTML'",
"fieldname": "header_script", "fieldname": "header_script",
"fieldtype": "Code", "fieldtype": "Code",
"label": "Header Script", "label": "Header Script",
"options": "Javascript" "options": "Javascript"
}, },
{ {
"depends_on": "eval:!doc.__islocal && doc.footer_source==='HTML'", "depends_on": "eval: doc.footer_source==='HTML'",
"fieldname": "footer_script", "fieldname": "footer_script",
"fieldtype": "Code", "fieldtype": "Code",
"label": "Footer Script", "label": "Footer Script",
@ -189,7 +188,7 @@
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "eval: doc.header_script || doc.footer_script", "collapsible_depends_on": "eval: doc.header_script || doc.footer_script",
"depends_on": "eval: !doc.__islocal", "depends_on": "eval: doc.source === 'HTML' || doc.footer_source === 'HTML'",
"fieldname": "scripts_section", "fieldname": "scripts_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Scripts" "label": "Scripts"
@ -223,6 +222,28 @@
"label": "Module", "label": "Module",
"mandatory_depends_on": "eval: doc.standard == \"Yes\"", "mandatory_depends_on": "eval: doc.standard == \"Yes\"",
"options": "Module Def" "options": "Module Def"
},
{
"fieldname": "column_break_dpzk",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_kfvf",
"fieldtype": "Column Break"
},
{
"depends_on": "eval: doc.source === 'HTML' || doc.footer_source === 'HTML'",
"fieldname": "custom_css",
"fieldtype": "Code",
"label": "Custom CSS",
"options": "CSS"
},
{
"collapsible": 1,
"depends_on": "eval: doc.source === 'HTML' || doc.footer_source === 'HTML'",
"fieldname": "style_section",
"fieldtype": "Section Break",
"label": "Style"
} }
], ],
"icon": "fa fa-font", "icon": "fa fa-font",
@ -230,7 +251,7 @@
"links": [], "links": [],
"make_attachments_public": 1, "make_attachments_public": 1,
"max_attachments": 3, "max_attachments": 3,
"modified": "2026-04-08 13:15:24.935222", "modified": "2026-04-22 20:25:24.438817",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Printing", "module": "Printing",
"name": "Letter Head", "name": "Letter Head",

View file

@ -19,6 +19,7 @@ class LetterHead(Document):
align: DF.Literal["Left", "Right", "Center"] align: DF.Literal["Left", "Right", "Center"]
content: DF.HTMLEditor | None content: DF.HTMLEditor | None
custom_css: DF.Code | None
disabled: DF.Check disabled: DF.Check
footer: DF.HTMLEditor | None footer: DF.HTMLEditor | None
footer_align: DF.Literal["Left", "Right", "Center"] footer_align: DF.Literal["Left", "Right", "Center"]
@ -39,11 +40,6 @@ class LetterHead(Document):
standard: DF.Literal["No", "Yes"] standard: DF.Literal["No", "Yes"]
# end: auto-generated types # end: auto-generated types
def before_insert(self):
# for better UX, let user set from attachment
if not frappe.flags.in_migrate and not frappe.flags.in_install:
self.source = "Image"
def on_trash(self): def on_trash(self):
from frappe.defaults import clear_default from frappe.defaults import clear_default