[Question] Load Custom Plugin For Every Web Form

Hello everyone,

I have created a plugin called Frappe Better Attach Control which extends several attachment related javascript libraries but it doesn’t get loaded for Web Forms

Useing the hooks webform_include_js and webform_include_css is meant to inject files to specific standard web forms so I can’t use it to make the plugin work in all web forms…

I have also tried injecting the plugin files directly to the context using the hook update_website_context but it seems that the hook gets ececuted before the web form code so the hook can’t determine if the current context is for a web page or a web form…

Even if I don’t check the context for what does it belong to and directly load the files into the context, the code for standard web forms override the scripts and styles instead of appending if previous value exists so all scripts and styles I load will be removed from the context

Finally, my question is does anyone of you know how I can load my plugin for web forms?

I have checked everywhere but I couldn’t find a solution. You guys are my last resort, so any help will be appreciated…

Thanks in advance…

Best regards…

I might be wrong about the context. I will give it another try and if I succeed then I will post the code for others to benefit…

But for the mean time, I would like to know if anyone has a solution…

Best regards…

I have managed to make it work. It’s funny how talking to yourself can be helpful sometimes :sweat_smile:

So let me share the what must be done with you…

If you want to manually inject JavaScript and/or CSS files to Web Form, you need to do the following…

  1. For JavaScript, the code must not include import and export but if it does, then the file must be compiled using esbuild. It’s better to use a separate compiled file for Web Form and the original is kept for Desk

  2. In the hooks.py file, add a custom hook key to store the list of paths for the JavaScript and/or CSS files

Example:

my_webform_css = ["/assets/my_app/css/my_app_webform.css"]
my_webform_js = ["/assets/my_app/js/my_app_webform.js"]
  1. Create a py file to update the website context from within and then use the following code or modify it
import os

import frappe
from frappe import _

def update_website_context(context):
    if context.get("doc") and context.doc.doctype == "Web Form" and context.doc.name:
        app_name = "my_app"
        
        # Inject JavaScript files
        try:
            js_files = frappe.get_hooks("my_webform_js", default=None, app_name=app_name)
            if js_files:
                if not isinstance(js_files, list):
                    js_files = [js_files]
                
                script = context.get("script", "")
                for js in js_files:
                    path = frappe.get_app_path(app_name, *js.strip("/").split("/"))
                    if os.path.exists(path):
                        custom_js = frappe.render_template(open(path).read(), context)
                        script = "\n\n".join([script, custom_js])
                    else:
                        frappe.throw(_("Unable to inject the js file \"{0}\" to context.").format(js))
                    
                context.script = script
        except Exception:
            frappe.throw(_("Unable to inject the js files to context."))
        
        # Inject CSS files
        try:
            css_files = frappe.get_hooks("my_webform_css", default=None, app_name=app_name)
            if css_files:
                if not isinstance(css_files, list):
                    css_files = [css_files]
                
                style = context.get("style", "")
                for css in css_files:
                    path = frappe.get_app_path(app_name, *css.strip("/").split("/"))
                    if os.path.exists(path):
                        custom_css = open(path).read()
                        style = "\n\n".join([style, custom_css])
                    else:
                        frappe.throw(_("Unable to inject the css file \"{0}\" to context.").format(css))
                    
                context.style = style
        
        except Exception:
            frappe.throw(_("Unable to inject the css files to context."))
  1. Finally in the hooks.py file, put the path of the file created in step 3 as the value for the hook update_website_context

Example:
my_website_context is the name of thw file created in step 3…

update_website_context = "my_app.my_website_context.update_website_context"
1 Like