<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Explorer</title>
    <description>The latest articles on Forem by Explorer (@exploringmylifeworks).</description>
    <link>https://forem.com/exploringmylifeworks</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3593062%2F8342d187-9126-488c-8cf5-aa6e3da66453.png</url>
      <title>Forem: Explorer</title>
      <link>https://forem.com/exploringmylifeworks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/exploringmylifeworks"/>
    <language>en</language>
    <item>
      <title>🎨 Dynamic Dashboard Colors: Mapping Pie Chart Slices to Workflow Status in Joget</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Sun, 16 Nov 2025 11:24:01 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/dynamic-dashboard-colors-mapping-pie-chart-slices-to-workflow-status-2ihg</link>
      <guid>https://forem.com/exploringmylifeworks/dynamic-dashboard-colors-mapping-pie-chart-slices-to-workflow-status-2ihg</guid>
      <description>&lt;h2&gt;
  
  
  1. Overview
&lt;/h2&gt;

&lt;p&gt;Dashboards are only effective when they communicate information clearly. When using Joget's Report Builder (which uses ECharts) to create a &lt;strong&gt;Status Pie Chart&lt;/strong&gt;, the default colors are meaningless. We need &lt;strong&gt;Green&lt;/strong&gt; for "Authorized," &lt;strong&gt;Red&lt;/strong&gt; for "Rejected," and &lt;strong&gt;Yellow&lt;/strong&gt; for "Pending."&lt;/p&gt;

&lt;p&gt;This client-side JavaScript solution, placed in a &lt;strong&gt;Custom HTML&lt;/strong&gt; element on your Userview page, allows you to hijack the chart after it loads and programmatically apply meaningful colors based on the data label (the workflow status).&lt;/p&gt;




&lt;h2&gt;
  
  
  2. How It Works
&lt;/h2&gt;

&lt;p&gt;This script performs two main actions: &lt;strong&gt;Color Customization&lt;/strong&gt; and &lt;strong&gt;Interactive Filtering&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Action 1: Applying Status Colors 🌈
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Chart Initialization:&lt;/strong&gt; The script waits for the page to load, then targets the specific ECharts instance using its unique HTML ID (e.g., &lt;code&gt;echarts_XYZ&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Data Grab:&lt;/strong&gt; It pulls the raw data (&lt;code&gt;series[0].data&lt;/code&gt;) from the chart.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Color Loop:&lt;/strong&gt; It loops through every slice. For each slice, it checks the slice's name (the status, like "Authorized") and manually injects a custom &lt;code&gt;itemStyle&lt;/code&gt; property with a specific Hex color.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Redraw:&lt;/strong&gt; Finally, it uses &lt;code&gt;aChart.setOption(option)&lt;/code&gt; to force ECharts to redraw the chart with the new color mappings.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Action 2: Interactive Filtering 🖱️
&lt;/h3&gt;

&lt;p&gt;The second part of the script uses the ECharts built-in &lt;code&gt;on('click', ...)&lt;/code&gt; event listener to turn each pie slice into a functional link, routing users to specific, pre-filtered Userview dashboards.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Full Code
&lt;/h2&gt;

&lt;p&gt;Paste this code into a &lt;strong&gt;Custom HTML&lt;/strong&gt; element (placed after the Report Grid element) in your Joget Userview.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Note:&lt;/strong&gt; The unique ECharts ID (&lt;code&gt;echarts_CHART_ID_HERE&lt;/code&gt;) and the Userview paths have been made generic. &lt;strong&gt;You must replace these placeholders&lt;/strong&gt; with your actual values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// IMPORTANT: Replace 'echarts_CHART_ID_HERE' with the actual ID of your chart element.&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;aChart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;echarts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;echarts_CHART_ID_HERE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;option&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aChart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOption&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;pieData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aChart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getOption&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// --- DYNAMIC COLOR MAPPING LOGIC ---&lt;/span&gt;
        &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;pieData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pieData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Set color based on status (Use Hex codes for consistency)&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Approval Pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;itemStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#FFDD7D&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Yellow/Amber&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;itemStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#9AC4A1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Green&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Draft&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;itemStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#EEA94E&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Orange&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Escalated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;itemStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Red Flag&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rejected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Request Rejected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;itemStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#DC143C&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Crimson (Strong Red)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Default color for any unexpected or unmapped values&lt;/span&gt;
                &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;series&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;itemStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gray&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Apply the new colors to the chart&lt;/span&gt;
        &lt;span class="nx"&gt;aChart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setOption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// --- INTERACTIVE CLICK LOGIC ---&lt;/span&gt;
        &lt;span class="nx"&gt;aChart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

            &lt;span class="c1"&gt;// IMPORTANT: Replace the generic Userview links with your actual paths.&lt;/span&gt;

            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Approval Pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/jw/web/userview/app_name/v/_/pending_dashboard`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/jw/web/userview/app_name/v/_/authorized_dashboard`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Rejected&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/jw/web/userview/app_name/v/_/rejected_dashboard`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Escalated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/jw/web/userview/app_name/v/_/escalated_dashboard`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// Generic fall-through: For all other statuses, open the main list filtered by the clicked status name&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// The hash variable used here (d-6985709-fn_Status) is a Report Grid filter hash.&lt;/span&gt;
                &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/jw/web/userview/app_name/v/_/all_document_list?d-GRID_HASH-fn_Status=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Example Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Compliance Dashboards:&lt;/strong&gt; Instantly highlight records that are "Expired" (Red) or "Compliant" (Green).&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Service Desk:&lt;/strong&gt; Show tickets based on priority: "High" (Red), "Medium" (Orange), "Low" (Blue).&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Financial Approvals:&lt;/strong&gt; Use strong, clear colors to represent the stage of the approval process: "Submitted" (Yellow), "Finance Approved" (Light Green), "CEO Approved" (Dark Green).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Customization Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;💡 &lt;strong&gt;Using Lookup Objects:&lt;/strong&gt; For cleaner code, consider replacing the long &lt;code&gt;if/else if&lt;/code&gt; chain with a simple JavaScript lookup object (or map). This is easier to update when new statuses are added.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Userview Pathing:&lt;/strong&gt; The click logic uses &lt;code&gt;window.open(...)&lt;/code&gt; to direct users. Ensure you replace &lt;code&gt;app_name&lt;/code&gt; and the specific Userview categories (&lt;code&gt;_&lt;/code&gt;) with your application's actual URLs.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;ECharts ID Safety:&lt;/strong&gt; If you modify and save your Report Grid element, Joget sometimes changes the unique ECharts ID. If your colors disappear, this is the first place to check!&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Key Benefits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🚀 &lt;strong&gt;Enhanced Clarity:&lt;/strong&gt; Makes dashboards instantly readable by associating colors with conventional meanings (red=bad, green=good).&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Improved UX:&lt;/strong&gt; Turns a static visualization into an interactive filter, drastically reducing the clicks needed for users to drill down into data.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Maintainability:&lt;/strong&gt; All coloring rules are contained in one simple, client-side script, separate from the core reporting configuration.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. Security Note
&lt;/h2&gt;

&lt;p&gt;🔒 &lt;strong&gt;Userview Security:&lt;/strong&gt; The click logic exposes Userview URLs. Ensure that the target dashboard pages (&lt;code&gt;/pending_dashboard&lt;/code&gt;, &lt;code&gt;/rejected_dashboard&lt;/code&gt;, etc.) are protected with appropriate Userview &lt;strong&gt;Permission&lt;/strong&gt; settings so unauthorized users cannot access the underlying data lists directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Final Thoughts
&lt;/h2&gt;

&lt;p&gt;By using client-side scripting to customize the ECharts object, you transform a generic Joget dashboard into a highly intuitive and interactive data visualization tool. This simple technique is fundamental for professional-grade reporting in Joget.&lt;/p&gt;

</description>
      <category>joget</category>
      <category>charts</category>
      <category>dashboard</category>
      <category>javascript</category>
    </item>
    <item>
      <title>💾 Custom Approval History in Joget: Direct SQL Insertion via BeanShell Tool</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Sun, 16 Nov 2025 11:19:31 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/custom-approval-history-in-joget-direct-sql-insertion-via-beanshell-tool-1kcp</link>
      <guid>https://forem.com/exploringmylifeworks/custom-approval-history-in-joget-direct-sql-insertion-via-beanshell-tool-1kcp</guid>
      <description>&lt;h2&gt;
  
  
  1. Overview
&lt;/h2&gt;

&lt;p&gt;While Joget's Form Binders and built-in audit trails handle standard data storage, complex enterprise applications often require specialized auditing and history tracking. This is particularly true when you need a dedicated, sequentially numbered &lt;strong&gt;Approval History&lt;/strong&gt; table that links to multiple main processes for easy reporting.&lt;/p&gt;

&lt;p&gt;This post details an advanced, expert technique using a &lt;strong&gt;Java/BeanShell Process Tool&lt;/strong&gt; to perform a direct SQL &lt;code&gt;INSERT&lt;/code&gt; into a custom database table (&lt;code&gt;app_fd_approval_history&lt;/code&gt;). This method grants granular control over the primary key (&lt;code&gt;id&lt;/code&gt;) sequence and allows you to log specific metadata (user, status, date) mid-workflow, decoupled from the main form's binder.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. How It Works
&lt;/h2&gt;

&lt;p&gt;The script operates as a robust, transactional database logger configured as a workflow tool.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;⚙️ Database Connection:&lt;/strong&gt; It securely retrieves the Joget application's main database connection via the &lt;strong&gt;&lt;code&gt;setupDataSource&lt;/code&gt;&lt;/strong&gt; Spring bean using &lt;code&gt;AppUtil.getApplicationContext().getBean("setupDataSource")&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚙️ Manual ID Generation:&lt;/strong&gt; It executes &lt;code&gt;SELECT MAX(CAST(id AS SIGNED))&lt;/code&gt; to find the highest existing sequential ID. Crucially, it &lt;strong&gt;casts the &lt;code&gt;id&lt;/code&gt;&lt;/strong&gt; (often stored as a string in Joget forms) to a signed integer to ensure correct numerical ordering, and then calculates the &lt;code&gt;nextId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚙️ Dynamic Data Capture:&lt;/strong&gt; It utilizes Joget &lt;strong&gt;Hash Variables&lt;/strong&gt; (e.g., &lt;code&gt;#form.new_request.createdByName#&lt;/code&gt;) to dynamically capture real-time form data, user information, and timestamps directly from the workflow instance before preparing the data for insertion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚙️ Execution &amp;amp; Cleanup:&lt;/strong&gt; It uses a &lt;code&gt;PreparedStatement&lt;/code&gt; to safely execute the &lt;code&gt;INSERT&lt;/code&gt; query. The &lt;strong&gt;&lt;code&gt;finally&lt;/code&gt;&lt;/strong&gt; block is critical, ensuring the database connection is closed (&lt;code&gt;conn.close()&lt;/code&gt;) even if an exception occurs, preventing connection leaks.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Full Code
&lt;/h2&gt;

&lt;p&gt;This code is configured as a &lt;strong&gt;BeanShell&lt;/strong&gt; within a Joget workflow, typically placed immediately after a submission or approval step where the status transition occurs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.joget.apps.app.service.AppUtil;
import org.joget.commons.util.LogUtil;

public void insertNewData() {
    Connection conn = null;
    try {
        LogUtil.info("ApprovalHistoryTool", "Starting Approval History Creation Tool");

        // 1. Get the primary application data source
        DataSource ds = (DataSource) AppUtil.getApplicationContext().getBean("setupDataSource");
        conn = ds.getConnection();

        // 2. Determine the next sequential ID manually
        String maxIdQuery = "SELECT MAX(CAST(id AS SIGNED)) FROM app_fd_approval_history";
        PreparedStatement maxIdStmt = conn.prepareStatement(maxIdQuery);
        ResultSet resultSet = maxIdStmt.executeQuery();

        int nextId = 1; 
        if (resultSet.next()) {
            nextId = resultSet.getInt(1) + 1;
            LogUtil.info("ApprovalHistoryTool", "Next ID to be inserted: " + nextId);
        }
        maxIdStmt.close();

        // 3. Prepare the INSERT statement
        String insertQuery = "INSERT INTO app_fd_approval_history" +
                "(id, c_name, c_status, c_date, c_remark, c_request_for, c_parent_id)" +
                "VALUES (?, ?, ?, ?, ?, ?, ?)";

        // 4. Capture dynamic data using Joget Hash Variables
        // IMPORTANT: Replace the placeholder values (#form.new_request...) with your actual form IDs.
        String name = "";
        String status = "";
        String date = "";

        // Conditional logic based on a form field value (Request Type)
        if ("New".equals("#form.new_request.request_type#")) {
            name = "#form.new_request.createdByName#";
            status = "Request Submitted";
            date = "#form.new_request.dateCreated#";
        } else if ("Update".equals("#form.new_request.request_type#")) {
            name = "#form.new_request.modifiedByName#";
            status = "Update Request";
            date = "#form.new_request.dateModified#";
        }

        String remark = "#form.new_request.reason#";
        String requestFor = ""; 
        String foreignKey = "#form.new_request.id#"; // Links to the main form record

        PreparedStatement insertStmt = conn.prepareStatement(insertQuery);

        // Set values using the PreparedStatement (prevents SQL injection)
        insertStmt.setInt(1, nextId);
        insertStmt.setString(2, name);
        insertStmt.setString(3, status);
        insertStmt.setString(4, date);
        insertStmt.setString(5, remark);
        insertStmt.setString(6, requestFor);
        insertStmt.setString(7, foreignKey);
        LogUtil.info("ApprovalHistoryTool", "Linking history to parent ID: " + foreignKey);

        // 5. Execute and close
        insertStmt.executeUpdate();
        insertStmt.close();

        LogUtil.info("ApprovalHistoryTool", "Data inserted successfully in Approval History Table.");
    } catch (Exception e) {
        LogUtil.error("ApprovalHistoryTool", "Error inserting data: " + e.getMessage(), e);
    } finally {
        // 6. Ensure connection is always closed
        try {
            if (conn != null &amp;amp;&amp;amp; !conn.isClosed()) {
                conn.close();
            }
        } catch (SQLException e) {
            LogUtil.error("ApprovalHistoryTool", "Error closing connection: " + e.getMessage(), e);
        }
    }
}

insertNewData();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Example Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Complete Audit Trail:&lt;/strong&gt; Creating a persistent, uneditable log of every approval, rejection, and modification action for compliance or security auditing.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Custom Reporting:&lt;/strong&gt; Facilitating streamlined reporting on approval cycle times, user actions, and status changes using a normalized, dedicated history table structure.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Sequential Primary Keys:&lt;/strong&gt; Implementing simple, strictly sequential, numeric &lt;code&gt;id&lt;/code&gt;s for history records, which are far easier for end-users and reports to reference than default UUIDs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Customization Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;💡 &lt;strong&gt;Hash Variable Validation:&lt;/strong&gt; Always verify that your Joget Hash Variables are correctly returning the required values (e.g., using &lt;code&gt;LogUtil.info()&lt;/code&gt;) before trusting them in a database insertion.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Error Logging:&lt;/strong&gt; Expand the &lt;code&gt;catch&lt;/code&gt; block to include logic that notifies an administrator (e.g., sends an email) if the database insertion fails, rather than just logging the error.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Time Zone Control:&lt;/strong&gt; Dates retrieved via Hash Variables may require explicit formatting. Use Java's &lt;code&gt;SimpleDateFormat&lt;/code&gt; inside the script to format the date string before insertion, ensuring consistency across different geographic locations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Key Benefits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🚀 &lt;strong&gt;Granular Control:&lt;/strong&gt; Provides developers with complete control over SQL execution, allowing for custom data structures and transactional integrity.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Decoupled Logging:&lt;/strong&gt; Separates the audit trail from the main form data, making both the main table and the history table cleaner and more performant.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Security &amp;amp; Consistency:&lt;/strong&gt; Use of &lt;strong&gt;&lt;code&gt;PreparedStatement&lt;/code&gt;&lt;/strong&gt; protects against basic SQL injection and guarantees the field values are treated as data, not code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Security Note
&lt;/h2&gt;

&lt;p&gt;🔒 &lt;strong&gt;SQL Injection Prevention:&lt;/strong&gt; The use of &lt;strong&gt;&lt;code&gt;PreparedStatement&lt;/code&gt;&lt;/strong&gt; with placeholder &lt;code&gt;?&lt;/code&gt; marks is the most critical security feature in this script. It prevents external data (even malicious data from a form field) from altering the structure of the SQL command.&lt;br&gt;
🔒 &lt;strong&gt;Resource Leaks:&lt;/strong&gt; The meticulous implementation of the &lt;strong&gt;&lt;code&gt;finally&lt;/code&gt;&lt;/strong&gt; block to close the &lt;code&gt;Connection&lt;/code&gt; is non-negotiable. Failing to close connections will lead to resource exhaustion and potential database failure under load.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Mastering direct database interaction via BeanShell Tools gives you expert-level control over your Joget application's data layer. This technique is indispensable for building robust, compliance-ready solutions that require custom, sequential auditing and complex data relationships.&lt;/p&gt;

</description>
      <category>joget</category>
      <category>beanshell</category>
      <category>workflow</category>
      <category>automation</category>
    </item>
    <item>
      <title>🚨 Resuming Stuck Joget Workflow Tools with API</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Sun, 16 Nov 2025 10:45:20 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/resuming-stuck-joget-workflow-tools-with-api-kg1</link>
      <guid>https://forem.com/exploringmylifeworks/resuming-stuck-joget-workflow-tools-with-api-kg1</guid>
      <description>&lt;h2&gt;
  
  
  1. Overview
&lt;/h2&gt;

&lt;p&gt;A critical issue in production environments is a workflow process halting when the Joget server unexpectedly goes down. While user activities are often easy to "Complete" manually via the Process Monitor, processes stuck specifically on a &lt;strong&gt;Tool Plugin&lt;/strong&gt; (like an Email Tool or integration script) often resist normal completion attempts. The "Complete" button in the Process Monitor UI has no effect because a Tool is a server-side action, not a user-driven workflow task.&lt;/p&gt;

&lt;p&gt;This post reveals the expert method, recommended by the Joget support team, to reliably &lt;strong&gt;force-resume or restart&lt;/strong&gt; a stuck tool using the built-in &lt;strong&gt;JSON Monitoring API&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. How It Works
&lt;/h2&gt;

&lt;p&gt;When a tool element is stuck, the workflow engine typically believes the tool is still running or failed to complete its transaction. The solution is to issue a direct command to the workflow engine using the API endpoint specifically designed to manage activity states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Endpoint:&lt;/strong&gt; The &lt;code&gt;/monitoring/activity/start&lt;/code&gt; endpoint is used to push the activity back into the "ready" or "running" state, effectively forcing a re-execution of the tool's logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication:&lt;/strong&gt; The API requires administrative credentials (Basic Authentication or URL parameters) to ensure only authorized users can manipulate the workflow state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parameters:&lt;/strong&gt; You must provide the specific &lt;strong&gt;Process Instance ID&lt;/strong&gt; and the &lt;strong&gt;Activity Definition ID&lt;/strong&gt; (the unique ID of the tool element in the workflow designer) to target the exact stuck step.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Full API Command
&lt;/h2&gt;

&lt;p&gt;The API call uses the &lt;code&gt;POST&lt;/code&gt; method and the &lt;code&gt;/monitoring/activity/start/&lt;/code&gt; endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /jw/web/json/monitoring/activity/start/(*:processId)/(*:activityDefId)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example Request (Using URL Authentication)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:8080/jw/web/json/monitoring/activity/start/24002_sampleApp_requestForm_approver_process/email_on_received?j_username=admin&amp;amp;j_password=admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Must be used to change state.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;processId&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The specific instance ID of the stalled workflow.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;24002_sampleApp_requestForm_approver_process&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;activityDefId&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The unique ID of the stuck Tool Plugin.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;email_on_received&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;j_username&lt;/code&gt;/&lt;code&gt;j_password&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Admin credentials for authorization.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;admin&lt;/code&gt;/&lt;code&gt;admin&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. Example Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Integration Failure Recovery:&lt;/strong&gt; A process is stuck on an &lt;strong&gt;API Tool Plugin&lt;/strong&gt; because the external service was temporarily unavailable during a server interruption. Restarting the tool allows the integration to run again.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Notification Retries:&lt;/strong&gt; An &lt;strong&gt;Email Tool&lt;/strong&gt; failed to send due to a mail server timeout. The API call forces the email tool to re-execute and notify users correctly.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Data Sync Issues:&lt;/strong&gt; A &lt;strong&gt;Script Tool&lt;/strong&gt; responsible for data synchronization failed mid-execution and left the process in an ambiguous state. The API forces the engine to resolve the tool's status and advance the flow.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Customization Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;💡 &lt;strong&gt;Postman Environment:&lt;/strong&gt; Create an environment variable in Postman for your base Joget URL (e.g., &lt;code&gt;http://localhost:8080/jw&lt;/code&gt;) and your admin credentials to quickly execute these recovery calls without re-typing.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Basic Auth:&lt;/strong&gt; For better security, use the &lt;strong&gt;Basic Auth&lt;/strong&gt; tab in Postman or similar tools instead of passing credentials in the URL parameters, especially in non-local environments.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Logging:&lt;/strong&gt; Before using this method, always consult the Joget server logs (&lt;code&gt;wflow.log&lt;/code&gt;, &lt;code&gt;catalina.out&lt;/code&gt;, etc.) to understand &lt;em&gt;why&lt;/em&gt; the tool failed initially. Addressing the root cause is crucial to prevent recurrence.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Key Benefits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🚀 &lt;strong&gt;Immediate Process Continuity:&lt;/strong&gt; Quickly recovers stalled business processes without requiring manual data intervention or lengthy server restarts.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;High Data Integrity:&lt;/strong&gt; By utilizing the official Monitoring API, you maintain the integrity of the workflow state within the Joget engine.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Targeted Fix:&lt;/strong&gt; Allows administrators to precisely fix specific process instances without affecting other running processes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Security Note
&lt;/h2&gt;

&lt;p&gt;🔒 &lt;strong&gt;API Exposure:&lt;/strong&gt; This API grants the power to manipulate live workflow states. &lt;strong&gt;Never&lt;/strong&gt; expose this endpoint publicly. Ensure that any system making calls to this API uses strong administrative credentials and operates exclusively within a secure network environment.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Understanding how to directly interface with the Joget workflow engine via the Monitoring JSON API is a critical skill for any advanced Joget administrator. It provides the necessary surgical precision to handle edge cases like stuck Tool Plugins, ensuring your production workflows remain resilient and functional even after unexpected interruptions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyy0sl7nzcstifqzm56k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyy0sl7nzcstifqzm56k.png" alt=" " width="800" height="199"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>joget</category>
      <category>lowcode</category>
    </item>
    <item>
      <title>🛠️ Dynamic Joget Form Generation Using BeanShell Scripting</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Sun, 16 Nov 2025 10:02:36 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/dynamic-joget-form-generation-using-beanshell-scripting-aln</link>
      <guid>https://forem.com/exploringmylifeworks/dynamic-joget-form-generation-using-beanshell-scripting-aln</guid>
      <description>&lt;h2&gt;
  
  
  1. Overview
&lt;/h2&gt;

&lt;p&gt;While Joget's Form Builder is excellent for visual design, there are advanced scenarios where you need to generate form definitions programmatically. This is typically done when a form's structure is &lt;strong&gt;dynamic&lt;/strong&gt; or based on an external metadata source (e.g., a database table, a configuration list, or another Joget list).&lt;/p&gt;

&lt;p&gt;This blog post analyzes a powerful &lt;strong&gt;BeanShell/Groovy script&lt;/strong&gt; designed to run within Joget's server-side tools. It reads form field definitions from a &lt;code&gt;FormRowSet&lt;/code&gt; (or similar data source) and constructs a complete Joget Form Definition JSON, finally saving it to the App Definition.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. How It Works
&lt;/h2&gt;

&lt;p&gt;The script operates by constructing the complete JSON object that defines a Joget Form, adhering strictly to the internal schema used by the platform.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;⚙️ Data Source:&lt;/strong&gt; The script assumes field metadata (ID, Label, Class Name) is available in the &lt;code&gt;rows&lt;/code&gt; variable (a &lt;code&gt;FormRowSet&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚙️ Field Construction:&lt;/strong&gt; It iterates over the &lt;code&gt;rows&lt;/code&gt;. For each entry, it creates a string representation of a JSON object for a single form element, ensuring the &lt;code&gt;className&lt;/code&gt; (field type) and &lt;code&gt;properties&lt;/code&gt; (ID and Label) are correctly inserted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚙️ JSON Aggregation:&lt;/strong&gt; All individual field JSONs are placed into a &lt;code&gt;JSONArray&lt;/code&gt; (&lt;code&gt;fields&lt;/code&gt;). This array is then inserted as the content of the Form's main &lt;code&gt;Column&lt;/code&gt; element.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚙️ Form Saving:&lt;/strong&gt; Finally, the fully assembled JSON is wrapped in a &lt;code&gt;FormDefinition&lt;/code&gt; object. The script uses the &lt;code&gt;AppService&lt;/code&gt; bean to call &lt;code&gt;createFormDefinition&lt;/code&gt;, making the new form instantly available in the App Designer.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Full Code
&lt;/h2&gt;

&lt;p&gt;This code is typically executed within a &lt;strong&gt;Process Tool&lt;/strong&gt; using the BeanShell/Groovy language or in a &lt;strong&gt;Form/Datalist Binder&lt;/strong&gt; script where the &lt;code&gt;rows&lt;/code&gt; variable is populated.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Collection&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.joget.apps.app.model.AppDefinition&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.joget.apps.app.model.FormDefinition&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.joget.apps.app.service.AppService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.joget.apps.app.service.AppUtil&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.joget.apps.form.model.FormRow&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.joget.apps.form.model.FormRowSet&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.joget.commons.util.LogUtil&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.json.JSONArray&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.json.JSONObject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Get form metadata from request or script tool properties&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;formDefId&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRequestParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"form_def_id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;formName&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRequestParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"form_name"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;formTableName&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;formData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRequestParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"form_table_name"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Example: Assume 'rows' is a FormRowSet containing field configurations&lt;/span&gt;
&lt;span class="c1"&gt;// FormRowSet rows = ... &lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;JSONArray&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JSONArray&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Loop through the source data (e.g., field definitions)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;FormRow&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;fieldId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"field_id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;fieldLabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"field_label"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// Must be a valid Joget Form Element class name (e.g., org.joget.apps.form.lib.TextField)&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"field_type"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;JSONObject&lt;/span&gt; &lt;span class="n"&gt;formElement&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

        &lt;span class="c1"&gt;// Build the JSON structure for a single form element&lt;/span&gt;
        &lt;span class="n"&gt;formElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JSONObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"    \"className\" : \""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;className&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"    \"properties\": {\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        \"controlField\": \"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        \"id\": \""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fieldId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        \"label\": \""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fieldLabel&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        \"permissionHidden\": \"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        \"readonly\": \"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        \"readonlyLabel\": \"\",\n  "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        \"size\": \"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        \"validator\": {\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"            \"className\": \"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"            \"properties\": {}\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        },\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        \"value\": \"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"        \"workflowVariable\": \"\"\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"    }\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                                        &lt;span class="s"&gt;"}"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formElement&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Embed all generated fields into the main Form structure&lt;/span&gt;
    &lt;span class="nc"&gt;JSONObject&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;JSONObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"    \"className\": \"org.joget.apps.form.model.Form\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"    \"elements\": [{\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"className\": \"org.joget.apps.form.model.Section\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"elements\": [{\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"className\": \"org.joget.apps.form.model.Column\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"elements\":"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"properties\": {\"width\": \"100%\"}\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        }],\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"properties\": {\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"id\": \"section1\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"label\": \"Dynamic Fields Section\"\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        }\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"    }],\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"    \"properties\": {\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"description\": \"Dynamically generated form.\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"id\": \""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;formDefId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"loadBinder\": {\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"className\": \"org.joget.apps.form.lib.WorkflowFormBinder\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"properties\": {}\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        },\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"name\": \""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;formName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"noPermissionMessage\": \"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"permission\": {\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"className\": \"\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"properties\": {}\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        },\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"postProcessor\": null,\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"postProcessorRunOn\": \"create\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"storeBinder\": {\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"className\": \"org.joget.apps.form.lib.WorkflowFormBinder\",\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"            \"properties\": {}\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        },\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"        \"tableName\": \""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;formTableName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\"\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"    }\n"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
    &lt;span class="s"&gt;"}"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Save the new Form Definition to the current App&lt;/span&gt;
    &lt;span class="nc"&gt;FormDefinition&lt;/span&gt; &lt;span class="n"&gt;formDefinition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormDefinition&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;formDefinition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setJson&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="n"&gt;formDefinition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formDefId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;formDefinition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;formDefinition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTableName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;formTableName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;AppService&lt;/span&gt; &lt;span class="n"&gt;appService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AppService&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;AppUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getApplicationContext&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getBean&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"appService"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;AppDefinition&lt;/span&gt; &lt;span class="n"&gt;appDef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AppUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCurrentAppDefinition&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;Collection&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;appService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createFormDefinition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appDef&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;formDefinition&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Optional: Log errors if form creation failed&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;LogUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;warn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FormCreator"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Errors encountered while creating form: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;LogUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"FormCreator"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Cannot Create Form"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Example Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Dynamic Survey Generation:&lt;/strong&gt; Create different forms instantly based on survey questions stored in a master configuration list.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Automated Integrations:&lt;/strong&gt; Automatically generate forms that mirror the structure of an external API or third-party database table, minimizing manual development work.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;User-Defined Forms:&lt;/strong&gt; Allow specific power-users to define the fields they need in a secondary Joget list, then use this script to generate the final functional form.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Customization Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;💡 &lt;strong&gt;Field Type Mapping:&lt;/strong&gt; Use a map structure in your BeanShell script to translate simple data types (e.g., &lt;code&gt;"TEXT"&lt;/code&gt;) from your source &lt;code&gt;rows&lt;/code&gt; into the necessary full Java Class Names (e.g., &lt;code&gt;"org.joget.apps.form.lib.TextField"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Adding Complex Properties:&lt;/strong&gt; For field types like Select Boxes, dynamically add the &lt;code&gt;options&lt;/code&gt; or &lt;code&gt;optionsBinder&lt;/code&gt; properties to the &lt;code&gt;formElement&lt;/code&gt; JSON to ensure the new field is fully functional upon creation.&lt;/li&gt;
&lt;li&gt;💡 &lt;strong&gt;Metadata Refresh:&lt;/strong&gt; If you use this script to &lt;em&gt;update&lt;/em&gt; a form, use &lt;code&gt;appService.updateFormDefinition&lt;/code&gt; instead of &lt;code&gt;createFormDefinition&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Key Benefits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🚀 &lt;strong&gt;Metadata-Driven Design:&lt;/strong&gt; Decouples form structure from application code, making forms easier to manage and update.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Consistency:&lt;/strong&gt; Ensures all generated forms follow a consistent structure (e.g., using the same default binders and field properties).&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Extensibility:&lt;/strong&gt; Enables the creation of complex applications where form fields are determined by business rules or configuration, not just static design.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Security Note
&lt;/h2&gt;

&lt;p&gt;🔒 &lt;strong&gt;Access Control:&lt;/strong&gt; Since this script can alter the core application definition, it must be carefully permissioned. It is best used in a &lt;strong&gt;workflow process tool&lt;/strong&gt; that runs under the secure system user account or is triggered only by application administrators. Avoid exposing this logic directly in front-end form validation or client-side scripts.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Leveraging BeanShell to dynamically manipulate Joget's JSON configurations is the pinnacle of advanced Joget development. It unlocks the ability to build flexible, configuration-driven solutions that are resilient to changes in business requirements.&lt;/p&gt;

</description>
      <category>joget</category>
      <category>lowcode</category>
      <category>automation</category>
      <category>beanshell</category>
    </item>
    <item>
      <title>Mastering BeanShell Hash Variables in Joget: The Most Basic &amp; Practical Guide</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Sun, 16 Nov 2025 07:49:01 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/mastering-beanshell-hash-variables-in-joget-the-most-basic-practical-guide-5fnc</link>
      <guid>https://forem.com/exploringmylifeworks/mastering-beanshell-hash-variables-in-joget-the-most-basic-practical-guide-5fnc</guid>
      <description>&lt;p&gt;1.Create an App Variable &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4r2m5g68erwpl07kl945.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4r2m5g68erwpl07kl945.png" alt=" " width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2.call it&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wqltu3yb2hqq32vapwc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7wqltu3yb2hqq32vapwc.png" alt=" " width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Points to Note Down:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It only Return String.&lt;/li&gt;
&lt;li&gt;You can pass Dynamic Values to it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How To Pass dynamic values to Beanshell hash.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4jnufuecn3sxnfled60.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4jnufuecn3sxnfled60.png" alt=" " width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Call it by passing the dynamic value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbs8xzgmngr50ipju34ul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbs8xzgmngr50ipju34ul.png" alt=" " width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>joget</category>
      <category>beanshell</category>
      <category>lowcode</category>
      <category>automation</category>
    </item>
    <item>
      <title>🌍 Auto-Load Multilingual Dropdown Labels in Joget Using Platform Locale</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Tue, 11 Nov 2025 06:17:11 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/auto-load-multilingual-dropdown-labels-in-joget-using-platform-locale-37md</link>
      <guid>https://forem.com/exploringmylifeworks/auto-load-multilingual-dropdown-labels-in-joget-using-platform-locale-37md</guid>
      <description>&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;When building multilingual Joget apps, it’s important for dropdowns (Select Boxes) to automatically display values in the user’s active language — whether that’s English, Arabic, or any other supported locale.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn how to configure a &lt;strong&gt;Joget Select Box&lt;/strong&gt; to automatically load the &lt;strong&gt;localized label&lt;/strong&gt; based on the platform’s current language setting.&lt;/p&gt;

&lt;p&gt;This approach ensures that users see the same data, but with labels translated according to their locale — all without extra scripting.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ How It Works
&lt;/h2&gt;

&lt;p&gt;💡 The solution uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;SQL query&lt;/strong&gt; as the Select Box’s options source.&lt;/li&gt;
&lt;li&gt;Joget’s variable &lt;code&gt;#platform.currentLanguage#&lt;/code&gt; to detect the logged-in user’s interface language.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;CASE statement&lt;/strong&gt; in SQL to conditionally choose between Arabic and English column names.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever the user changes the platform locale (via User Profile or &lt;code&gt;/web/userview&lt;/code&gt; language toggle), the dropdown automatically displays the correct labels.&lt;/p&gt;




&lt;h2&gt;
  
  
  💻 Full Code
&lt;/h2&gt;

&lt;p&gt;Use the following SQL query in your &lt;strong&gt;Select Box’s “Options Binder → JDBC Binder → SQL Query”&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT 
    c_code AS value, 
    CASE '#platform.currentLanguage#'
        WHEN 'ar' THEN c_name_ar
        ELSE c_name_en
    END AS label
FROM app_fd_governate_mst
ORDER BY label
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;c_code&lt;/code&gt; → The stored value in Joget (e.g., &lt;code&gt;001&lt;/code&gt;, &lt;code&gt;002&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;c_name_en&lt;/code&gt; → English name of the location&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;c_name_ar&lt;/code&gt; → Arabic name of the location&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#platform.currentLanguage#&lt;/code&gt; → Joget’s built-in variable that returns &lt;code&gt;"ar"&lt;/code&gt;, &lt;code&gt;"en"&lt;/code&gt;, or another locale code depending on the user’s active language&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Example Use Cases
&lt;/h2&gt;

&lt;p&gt;🌍 &lt;strong&gt;Government or Region Dropdowns&lt;/strong&gt; — show English or Arabic names dynamically.&lt;br&gt;
🏢 &lt;strong&gt;Branch Selector&lt;/strong&gt; — display localized branch names based on user locale.&lt;br&gt;
🏫 &lt;strong&gt;School / University Lists&lt;/strong&gt; — handle multilingual labels seamlessly.&lt;br&gt;
📦 &lt;strong&gt;Product Category Selector&lt;/strong&gt; — automatically adjust category names to the interface language.&lt;/p&gt;


&lt;h2&gt;
  
  
  🛠️ Customization Tips
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;Add more languages:&lt;/strong&gt;&lt;br&gt;
You can extend the CASE statement to support multiple locales:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CASE '#platform.currentLanguage#'
    WHEN 'ar' THEN c_name_ar
    WHEN 'fr' THEN c_name_fr
    ELSE c_name_en
END
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚙️ &lt;strong&gt;Default language fallback:&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;ELSE c_name_en&lt;/code&gt; ensures that English labels appear when no match is found.&lt;/p&gt;

&lt;p&gt;🗂️ &lt;strong&gt;Table naming convention:&lt;/strong&gt;&lt;br&gt;
Keep your table name generic — e.g., &lt;code&gt;app_fd_location_mst&lt;/code&gt; — to avoid exposing project-specific data.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌟 Key Benefits
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;Fully automatic localization&lt;/strong&gt; — no extra scripting or filters required.&lt;br&gt;
⚙️ &lt;strong&gt;Simple SQL-only setup&lt;/strong&gt; — works directly in Select Box configuration.&lt;br&gt;
💬 &lt;strong&gt;Consistent user experience&lt;/strong&gt; — respects platform locale settings.&lt;br&gt;
📱 &lt;strong&gt;Multilingual scalability&lt;/strong&gt; — add new columns and locales easily.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔒 Security Note
&lt;/h2&gt;

&lt;p&gt;⚠️ Always ensure that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The SQL query only reads from safe, authorized tables.&lt;/li&gt;
&lt;li&gt;No sensitive information is exposed via the &lt;code&gt;label&lt;/code&gt; column.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;parameterized queries&lt;/strong&gt; if dynamic filtering is added later.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>automation</category>
      <category>sql</category>
      <category>joget</category>
      <category>lowcode</category>
    </item>
    <item>
      <title>🗑️ Add Delete Button and Add Row Feature in Joget Spreadsheet using JS</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Tue, 11 Nov 2025 05:44:52 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/add-dynamic-delete-button-and-add-row-feature-in-joget-spreadsheet-3jio</link>
      <guid>https://forem.com/exploringmylifeworks/add-dynamic-delete-button-and-add-row-feature-in-joget-spreadsheet-3jio</guid>
      <description>&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;In Joget, the &lt;strong&gt;Spreadsheet form element&lt;/strong&gt; (powered by Handsontable) is perfect for managing tabular data inside forms.&lt;br&gt;
However, it doesn’t natively provide built-in &lt;strong&gt;row-level delete buttons&lt;/strong&gt; or a convenient &lt;strong&gt;add row&lt;/strong&gt; feature.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll learn how to:&lt;br&gt;
✅ Add a delete button to each row in a Joget Spreadsheet.&lt;br&gt;
✅ Create a separate “Add Row” button that dynamically inserts new rows.&lt;br&gt;
✅ Keep the entire implementation simple, secure, and fully functional.&lt;/p&gt;


&lt;h2&gt;
  
  
  ⚙️ How It Works
&lt;/h2&gt;

&lt;p&gt;💡 The approach uses a &lt;strong&gt;custom column renderer&lt;/strong&gt; and a &lt;strong&gt;jQuery button event&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The custom renderer injects a &lt;strong&gt;Delete&lt;/strong&gt; button in a column cell.&lt;/li&gt;
&lt;li&gt;The button calls &lt;code&gt;hot.alter("remove_row", row)&lt;/code&gt; to delete that row.&lt;/li&gt;
&lt;li&gt;A separate &lt;strong&gt;Add Row&lt;/strong&gt; button triggers &lt;code&gt;hot.alter("insert_row_below")&lt;/code&gt; to insert a new row at the bottom.&lt;/li&gt;
&lt;li&gt;Both actions are performed dynamically without reloading the form.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🧠 &lt;strong&gt;Important:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set the spreadsheet &lt;strong&gt;column format type&lt;/strong&gt; to &lt;code&gt;Custom&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Paste the renderer script directly in the custom field configuration.&lt;/li&gt;
&lt;li&gt;Avoid adding any extra comments inside the &lt;code&gt;renderer&lt;/code&gt; function (to prevent Joget parser issues).&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  💻 Full Code
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- 🔹 Place this inside your Spreadsheet column (Format Type: Custom) --&amp;gt;&lt;/span&gt;
{ { 
  renderer: function (instance, td, row, col, prop, value, cellProperties) { 
    td.innerHTML = "&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;'button'&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;'eaction'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;#i18n.ocs_delete_btn#&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;"; 
    td.style.textAlign = "center"; 
    $(document).ready(function () { 
      let hot = FormUtil.getField("appointments_spreadSheet").data("hot"); 
      let btn = td.querySelector(".eaction"); 
      btn.onclick = function (e) { 
        e.preventDefault(); 
        e.stopPropagation(); 
        hot.alter("remove_row", row); 
      }; 
    }); 
  } 
} }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 🔹 Add this script in a Custom HTML element below your Spreadsheet&lt;/span&gt;

&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#addRowBtn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;FormUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;appointments_spreadSheet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Handsontable not initialized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countRows&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;hot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;insert_row_below&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastRow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to add row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- 🔹 Add Row Button --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"addRowBtn"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  ➕ Add Row
&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧠 Example Use Cases
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;Employee Shift Planner&lt;/strong&gt; – Quickly add or remove shift rows.&lt;br&gt;
✅ &lt;strong&gt;Project Task Tracker&lt;/strong&gt; – Manage task entries dynamically.&lt;br&gt;
✅ &lt;strong&gt;Material Request Form&lt;/strong&gt; – Add/remove item rows on demand.&lt;br&gt;
✅ &lt;strong&gt;Appointment List&lt;/strong&gt; – Delete canceled appointments instantly.&lt;/p&gt;


&lt;h2&gt;
  
  
  🛠️ Customization Tips
&lt;/h2&gt;

&lt;p&gt;💡 &lt;strong&gt;To change the delete button label:&lt;/strong&gt;&lt;br&gt;
Replace&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;#i18n.ocs_delete_btn#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with plain text (e.g., &lt;code&gt;"Delete"&lt;/code&gt; or &lt;code&gt;"Remove"&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;⚙️ &lt;strong&gt;To use a custom Spreadsheet ID:&lt;/strong&gt;&lt;br&gt;
Update this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;FormUtil&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;appointments_spreadSheet&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to match your spreadsheet’s field ID.&lt;/p&gt;

&lt;p&gt;🎨 &lt;strong&gt;To style buttons:&lt;/strong&gt;&lt;br&gt;
Apply CSS in a Custom HTML element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.eaction&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e74c3c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#fff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;#addRowBtn&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🌟 Key Benefits
&lt;/h2&gt;

&lt;p&gt;✅ &lt;strong&gt;User-friendly:&lt;/strong&gt; Adds clear visual controls to manage rows.&lt;br&gt;
⚙️ &lt;strong&gt;Lightweight:&lt;/strong&gt; Uses only jQuery and built-in Joget APIs.&lt;br&gt;
🚀 &lt;strong&gt;Instant updates:&lt;/strong&gt; No form reload needed.&lt;br&gt;
🧩 &lt;strong&gt;Reusable:&lt;/strong&gt; Works in any spreadsheet-based form.&lt;br&gt;
🔧 &lt;strong&gt;Customizable:&lt;/strong&gt; Easily adjust button text, color, or logic.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔒 Security Note
&lt;/h2&gt;

&lt;p&gt;⚠️ Ensure that delete actions are &lt;strong&gt;contextually safe&lt;/strong&gt; —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only use this feature for &lt;strong&gt;client-side data entry&lt;/strong&gt; (not for record deletion in the database).&lt;/li&gt;
&lt;li&gt;If connected to backend records, validate all deletion logic on the server side before applying changes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧭 Final Thoughts
&lt;/h2&gt;

&lt;h2&gt;
  
  
  By adding these small enhancements, your Joget Spreadsheet becomes much more interactive and user-friendly.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fap7hlbau5vfjyoe23unu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fap7hlbau5vfjyoe23unu.png" alt=" " width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frezjso2yngz45of120or.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frezjso2yngz45of120or.png" alt=" " width="800" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>joget</category>
      <category>lowcode</category>
      <category>handsontable</category>
    </item>
    <item>
      <title>🌐Locale Message Updates in Joget Using Beanshell and REST API</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Thu, 06 Nov 2025 05:27:22 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/locale-message-updates-in-joget-using-beanshell-and-rest-api-2i84</link>
      <guid>https://forem.com/exploringmylifeworks/locale-message-updates-in-joget-using-beanshell-and-rest-api-2i84</guid>
      <description>&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;Managing multiple language packs in Joget can become tedious when you frequently update UI text or labels.&lt;br&gt;
This blog demonstrates how to &lt;strong&gt;automate locale message updates&lt;/strong&gt; by fetching localized entries from a database table and submitting them to the Joget message API — all within a &lt;strong&gt;Beanshell script&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Goal:&lt;/strong&gt; Automatically sync custom message entries (key–value–locale) into Joget’s system locale messages.&lt;br&gt;
⚙️ &lt;strong&gt;Technology Used:&lt;/strong&gt; Beanshell, Joget REST API, CSRF-secured POST request.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;🗄️ &lt;strong&gt;Fetch data&lt;/strong&gt; from a custom table (&lt;code&gt;app_fd_locale_msg_update&lt;/code&gt;) containing message keys, values, and locales.&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;Construct a JSON payload&lt;/strong&gt; dynamically for each record.&lt;/li&gt;
&lt;li&gt;🔐 &lt;strong&gt;Retrieve CSRF token&lt;/strong&gt; securely from the Joget runtime.&lt;/li&gt;
&lt;li&gt;🌍 &lt;strong&gt;POST the data&lt;/strong&gt; to Joget’s internal message submission API endpoint.&lt;/li&gt;
&lt;li&gt;🧹 &lt;strong&gt;Clean up&lt;/strong&gt; by deleting the processed record from the table.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This makes localization updates dynamic, safe, and fully automated — without manual form submissions.&lt;/p&gt;




&lt;h2&gt;
  
  
  💻 Full Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.URL&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.HttpURLConnection&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.io.OutputStream&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.io.InputStreamReader&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.io.BufferedReader&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.URLEncoder&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.sql.Connection&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.sql.PreparedStatement&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.sql.ResultSet&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.sql.DataSource&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.joget.apps.app.service.AppUtil&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.joget.commons.util.LogUtil&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.joget.commons.util.SecurityUtil&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;escapeJson&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"\\\\"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"\\\""&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"\\n"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\r"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"\\r"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\t"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"\\t"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1️⃣ Fetch current record ID&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;currentId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#form.locale_msg_update.id#"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  

    &lt;span class="c1"&gt;// 2️⃣ Prepare database connection&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;appId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messageKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messageValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;DataSource&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DataSource&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;AppUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getApplicationContext&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getBean&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"setupDataSource"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;Connection&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConnection&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SELECT * FROM app_fd_locale_msg_update WHERE id = ?"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;PreparedStatement&lt;/span&gt; &lt;span class="n"&gt;ps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;prepareStatement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currentId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;ResultSet&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeQuery&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;appId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"c_app_id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"c_locale"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;messageKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"c_messageKey"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;messageValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"c_message"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;app_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"c_app_version"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;rs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;ps&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nc"&gt;LogUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DB Values"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"appId="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;appId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;", locale="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;", key="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;messageKey&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3️⃣ Build API request&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;appVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#appDef.version#"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;apiUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#request.baseURL#/web/json/console/app/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;appId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;app_version&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/message/submit"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;messageId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messageKey&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"_"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;jsonPayload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"[{\"id\":\""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;escapeJson&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; 
                        &lt;span class="s"&gt;"\",\"key\":\""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;escapeJson&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageKey&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; 
                        &lt;span class="s"&gt;"\",\"value\":\""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;escapeJson&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageValue&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\"}]"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 4️⃣ Include CSRF token&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;csrfParam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SecurityUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCsrfTokenName&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;csrfValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SecurityUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCsrfTokenValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;postData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"appId="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;URLEncoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"&amp;amp;appVersion="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;URLEncoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appVersion&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"&amp;amp;locale="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;URLEncoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"&amp;amp;data="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;URLEncoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonPayload&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"&amp;amp;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;csrfParam&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nc"&gt;URLEncoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;encode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csrfValue&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 5️⃣ Send POST request&lt;/span&gt;
    &lt;span class="nc"&gt;HttpURLConnection&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;BufferedReader&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;URL&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="no"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiUrl&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpURLConnection&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openConnection&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequestMethod&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDoOutput&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequestProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/x-www-form-urlencoded"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequestProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cookie"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"JSESSIONID="&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSession&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequestProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csrfParam&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;csrfValue&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setRequestProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Requested-With"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"XMLHttpRequest"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;OutputStream&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOutputStream&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;write&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;postData&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBytes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flush&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="nc"&gt;InputStreamReader&lt;/span&gt; &lt;span class="n"&gt;isr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getResponseCode&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;
            &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InputStreamReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getErrorStream&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InputStreamReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInputStream&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BufferedReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isr&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;StringBuilder&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StringBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readLine&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="nc"&gt;LogUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LocaleUpdater"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"API Response: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="c1"&gt;// 6️⃣ Delete processed record&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConnection&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;deleteSql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"DELETE FROM app_fd_locale_msg_update WHERE id = ?"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="nc"&gt;PreparedStatement&lt;/span&gt; &lt;span class="n"&gt;deletePs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;prepareStatement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deleteSql&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;deletePs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;currentId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;deletedRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deletePs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeUpdate&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="nc"&gt;LogUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LocaleUpdater"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Deleted rows: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;deletedRows&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;deletePs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;con&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;con&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disconnect&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;LogUtil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"LocaleUpdater"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Error in locale message update script"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  💡 Example Use Cases
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🌍 &lt;strong&gt;Multilingual UI Management:&lt;/strong&gt; Automatically add or modify translated strings without opening the message manager.&lt;/li&gt;
&lt;li&gt;⚙️ &lt;strong&gt;Bulk Localization Sync:&lt;/strong&gt; Use this script in a scheduled process or post-form submission trigger.&lt;/li&gt;
&lt;li&gt;🧾 &lt;strong&gt;Translation Review Workflow:&lt;/strong&gt; Integrate it with a form where translators submit updates that get pushed into Joget once approved.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧰 Customization Tips
&lt;/h2&gt;

&lt;p&gt;✅ Modify the source table name if your schema differs.&lt;br&gt;
✅ Add &lt;code&gt;WHERE&lt;/code&gt; clauses or filters to handle batch updates.&lt;br&gt;
✅ If used with multiple locales, consider looping through a list of pending records.&lt;br&gt;
✅ You can return a JSON status to display on form submission (optional).&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Key Benefits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;⚙️ &lt;strong&gt;Automation:&lt;/strong&gt; Removes manual locale message maintenance.&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;Accuracy:&lt;/strong&gt; Ensures translation entries stay consistent across versions.&lt;/li&gt;
&lt;li&gt;🔄 &lt;strong&gt;Integration:&lt;/strong&gt; Works with internal Joget CSRF-secured APIs.&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Reusability:&lt;/strong&gt; Plug this into any app that maintains localized text data.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔒 Security Note
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Always use &lt;code&gt;SecurityUtil.getCsrfTokenValue(request)&lt;/code&gt; to include the valid CSRF token.&lt;/li&gt;
&lt;li&gt;Avoid exposing internal API URLs publicly.&lt;/li&gt;
&lt;li&gt;Validate &lt;code&gt;messageKey&lt;/code&gt; and &lt;code&gt;locale&lt;/code&gt; before submission to prevent injection attacks.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🏁 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This automation provides a robust way to manage dynamic language updates directly from your form data into the Joget platform — no manual clicks required.&lt;br&gt;
By coupling Beanshell with Joget’s secure internal API, you gain a powerful extension point for localization and continuous deployment of translated UI assets.&lt;/p&gt;

</description>
      <category>joget</category>
      <category>beanshell</category>
      <category>lowcode</category>
      <category>i18n</category>
    </item>
    <item>
      <title>🧩 Track Workflow Activities by Name in Joget Using SQL and DataList Configuration</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Wed, 05 Nov 2025 07:37:51 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/track-workflow-activities-by-name-in-joget-using-sql-and-datalist-configuration-3gkn</link>
      <guid>https://forem.com/exploringmylifeworks/track-workflow-activities-by-name-in-joget-using-sql-and-datalist-configuration-3gkn</guid>
      <description>&lt;h2&gt;
  
  
  🧭 Overview
&lt;/h2&gt;

&lt;p&gt;In Joget, workflow tracking is essential for monitoring running or pending activities within business processes.&lt;br&gt;&lt;br&gt;
This implementation allows you to &lt;strong&gt;track workflow activities by their activity names&lt;/strong&gt; (e.g., those containing &lt;code&gt;"validate request"&lt;/code&gt;) by joining Joget’s workflow tables.&lt;/p&gt;

&lt;p&gt;The combination of SQL and DataList configuration provides an easy way to display:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔢 Process IDs
&lt;/li&gt;
&lt;li&gt;🧾 Record IDs
&lt;/li&gt;
&lt;li&gt;👤 Pending Users
&lt;/li&gt;
&lt;li&gt;⚙️ Activity Details
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚙️ How It Works
&lt;/h2&gt;

&lt;p&gt;Here’s the breakdown of how the query and DataList integration work together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧩 &lt;strong&gt;SHKAssignmentsTable&lt;/strong&gt; stores assignment-level workflow information.
&lt;/li&gt;
&lt;li&gt;🔗 &lt;strong&gt;wf_process_link&lt;/strong&gt; maps workflow process instances to their originating form records.
&lt;/li&gt;
&lt;li&gt;⚙️ The SQL &lt;code&gt;JOIN&lt;/code&gt; retrieves both workflow instance and record identifiers.
&lt;/li&gt;
&lt;li&gt;🔍 &lt;code&gt;WHERE ActivityId LIKE '%validate%request%'&lt;/code&gt; filters workflow activities by name pattern.
&lt;/li&gt;
&lt;li&gt;💡 DataList columns map the query results for display within Joget’s UI.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Full Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  📘 SQL Query
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT 
    shk.*, 
    wfl.originProcessId AS recordId
FROM 
    SHKAssignmentsTable AS shk
INNER JOIN 
    wf_process_link AS wfl
    ON shk.ActivityProcessId = wfl.processId
WHERE 
    ActivityId LIKE '%validate%request%';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;

&lt;p&gt;This query returns all workflow assignments for activities whose IDs or names contain &lt;code&gt;"validate"&lt;/code&gt; and &lt;code&gt;"request"&lt;/code&gt; — allowing you to trace specific approval or validation tasks.&lt;/p&gt;




&lt;h3&gt;
  
  
  🧱 DataList JSON Columns
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
"columns": [&lt;br&gt;
  {&lt;br&gt;
    "id": "column_9",&lt;br&gt;
    "name": "ResourceId",&lt;br&gt;
    "label": "Pending With",&lt;br&gt;
    "filterable": true,&lt;br&gt;
    "hidden": "false",&lt;br&gt;
    "sortable": "false"&lt;br&gt;
  },&lt;br&gt;
  {&lt;br&gt;
    "id": "column_1",&lt;br&gt;
    "name": "ActivityProcessId",&lt;br&gt;
    "label": "Process ID",&lt;br&gt;
    "filterable": true,&lt;br&gt;
    "hidden": "false",&lt;br&gt;
    "sortable": "false"&lt;br&gt;
  },&lt;br&gt;
  {&lt;br&gt;
    "id": "column_0",&lt;br&gt;
    "name": "recordId",&lt;br&gt;
    "label": "Record ID",&lt;br&gt;
    "filterable": true,&lt;br&gt;
    "hidden": "false",&lt;br&gt;
    "sortable": "false"&lt;br&gt;
  },&lt;br&gt;
  {&lt;br&gt;
    "id": "column_7",&lt;br&gt;
    "name": "ActivityId",&lt;br&gt;
    "label": "Activity ID",&lt;br&gt;
    "filterable": true,&lt;br&gt;
    "hidden": "false",&lt;br&gt;
    "sortable": "false"&lt;br&gt;
  },&lt;br&gt;
  {&lt;br&gt;
    "id": "column_6",&lt;br&gt;
    "name": "ActivityProcessDefName",&lt;br&gt;
    "label": "Activity Process Definition Name",&lt;br&gt;
    "filterable": true,&lt;br&gt;
    "hidden": "false",&lt;br&gt;
    "sortable": "false"&lt;br&gt;
  }&lt;br&gt;
]&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  💼 Example Use Cases
&lt;/h2&gt;

&lt;p&gt;✅ Identify all pending “validation” tasks across multiple running workflows.&lt;br&gt;
✅ Quickly locate which users are holding specific process steps.&lt;br&gt;
✅ Monitor validation-related approvals for auditing or SLA tracking.&lt;br&gt;
✅ Build a &lt;strong&gt;Workflow Tracker Dashboard&lt;/strong&gt; within Joget using a DataList and this query.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Customization Tips
&lt;/h2&gt;

&lt;p&gt;💡 Modify the &lt;code&gt;LIKE&lt;/code&gt; condition to match any keyword (e.g., &lt;code&gt;%approval%&lt;/code&gt; or &lt;code&gt;%review%&lt;/code&gt;).&lt;br&gt;
⚙️ Add date filters (&lt;code&gt;shk.DateCreated&lt;/code&gt;) for recent activity monitoring.&lt;br&gt;
🔁 Join with &lt;code&gt;SHKActivitiesTable&lt;/code&gt; to include activity names or durations.&lt;br&gt;
📊 Use &lt;strong&gt;Datalist Formatter Plugins&lt;/strong&gt; for color-coded task states (e.g., Overdue, Pending).&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Key Benefits
&lt;/h2&gt;

&lt;p&gt;✅ Provides full visibility into active workflow states.&lt;br&gt;
⚡ Reduces dependency on manual process tracking.&lt;br&gt;
📈 Enables performance dashboards for process bottlenecks.&lt;br&gt;
🔎 Easy to extend with filters and user-based views.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔐 Security Note
&lt;/h2&gt;

&lt;p&gt;⚠️ Ensure that only authorized users (e.g., administrators or process owners) have access to this DataList.&lt;br&gt;
⚠️ Avoid exposing sensitive &lt;code&gt;ResourceId&lt;/code&gt; or &lt;code&gt;ProcessId&lt;/code&gt; fields to public users.&lt;br&gt;
⚠️ Apply &lt;strong&gt;Joget permission controls&lt;/strong&gt; (e.g., Userview Permission) on the DataList page.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This workflow tracking implementation helps administrators and managers gain real-time insights into active Joget processes by activity name.&lt;br&gt;
It’s a powerful tool for &lt;strong&gt;auditing, debugging, and reporting&lt;/strong&gt; in large workflow environments.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>joget</category>
      <category>lowcode</category>
      <category>automaton</category>
    </item>
    <item>
      <title>🔄 Process Migration Between App Versions in Joget</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Wed, 05 Nov 2025 07:27:29 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/process-migration-between-app-versions-in-joget-bok</link>
      <guid>https://forem.com/exploringmylifeworks/process-migration-between-app-versions-in-joget-bok</guid>
      <description>&lt;h2&gt;
  
  
  🧭 Overview
&lt;/h2&gt;

&lt;p&gt;When upgrading an app in Joget from one version to another, existing running workflow instances might still be tied to the old version.&lt;br&gt;&lt;br&gt;
This BeanShell script automates the &lt;strong&gt;migration of running workflow processes&lt;/strong&gt; from a previous app version (&lt;code&gt;from_version&lt;/code&gt;) to a new version (&lt;code&gt;to_version&lt;/code&gt;), ensuring smooth continuity without manually aborting or recreating processes.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ How It Works
&lt;/h2&gt;

&lt;p&gt;Here’s how this BeanShell script operates inside a &lt;strong&gt;Form Store Binder&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚙️ Fetches the submitted data (App ID, From Version, To Version, Process ID).
&lt;/li&gt;
&lt;li&gt;🔍 Retrieves all &lt;strong&gt;running workflow processes&lt;/strong&gt; for the specified &lt;code&gt;app_id&lt;/code&gt; and &lt;code&gt;from_version&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;📦 Loads all available process definitions from the new version (&lt;code&gt;to_version&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;🔁 Compares process definitions between old and new versions.
&lt;/li&gt;
&lt;li&gt;🧩 For matching process definitions:

&lt;ul&gt;
&lt;li&gt;Migrates the workflow instance to the new version.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;🚫 For unmatched processes:

&lt;ul&gt;
&lt;li&gt;Aborts them safely to maintain workflow consistency.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;🧾 Logs every action and updates a &lt;code&gt;num_migrated&lt;/code&gt; field with the total migrated count.
&lt;/li&gt;

&lt;li&gt;💾 Finally, uses the &lt;code&gt;WorkflowFormBinder&lt;/code&gt; to store the updated record.&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Full Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.Collection;
import java.util.ArrayList;
import org.joget.commons.util.LogUtil;
import org.joget.apps.form.model.Element;
import org.joget.apps.form.model.FormData;
import org.joget.apps.form.model.FormRow;
import org.joget.apps.form.model.FormRowSet;
import org.joget.apps.form.service.FormUtil;
import org.joget.apps.form.model.FormStoreBinder;
import org.joget.plugin.base.PluginManager;
import org.joget.apps.app.model.AppDefinition;
import org.joget.apps.app.model.PackageDefinition;
import org.joget.apps.app.service.AppUtil;
import org.joget.workflow.model.WorkflowProcess;
import org.joget.workflow.model.service.WorkflowManager;
import org.joget.workflow.util.WorkflowUtil;

public FormRowSet execute(element, rows, formData) {

    if (rows != null &amp;amp;&amp;amp; !rows.isEmpty()) {
        WorkflowManager workflowManager = (WorkflowManager) AppUtil.getApplicationContext().getBean("workflowManager");

        FormRow row = rows.get(0);
        String appId = row.getProperty("app_id");
        String fromVersion = row.getProperty("from_version");
        String toVersion = row.getProperty("to_version");
        String single_processId = row.getProperty("single_processId");

        Collection runningProcessList = workflowManager.getRunningProcessList(appId, null, null, fromVersion, null, null, 0, null);
        Collection processes = workflowManager.getProcessList(appId, toVersion);

        LogUtil.info("BeanShell", "Updating running processes for " + appId + " from " + fromVersion + " to " + toVersion);

        if (!runningProcessList.isEmpty() &amp;amp;&amp;amp; !processes.isEmpty()) {
            Collection newProcessDefIds = new ArrayList();
            for (WorkflowProcess process : processes) {
                newProcessDefIds.add(process.getId());
            }

            for (WorkflowProcess process : runningProcessList) {
                String processId = null;
                try {
                    processId = process.getInstanceId();
                    String processDefId = process.getId();
                    processDefId = processDefId.replace("#" + fromVersion.toString() + "#", "#" + toVersion.toString() + "#");

                    if (newProcessDefIds.contains(processDefId)) {
                        workflowManager.processCopyFromInstanceId(processId, processDefId, true);
                        LogUtil.info("BeanShell", "Migrated process " + processId + ".");
                    } else {
                        workflowManager.processAbort(processId);
                        LogUtil.info("BeanShell", "Process Def ID " + processDefId + " does not exist. Aborted process " + processId + ".");
                    }
                } catch (Exception e) {
                    LogUtil.error(getClass().getName(), e, "Error updating process " + processId);
                }
            }

            row.setProperty("num_migrated", runningProcessList.size().toString());
            LogUtil.info("BeanShell", "Completed updating running processes for " + appId + " from " + fromVersion + " to " + toVersion);
        } else {
            LogUtil.info("BeanShell", "No running processes to update for " + appId + " from " + fromVersion + " to " + toVersion);
        }

        PluginManager pluginManager = (PluginManager) AppUtil.getApplicationContext().getBean("pluginManager");
        FormStoreBinder binder = (FormStoreBinder) pluginManager.getPlugin("org.joget.apps.form.lib.WorkflowFormBinder");
        binder.store(element, rows, formData);
    }

    return rows;
}

return execute(element, rows, formData);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;




&lt;h2&gt;
  
  
  💼 Example Use Cases
&lt;/h2&gt;

&lt;p&gt;✅ Migrating all running workflow instances when deploying a &lt;strong&gt;new app version&lt;/strong&gt;.&lt;br&gt;
✅ Keeping workflow states consistent between &lt;strong&gt;staging and production environments&lt;/strong&gt;.&lt;br&gt;
✅ Automating process transitions during &lt;strong&gt;app upgrades or refactoring&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Customization Tips
&lt;/h2&gt;

&lt;p&gt;💡 Add filtering logic to migrate only specific processes by ID or status.&lt;br&gt;
⚙️ Log more detailed migration statistics (e.g., per-process-type counts).&lt;br&gt;
🔁 Replace the manual &lt;code&gt;from_version&lt;/code&gt; / &lt;code&gt;to_version&lt;/code&gt; fields with auto-detected app versions using &lt;code&gt;AppDefinition&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Key Benefits
&lt;/h2&gt;

&lt;p&gt;✅ Saves hours of manual migration work.&lt;br&gt;
⚡ Ensures version continuity without breaking process logic.&lt;br&gt;
🧩 Automatically cleans up invalid or outdated process instances.&lt;br&gt;
🔒 Prevents version mismatches during deployment.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔐 Security Note
&lt;/h2&gt;

&lt;p&gt;⚠️ Always validate the &lt;code&gt;app_id&lt;/code&gt; and version inputs to prevent accidental cross-app migrations.&lt;br&gt;
⚠️ Avoid exposing internal process IDs in UI or logs in production.&lt;br&gt;
⚠️ Consider restricting this script’s execution to &lt;strong&gt;admin roles&lt;/strong&gt; only.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;This script provides a robust way to keep your workflow processes synchronized between Joget app versions — perfect for continuous delivery environments or versioned deployments.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>java</category>
      <category>joget</category>
      <category>beanshellscript</category>
    </item>
    <item>
      <title>💾 Custom "Save as Draft Without Validation" Button with Auto-Redirect in Joget Forms</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Tue, 04 Nov 2025 06:13:00 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/custom-save-as-draft-without-validation-button-with-auto-redirect-in-joget-forms-1839</link>
      <guid>https://forem.com/exploringmylifeworks/custom-save-as-draft-without-validation-button-with-auto-redirect-in-joget-forms-1839</guid>
      <description>&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;In many Joget applications, users want the flexibility to &lt;strong&gt;save their form progress as a draft&lt;/strong&gt; and return later to complete it.&lt;/p&gt;

&lt;p&gt;This guide shows how to create a &lt;strong&gt;custom "Save as Draft" button&lt;/strong&gt; in Joget using a BeanShell script that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds a &lt;strong&gt;Save as Draft button&lt;/strong&gt; dynamically.
&lt;/li&gt;
&lt;li&gt;Automatically updates workflow status fields (&lt;code&gt;status&lt;/code&gt; and &lt;code&gt;wf_status&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;Redirects users to the &lt;strong&gt;Inbox&lt;/strong&gt; (or another page) after saving.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚙️ How It Works
&lt;/h2&gt;

&lt;p&gt;✅ The script dynamically inserts a &lt;code&gt;SaveAsDraftButton&lt;/code&gt; inside a section’s layout.&lt;br&gt;&lt;br&gt;
✅ It then injects a small JavaScript snippet using a &lt;code&gt;CustomHTML&lt;/code&gt; element to handle:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Status updates to &lt;code&gt;"Draft"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Conditional redirection to a custom Userview page (&lt;code&gt;your_menu_id&lt;/code&gt;)
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💻 Full Code
&lt;/h2&gt;

&lt;p&gt;Paste this BeanShell script into your &lt;strong&gt;Form → Section → Data → Load Binder → BeanShell&lt;/strong&gt;:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import org.joget.apps.form.lib.SaveAsDraftButton;
import org.joget.apps.form.lib.SubmitButton;
import org.joget.apps.form.lib.CustomHTML;
import org.joget.apps.form.model.Column;
import org.joget.apps.form.model.Element;
import org.joget.apps.form.model.FormAction;
import org.joget.apps.form.model.FormData;
import org.joget.apps.form.model.Section;
import org.joget.apps.form.service.FormUtil;
import java.util.ArrayList;
import java.util.Collection;

// Create a collection for form actions
Collection formActions = new ArrayList();
String saveButtonLabel = "Save As Draft"; 
Element saveButton = new SaveAsDraftButton();
saveButton.setProperty(FormUtil.PROPERTY_ID, "customSaveAsDraft");
saveButton.setProperty("label", saveButtonLabel);
formActions.add(saveButton);

// Get the form section and its first column
Section section = element;
ArrayList columns = (ArrayList) section.getChildren();
Column column = columns.get(0);
column.setProperty("horizontal", "true");
column.setChildren(formActions);

// Add layout fix and script logic
Element html = new CustomHTML();
String script = "&amp;lt;script&amp;gt;$(document).ready(function(){ ";

// Move the button layout into section-actions
script += "$('#" + section.getPropertyString("id") + "').find('.form-cell').prependTo('#section-actions .form-column');";
script += "$('#" + section.getPropertyString("id") + "').remove();";

// Set workflow status to Draft
script += "$('#status').val('Draft').trigger('change');";
script += "$('#wf_status').val('Draft').trigger('change');";

// Check if Save As Draft button was clicked and redirect
FormData fd = formData;
if (fd.getRequestParameter("customSaveAsDraft") != null) {
    script += "window.location.href='your_menu_id';"; // Redirect to Inbox or another page
}

script += "});&amp;lt;/script&amp;gt;";

// Apply the custom HTML script
html.setProperty("id", "button_layout_fixes");
html.setProperty("label", "");
html.setProperty("value", script);
formActions.add(html);

return null;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>joget</category>
      <category>beanshell</category>
      <category>java</category>
      <category>lowcode</category>
    </item>
    <item>
      <title>💱 Automate Currency Conversion in Joget Using JavaScript and External API</title>
      <dc:creator>Explorer</dc:creator>
      <pubDate>Mon, 03 Nov 2025 07:42:56 +0000</pubDate>
      <link>https://forem.com/exploringmylifeworks/automate-live-currency-conversion-in-joget-builder-using-javascript-2353</link>
      <guid>https://forem.com/exploringmylifeworks/automate-live-currency-conversion-in-joget-builder-using-javascript-2353</guid>
      <description>&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;In many Joget applications, users enter transaction amounts in different currencies. To maintain financial consistency, it’s crucial to automatically convert these values into a standard base currency (like AED or USD).  &lt;/p&gt;

&lt;p&gt;This guide shows how to &lt;strong&gt;automate currency conversion inside a Joget form&lt;/strong&gt; using &lt;strong&gt;JavaScript&lt;/strong&gt;, &lt;strong&gt;Fetch API&lt;/strong&gt;, and &lt;strong&gt;jQuery Autocomplete&lt;/strong&gt; — all without writing any backend plugin code.  &lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ How It Works
&lt;/h2&gt;

&lt;p&gt;✅ On form load:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The script sets the AED amount field to zero.
&lt;/li&gt;
&lt;li&gt;Fetches live currency symbols from a safe exchange rate API.
&lt;/li&gt;
&lt;li&gt;Activates autocomplete on the currency input field.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚙️ Whenever a user changes:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The currency,
&lt;/li&gt;
&lt;li&gt;The amount, or
&lt;/li&gt;
&lt;li&gt;The product name,
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;the script recalculates the AED value dynamically and updates the related fields (&lt;code&gt;amountInAed&lt;/code&gt;, &lt;code&gt;BalanceAmount&lt;/code&gt;, and &lt;code&gt;exchangeRate&lt;/code&gt;).  &lt;/p&gt;




&lt;h2&gt;
  
  
  💻 Full Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Initialize amount field&lt;/span&gt;
    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#amountInAed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Fetch all currency symbols from a safe API endpoint&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://your-safe-api-endpoint/v4/latest/AED`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Enable autocomplete for currency input field&lt;/span&gt;
        &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#CURRENCY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;autocomplete&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="c1"&gt;// (Optional) Populate dropdown instead of autocomplete&lt;/span&gt;
        &lt;span class="c1"&gt;// currency.forEach((data) =&amp;gt; {&lt;/span&gt;
        &lt;span class="c1"&gt;//   $("select[name='CURRENCY']").append(`&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;option&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/option&amp;gt;`&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;        &lt;span class="c1"&gt;// });&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Trigger conversion whenever key fields change&lt;/span&gt;
    &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input[name='CURRENCY'], input[id='AMOUNT'], select[name=productName]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;change keyup click&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nc"&gt;JogetgetResults&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Fetch conversion rates and update results&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;JogetgetResults&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://your-safe-api-endpoint/v4/latest/USD`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JogetdisplayResults&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Perform conversion and update fields&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;JogetdisplayResults&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;fromRateValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[name="CURRENCY"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fromRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fromRateValue&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;toRate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

      &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;toRate&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;fromRate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input[id='AMOUNT']&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// Update converted amount fields&lt;/span&gt;
      &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input[id='amountInAed']&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input[id='amountInAed1']&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-IN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="c1"&gt;// Update balance fields&lt;/span&gt;
      &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#BalanceAmount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[name=rembudgetAmount]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#BalanceAmount1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[name=rembudgetAmount]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLocaleString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-IN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

      &lt;span class="c1"&gt;// Display exchange rate information&lt;/span&gt;
      &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;DividedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toRate&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;fromRate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toRate&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;fromRate&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#exchangeRate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`1 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fromRateValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;DividedValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; AED`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#exchangeRate1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`1 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fromRateValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;DividedValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; AED`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🧠 Example Use Cases
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Expense Forms&lt;/strong&gt; — Automatically convert foreign expenses into AED.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Quotation Requests&lt;/strong&gt; — Display AED equivalent of multi-currency offers.&lt;br&gt;&lt;br&gt;
✅ &lt;strong&gt;Budget Management&lt;/strong&gt; — Show live exchange rate for spending insights.  &lt;/p&gt;




&lt;h3&gt;
  
  
  🛠 Customization Tips
&lt;/h3&gt;

&lt;p&gt;💡 Replace &lt;code&gt;"AED"&lt;/code&gt; and &lt;code&gt;"USD"&lt;/code&gt; with your preferred base and target currencies.&lt;br&gt;&lt;br&gt;
💡 You can use a dropdown instead of autocomplete by uncommenting the related lines.&lt;br&gt;&lt;br&gt;
💡 Adjust &lt;code&gt;toLocaleString('en-IN')&lt;/code&gt; for your region (e.g., &lt;code&gt;'en-US'&lt;/code&gt; for US format).  &lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 Key Benefits
&lt;/h3&gt;

&lt;p&gt;✅ Fully client-side — no backend logic needed.&lt;br&gt;&lt;br&gt;
✅ Reduces human error in currency entry.&lt;br&gt;&lt;br&gt;
✅ Provides real-time and transparent conversion.&lt;br&gt;&lt;br&gt;
✅ Easy to extend or modify for multiple targets.  &lt;/p&gt;




&lt;h3&gt;
  
  
  🔒 Security Note
&lt;/h3&gt;

&lt;p&gt;Always use a trusted and HTTPS-secured API endpoint to fetch exchange rates.&lt;br&gt;&lt;br&gt;
Avoid exposing sensitive API keys directly in client-side code.  &lt;/p&gt;




&lt;h3&gt;
  
  
  🏁 Final Thoughts
&lt;/h3&gt;

&lt;p&gt;This lightweight JavaScript solution empowers Joget users to manage real-time currency conversion directly in forms — boosting both usability and data accuracy.&lt;/p&gt;

</description>
      <category>joget</category>
      <category>lowcode</category>
      <category>javascript</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
