<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://daniels-notes.de/feed.xml" rel="self" type="application/atom+xml" /><link href="https://daniels-notes.de/" rel="alternate" type="text/html" /><updated>2026-05-16T10:04:52+00:00</updated><id>https://daniels-notes.de/feed.xml</id><title type="html">Daniel’s notes</title><subtitle>Blog about WEBCON BPS and GitHub projects.</subtitle><author><name>Daniel Krüger</name></author><entry><title type="html">Removing binary content of deleted attachments</title><link href="https://daniels-notes.de/posts/2026/remove-content-of-deleted-attachments" rel="alternate" type="text/html" title="Removing binary content of deleted attachments" /><published>2026-05-16T00:00:00+00:00</published><updated>2026-05-16T00:00:00+00:00</updated><id>https://daniels-notes.de/posts/2026/remove-content-of-deleted-attachments</id><content type="html" xml:base="https://daniels-notes.de/posts/2026/remove-content-of-deleted-attachments"><![CDATA[<h1 id="overview">Overview</h1>

<p>When a user removes an attachment in the BPS Portal, or when an automation removes it without explicitly clearing the file content, the binary data stays in the database. The attachment is flagged as deleted but the bytes are never freed.</p>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-11-18-52.png" alt="Diskspace allocated by deleted attachments." /><figcaption>
      Diskspace allocated by deleted attachments.

    </figcaption></figure>

<p>Whether this is an issue depends on your use case. If it’s a generated document, you likely don’t need those. On the other hand, it may be even an issue, that users can access a deleted version. While you can correct this for the future, you still need to handle the old documents.</p>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-11-55-40.png" alt="A user can view and download a deleted attachment from history." /><figcaption>
      A user can view and download a deleted attachment from history.

    </figcaption></figure>

<p>WEBCON BPS has no built-in mechanism to clean this up.</p>
<blockquote>
  <p><a href="https://community.webcon.com/forum/thread/8056?messageid=8056">Delete attachments with binary data</a><br />
Konrad Keppert (WEBCON)<br />
Unfortunately, there isn’t a simple way. Once an attachment is removed, it is flagged as deleted in the database and further actions do not operate on it.
The functionality to remove binary data of deleted attachments is in backlog and I hope it will be implemented sooner than later.
As a workaround, advanced users may want to <em>manually update</em> ATF_Value to empty string or change ATT_IsDeleted to 0 and then execute an action again.</p>
</blockquote>

<p>If something is not possible with the built-in options, it’s a good topic for a blog post. :)</p>

<h1 id="removing-the-content-of-deleted-attachments">Removing the content of deleted attachments</h1>
<p>The suggested way requires modifying the database directly. We can simply clear the content directly instead of marking the file as not deleted and then trigger the action to remove the content.</p>

<p>I prepared two versions for this:</p>
<ol>
  <li>Single script <br />
  This is intended for a quick execution / limited amount of data you want to remove in a test database. This runs in a single transaction.</li>
  <li>Database backend <br />
  I’ve used this to cleanup a large amount of data in a production environment. Production environments are always special, and I wanted to have a better option to verify what would happen and what did happen. In addition, this will execute the cleanup in transaction batches, which will prevent the transaction log file grows too much.</li>
</ol>

<p>Both approaches clear the file content by setting <code class="language-plaintext highlighter-rouge">ATF_Value</code> to an empty binary value (<code class="language-plaintext highlighter-rouge">0x0</code>) and marking both the attachment and its versions as deleted. Neither approach deletes rows from the database, so no referential integrity is affected.</p>

<p class="notice--warning"><strong>Remark:</strong> If the attachment isn’t deleted I recommend to use the action <code class="language-plaintext highlighter-rouge">Remove attachment</code> and set the mode to <code class="language-plaintext highlighter-rouge">Remove only history of binary data</code>. 
  <img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-10-00-14.png" alt="It's possible to remove the content of old versions for non-deleted attachments." /></p>

<p class="notice--warning"><strong>Remark:</strong> These scripts modify data directly in the WEBCON BPS database. Always run in dry-run mode first, take a backup before executing, and test in a non-production environment.</p>

<h2 id="option-1-single-script">Option 1: Single script</h2>

<p>This script is suited for targeted cleanups on a smaller number of records. It uses a table variable to collect the records matching your filter, shows a preview in dry-run mode or executes the update inside a single transaction.</p>

<p>The key configuration is at the top of the script:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">declare</span> <span class="o">@</span><span class="n">dryrun</span> <span class="nb">bit</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>  <span class="c1">-- Set to 1 for dry run (preview), 0 for actual update</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">WHERE</code> clause controls which attachments are selected. Adjust it to match your scenario:</p>

<div class="language-sql notice--info highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">where</span> <span class="cm">/* ATT_Name like 'GeneratedDocument%' */</span>
    <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="mi">993</span>
    <span class="k">and</span> <span class="n">ATT_IsDeleted</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="k">and</span> <span class="n">datalength</span><span class="p">(</span><span class="n">ATF_Value</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span><span class="p">;</span>
</code></pre></div></div>
<p><strong>Tip:</strong> Take a look at <a href="#filters">Filters</a> for a few suggestions.</p>

<p>Make sure that you update all references to the correct databases. There are three places in the script in which you need to define the content database and the attachment file database. The latter one can be different for each process. You can simply search for <code class="language-plaintext highlighter-rouge">Content</code> in the script.</p>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-10-21-57.png" alt="Update the database names to match your process configuration." /><figcaption>
      Update the database names to match your process configuration.

    </figcaption></figure>

<p class="notice--info"><strong>Tip:</strong> If you have already opened up the SQL Management Studio, you can use this statement to get the attachment database name of the processes.</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">select</span> <span class="n">DEF_Name</span><span class="p">,</span> <span class="n">DEF_AttachmentsDatabase</span>
<span class="k">from</span> <span class="n">WFDefinitions</span> 
</code></pre></div></div>

<p>In dry-run mode the script prints the estimated data volume and lists every record that would be affected. When you set <code class="language-plaintext highlighter-rouge">@dryrun = 0</code> the update runs in a single transaction and rolls back automatically if the affected row count does not match the expected count.</p>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-10-27-27.png" alt="Result of the dry-run." /><figcaption>
      Result of the dry-run.

    </figcaption></figure>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-10-28-07.png" alt="Result of the executed script." /><figcaption>
      Result of the executed script.

    </figcaption></figure>

<p class="notice--warning"><strong>Remark:</strong> Because the update runs in a single transaction, the SQL Server transaction log will grow by at least the total size of the binary data being cleared. Verify that you have sufficient free space in the transaction log and data files before running the actual update on a large dataset. If in doubt, use Option 2.</p>

<h2 id="option-2-database-backend-approach">Option 2: Database backend approach</h2>

<p>This approach is designed for larger or long-running cleanups where you want better visibility and control. It uses a dedicated database <code class="language-plaintext highlighter-rouge">AttachmentCleanupDB</code> as a job log and processes records in configurable batches, committing each batch separately to keep the transaction log manageable.</p>

<p>The four scripts are intended to be run in order.</p>

<h3 id="script-1-setup">Script 1: Setup</h3>

<p>Run <code class="language-plaintext highlighter-rouge">AttachmentCleanupDB_1_setup.sql</code> once. It creates the <code class="language-plaintext highlighter-rouge">AttachmentCleanupDB</code> database, the <code class="language-plaintext highlighter-rouge">AttachmentCleanupLog</code> table, and two stored procedures:</p>

<ul>
  <li>
    <p>sp_AttachmentCleanup_DryRun<br />
Reads the log table and shows all pending records along with their current state in the live database.</p>
  </li>
  <li>
    <p>sp_AttachmentCleanup_BatchDelete<br />
Processes pending records in batches. Each batch is committed independently. Records that fail are marked with status <code class="language-plaintext highlighter-rouge">Error</code> and the error message is stored in the log table for review.</p>
  </li>
</ul>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-10-29-23.png" alt="The generated database." /><figcaption>
      The generated database.

    </figcaption></figure>

<h3 id="script-2-populate-log-table">Script 2: Populate log table</h3>

<p><code class="language-plaintext highlighter-rouge">AttachmentCleanupDB_2_populate.sql</code> queries the WEBCON BPS databases and inserts the matching attachment records into <code class="language-plaintext highlighter-rouge">AttachmentCleanupLog</code> with status <code class="language-plaintext highlighter-rouge">Pending</code>.</p>

<p>Before running the script, you need to:</p>
<ol>
  <li>Update the database names <br />
  WEBCON BPS can store attachment data in a dedicated database that is separate from the content database. These are defined by each process.</li>
  <li>Update the filter <br />
  This should match your scenario. I’ve listed a few suggestions <a href="#filters">here</a>.</li>
</ol>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-10-40-18.png" alt="You need to update these parts to match your environment and filter conditions." /><figcaption>
      You need to update these parts to match your environment and filter conditions.

    </figcaption></figure>

<p class="notice--warning"><strong>Remark:</strong> You need to provide the database names in the SELECT because the other stored procedures will generate a statement using these. Otherwise, we would need to modify the script each time to match the target databases.</p>

<p class="notice--info"><strong>Tip:</strong> If you already opened up the SQL Management Studio, you can use this statement to get the attachment database name of the procresses.</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">select</span> <span class="n">DEF_Name</span><span class="p">,</span> <span class="n">DEF_AttachmentsDatabase</span>
<span class="k">from</span> <span class="n">WFDefinitions</span> 
</code></pre></div></div>

<p>You can run this script multiple times with different filters to add more records to the log before starting the deletion.</p>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-10-44-24.png" alt="You can view the `AttachmentCleanupLog` table to verify the results." /><figcaption>
      You can view the <code class="language-plaintext highlighter-rouge">AttachmentCleanupLog</code> table to verify the results.

    </figcaption></figure>

<h3 id="script-3-dry-run">Script 3: Dry-run</h3>

<p><code class="language-plaintext highlighter-rouge">AttachmentCleanupDB_3_dryrun_proc.sql</code> calls <code class="language-plaintext highlighter-rouge">sp_AttachmentCleanup_DryRun</code>. Review the output to confirm the record list. This is the last step, to ensure that everything is fine.</p>

<ol>
  <li>You can compare the records using the file version</li>
  <li>Verify the current/new status / file size.</li>
  <li>You can view the generated statement.</li>
</ol>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-11-58-23.png" alt="The dry-run offers an option to review the files which are intended to be cleared." /><figcaption>
      The dry-run offers an option to review the files which are intended to be cleared.

    </figcaption></figure>

<h3 id="script-4-batch-delete">Script 4: Batch delete</h3>

<p><code class="language-plaintext highlighter-rouge">AttachmentCleanupDB_4_batchdelete_proc.sql</code> calls <code class="language-plaintext highlighter-rouge">sp_AttachmentCleanup_BatchDelete</code> with a configurable batch size (default 1000). Each batch commits independently, so the transaction log stays under control even for very large datasets.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">exec</span> <span class="n">sp_AttachmentCleanup_BatchDelete</span> <span class="o">@</span><span class="n">BatchSize</span> <span class="o">=</span> <span class="mi">1000</span>
</code></pre></div></div>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-10-57-14.png" alt="The test executed the deletion in batches of 1 for testing purposes." /><figcaption>
      The test executed the deletion in batches of 1 for testing purposes.

    </figcaption></figure>

<p>After completion, you can check the log table. In theory individual errors for a record should be saved, but I haven’t tested it.</p>

<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-11-00-52.png" alt="The executed procedure will update the log table" /><figcaption>
      The executed procedure will update the log table

    </figcaption></figure>

<h1 id="filters">Filters</h1>

<h2 id="filtering-the-attachment-files">Filtering the attachment files</h2>
<p>Recommendations for the filter</p>
<ul>
  <li>ATT_WFDID<br />
Restrict to a specific workflow instance for testing</li>
  <li>ATT_Name <br />
If you generate documents, you can filter using the same pattern used for generating. Make sure that no custom documents are accidentally included.</li>
  <li>ATT_Description
In some processes, I’ve hidden the description in the UI, so that the users couldn’t provide a description and I set a value for all generated values.</li>
  <li>ATT_Attribute1
This stores the value of the <code class="language-plaintext highlighter-rouge">Category(folder)</code> if enabled.</li>
  <li>ATT_IsDeleted = 1
Should always be included to avoid removing versions of still existing files, at least if you don’t want to achieve just that. :)</li>
  <li>AT<em>F</em>_TsUpdated
In case you want to remove only versions which are older than two years.</li>
</ul>

<h2 id="get-all-attachments-of-form-types">Get all attachments of form types</h2>
<p>This script will return all attachments in a given process.</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">declare</span> <span class="o">@</span><span class="n">defId</span> <span class="nb">int</span> <span class="o">=</span> <span class="p">(</span><span class="k">select</span> <span class="n">DEF_ID</span> <span class="k">from</span> <span class="n">WFDefinitions</span>  <span class="k">where</span> <span class="n">DEF_Guid</span> <span class="o">=</span> <span class="s1">'de6a610b-ca59-4058-9907-53f6b3887431'</span><span class="p">)</span>

<span class="k">select</span> <span class="n">WFAttachmentFiles</span><span class="p">.</span><span class="n">ATF_ATTID</span><span class="p">,</span> <span class="n">WFAttachmentFiles</span><span class="p">.</span><span class="n">ATF_Version</span><span class="p">,</span> <span class="n">WFAttachmentFiles</span><span class="p">.</span><span class="n">ATF_OrginalName</span>
<span class="k">from</span> 
  <span class="n">WFAttachmentFiles</span> 
  <span class="k">join</span> <span class="n">WFDataAttachmets</span> <span class="k">on</span> <span class="n">ATF_ATTID</span> <span class="o">=</span> <span class="n">ATT_ID</span>   
  <span class="cm">/* The V_WFElements view would be another option, but the execution will be longer because of the numerous joins */</span>
  <span class="k">join</span> <span class="n">WFElements</span> <span class="k">on</span> <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="n">WFD_ID</span>
  <span class="k">join</span> <span class="n">WFDocTypes</span> <span class="k">on</span>  <span class="n">WFD_DTYPEID</span> <span class="o">=</span> <span class="n">DTYPE_ID</span>
<span class="k">where</span> 
 <span class="n">DTYPE_DEFID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span>

</code></pre></div></div>

<p>You can combine the filters on the attachment files table with the form type like this</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="p">....</span>
<span class="k">where</span>
   <span class="n">ATT_IsDeleted</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">and</span> <span class="n">datalength</span><span class="p">(</span><span class="n">ATF_Value</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">1</span>
   <span class="k">and</span> <span class="n">ATT_ID</span> <span class="k">in</span> <span class="p">(</span> 
    <span class="k">select</span> <span class="n">WFAttachmentFiles</span><span class="p">.</span><span class="n">ATF_ATTID</span>
    <span class="k">from</span> 
      <span class="n">WFAttachmentFiles</span> 
      <span class="k">join</span> <span class="n">WFDataAttachmets</span> <span class="k">on</span> <span class="n">ATF_ATTID</span> <span class="o">=</span> <span class="n">ATT_ID</span>   
      <span class="cm">/* The V_WFElements view would be another option, but the execution will be longer because of the numerous joins */</span>
      <span class="k">join</span> <span class="n">WFElements</span> <span class="k">on</span> <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="n">WFD_ID</span>
      <span class="k">join</span> <span class="n">WFDocTypes</span> <span class="k">on</span>  <span class="n">WFD_DTYPEID</span> <span class="o">=</span> <span class="n">DTYPE_ID</span>
    <span class="k">where</span> 
    <span class="n">DTYPE_DEFID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span>
   <span class="p">)</span>
</code></pre></div></div>

<h1 id="disk-space-of-deleted-attachments">Disk space of deleted attachments</h1>
<p>This SQL statement will list how much space is taken by the attachment versions. While this can be an indicator of whether you can free up some space. Make sure that the business is fine for deleting those versions.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">select</span> 
 <span class="p">(</span> <span class="k">select</span> <span class="k">sum</span><span class="p">(</span><span class="n">datalength</span><span class="p">(</span><span class="n">ATF_Value</span><span class="p">)</span><span class="o">/</span><span class="mi">1024</span><span class="o">/</span><span class="mi">1024</span><span class="p">)</span> <span class="p">[</span><span class="n">Total</span> <span class="n">MB</span><span class="p">]</span>
  <span class="k">from</span> <span class="n">dbo</span><span class="p">.</span><span class="n">WFAttachmentFiles</span> <span class="k">join</span> <span class="n">WFDataAttachmets</span> <span class="k">on</span>  <span class="n">ATF_ATTID</span> <span class="o">=</span> <span class="n">ATT_ID</span>
  <span class="k">where</span> <span class="n">ATF_IsDeleted</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">and</span> <span class="n">ATT_IsDeleted</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">[</span><span class="n">Total</span> <span class="n">MB</span> <span class="k">of</span> <span class="n">deleted</span> <span class="n">attachments</span> <span class="n">versions</span><span class="p">,</span> <span class="n">which</span> <span class="n">files</span> <span class="n">have</span> <span class="k">not</span> <span class="n">been</span> <span class="n">deleted</span><span class="p">]</span> <span class="p">,</span>
  <span class="p">(</span> <span class="k">select</span> <span class="k">sum</span><span class="p">(</span><span class="n">datalength</span><span class="p">(</span><span class="n">ATF_Value</span><span class="p">)</span><span class="o">/</span><span class="mi">1024</span><span class="o">/</span><span class="mi">1024</span><span class="p">)</span> <span class="p">[</span><span class="n">Total</span> <span class="n">MB</span><span class="p">]</span>
  <span class="k">from</span> <span class="n">dbo</span><span class="p">.</span><span class="n">WFAttachmentFiles</span> <span class="k">join</span> <span class="n">WFDataAttachmets</span> <span class="k">on</span>  <span class="n">ATF_ATTID</span> <span class="o">=</span> <span class="n">ATT_ID</span>
  <span class="k">where</span> <span class="n">ATF_IsDeleted</span> <span class="o">=</span> <span class="mi">1</span> <span class="k">and</span> <span class="n">ATT_IsDeleted</span> <span class="o">=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">[</span><span class="n">Total</span> <span class="n">MB</span> <span class="k">of</span> <span class="n">deleted</span> <span class="n">attachments</span> <span class="n">versions</span><span class="p">,</span> <span class="n">which</span> <span class="n">files</span> <span class="n">have</span> <span class="n">been</span> <span class="n">deleted</span><span class="p">]</span> <span class="p">,</span>
  <span class="p">(</span> <span class="k">select</span> <span class="k">sum</span><span class="p">(</span><span class="n">datalength</span><span class="p">(</span><span class="n">ATF_Value</span><span class="p">)</span><span class="o">/</span><span class="mi">1024</span><span class="o">/</span><span class="mi">1024</span><span class="p">)</span> <span class="p">[</span><span class="n">Total</span> <span class="n">MB</span><span class="p">]</span>
  <span class="k">from</span> <span class="n">dbo</span><span class="p">.</span><span class="n">WFAttachmentFiles</span> <span class="k">join</span> <span class="n">WFDataAttachmets</span> <span class="k">on</span>  <span class="n">ATF_ATTID</span> <span class="o">=</span> <span class="n">ATT_ID</span>
  <span class="k">where</span> <span class="n">ATF_IsDeleted</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">[</span><span class="n">Total</span> <span class="n">MB</span> <span class="k">existing</span> <span class="n">attachments</span> <span class="n">versions</span><span class="p">]</span>

</code></pre></div></div>
<figure class=""><img src="/assets/images/posts/2026-05-16-remove-content-of-deleted-attachments/2026-05-16-11-30-23.png" alt="Comparision of the allocated disk space." /><figcaption>
      Comparision of the allocated disk space.

    </figcaption></figure>

<h1 id="download">Download</h1>

<p>You can find the files <a href="https://github.com/Daniel-Krueger/webcon_snippets/tree/main/removeContentOfDeletedAttachments">here</a>.</p>]]></content><author><name>Daniel Krüger</name></author><category term="WEBCON BPS" /><category term="Governance" /><summary type="html"><![CDATA[Depending on the way an attachment has been deleted in WEBCON BPS, its binary content can stay in the database indefinitely. These scripts let you reclaim that space safely.]]></summary></entry><entry><title type="html">How to hange a puzzle to the wall</title><link href="https://daniels-notes.de/posts/2026/how-to-hange-a-puzzle-to-the-wall" rel="alternate" type="text/html" title="How to hange a puzzle to the wall" /><published>2026-05-10T00:00:00+00:00</published><updated>2026-05-10T00:00:00+00:00</updated><id>https://daniels-notes.de/posts/2026/How-to-hange-a-puzzle-to-the-wall</id><content type="html" xml:base="https://daniels-notes.de/posts/2026/how-to-hange-a-puzzle-to-the-wall"><![CDATA[<h1 id="overview">Overview</h1>
<p>This is a step-by-step guide on my practiced approach on how to glue a puzzle and hang it on the wall. While it’s more for my personal guide for any future puzzles, there may be others who are interested in it. It’s based on this German YouTube video <a href="https://www.youtube.com/watch?v=TfHqVkCEbfA"> DIY - Puzzle XL auf Holzplatte kleben und Rahmen selber bauen | einfach und unkompliziert | Wie man </a>. Since I’m completely unskilled when it comes to physical work I opted for a solution without a frame.</p>

<h1 id="material">Material</h1>
<p>These are the materials I used.</p>
<ul>
  <li>Latex binder <a href="https://baufan.com/de/produkte/grundieren-und-isolieren/latex-bindemittel/750-ml/">Baufan Latex-Bindemittel</a><br />
This will glue the pieces together.</li>
  <li>Press roller <br />
I’ve used such a roller for glueing the pieces.
<img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-10-58-11.png" alt="" /></li>
  <li>Wood <a href="https://www.obi.de/p/6130579/mdf-analog-e1-ungeschliffen-280-cm-x-207-cm-x-0-5-cm-braun">MDF Analog E1 Ungeschliffen </a><br />
The wood to which the puzzle will be glued. It’s 0.5 cm thick. While it’s tempting to buy it before starting the puzzle, the size on the puzzle box is an estimation. I’ve measured the final puzzle.</li>
  <li>
    <p>Wolfcraft pilot drill with countersink and depth stop <a href="https://shop.wolfcraft.com/de-de/products/p_2544_vorbohrermitsenkerundtiefenstopp">Wolfcraft Vorbohrer mit Senker und Tiefenstopp</a> <br />
Will be used to prepare the wood, so that the screws will sit flush to the wood surface. <br />
<img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-10-51-35.png" alt="" /></p>
  </li>
  <li>Wood glue <a href="https://www.ponal.de/products/central-pdp.html/ponal-classic/SAP_0201FTP14EI1/variation/395725.html">Ponal Classic</a><br />
I used the undiluted glue.</li>
  <li>Smoothing squeegee <br />
I’ve used this to spread the glue on the wood.
<img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-10-55-56.png" alt="" /></li>
  <li>Find something which you can use to press the puzzle onto the wood</li>
</ul>

<h1 id="preparation">Preparation</h1>
<h2 id="moving-the-puzzle-onto-the-wood">Moving the puzzle onto the wood</h2>
<p>At this point the puzzle is already finished, and the wood has been purchased. The next home improvement store is OBI, which also offered to cut the wood in the required dimensions. The first step was to move the completed puzzle onto the wood as I wanted to put some protection on the surface before starting with the glueing.</p>

<p>The best approach was to:</p>
<ul>
  <li>Tear down the puzzle box</li>
  <li>Place the wood on a flat angle near the puzzle</li>
  <li>Place the flat puzzle box on the edge of the wood</li>
  <li>Slide it under the puzzle for about 10 cm, this reduced the angle even further</li>
  <li><em>Pull</em> the puzzle onto the wood, depending on the size, you will need a second person</li>
</ul>

<h2 id="drilling-the-sinks-for-the-screws">Drilling the sinks for the screws</h2>
<p>I’ve opted to screw the wood with six screws to the wall. This was fine for a 6000 pieces puzzle and will surely be fine for the 3000 piece puzzle in these pictures.</p>

<p>For each screw I removed four puzzle pieces which are 3 pieces away from the border. I will use the term ‘screw puzzles’ for those. The middle has been marked for drilling. This is way easier than measuring the correct location.</p>

<p>Afterwards I protected the table, slid down the puzzle from the wood and drilled the wood.</p>

<p>I don’t have a picture from this step, but this is how it looks at the end.</p>
<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-28-30.png" alt="The drilled sink after glueing the puzzle to the wood." /><figcaption>
      The drilled sink after glueing the puzzle to the wood.

    </figcaption></figure>

<h1 id="glueing-the-pieces">Glueing the pieces</h1>
<p>Maybe I’m a bit paranoid but this was a three-phase process. I used the press roller to press the Latex into the connections of the puzzle pieces. 
I’ve done this horizontally, vertically and diagonally. While the Latex looks white, it will fade to transparent over time.</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-22-06.png" alt="The white Latex will fade over time" /><figcaption>
      The white Latex will fade over time

    </figcaption></figure>

<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-26-10.png" alt="Closeup of a part where the Latex hasn&#39;t been applied." /><figcaption>
      Closeup of a part where the Latex hasn’t been applied.

    </figcaption></figure>

<p>In case of the screw puzzles, I’ve done this on the front and back.</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-26-39.png" alt="Glueing the screw puzzles on the front." /><figcaption>
      Glueing the screw puzzles on the front.

    </figcaption></figure>

<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-26-52.png" alt="Glueing the screw puzzles on the front." /><figcaption>
      Glueing the screw puzzles on the front.

    </figcaption></figure>

<p>I waited approximately an hour until the Latex had dried. Afterwards the puzzle will already be a single piece and can cautiously be moved. The image below shows the lifted puzzle, where the black is a shadow.</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-38-48.png" alt="The black part is the shadow of the lifted puzzle." /><figcaption>
      The black part is the shadow of the lifted puzzle.

    </figcaption></figure>

<p>Then I moved the puzzle onto the floor to reuse the cover.</p>
<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-46-44.png" alt="The dried Latex is no longer visible." /><figcaption>
      The dried Latex is no longer visible.

    </figcaption></figure>

<h1 id="glueing-the-puzzle-onto-the-wood">Glueing the puzzle onto the wood</h1>
<p>When I glued my first puzzle I made two mistakes:</p>
<ul>
  <li>I use a tube of glue</li>
  <li>It was a hot day</li>
</ul>

<p>Due to the dimensions of 1.2 m x 1.8 m applying the glue took way too long and it seemed to dry in some places.</p>

<p>Lessons learned:</p>
<ul>
  <li>Use a bucket of glue</li>
  <li>Pure out some glue and spread it on the wood</li>
</ul>

<p>I placed the wood on the floor, for which I used a part of the cover from before.</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-32-05.png" alt="Spreading the glue on the wood" /><figcaption>
      Spreading the glue on the wood

    </figcaption></figure>

<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-36-57.png" alt="Final result of the spread glue." /><figcaption>
      Final result of the spread glue.

    </figcaption></figure>

<p>Once every part, especially the border, was covered, I placed the puzzle onto it. The next step was to press the puzzle pieces to the wood. I used the leaves of my gaming table to distribute the pressure of games I moved onto those. In the picture you will only see the table leaf and games, the puzzle is between the floor and the leaf. I also protected the table leaves by using another part of the cover to place between the puzzle and the leaves.</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-49-32.png" alt="Pressing the puzzle pieces to the wood" /><figcaption>
      Pressing the puzzle pieces to the wood

    </figcaption></figure>

<p>I haven’t tested it how long it takes, but in my case, I did this in the evening and went to bed. In the morning everything the puzzle had successfully bound to the wood.</p>

<h1 id="result">Result</h1>
<p>Here are two pictures of the result, before screwing the puzzle to the wall.</p>
<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-55-20.png" alt="The puzzle with the prepared sinks" /><figcaption>
      The puzzle with the prepared sinks

    </figcaption></figure>

<figure class=""><img src="/assets/images/posts/2026-05-10-How-to-hange-a-puzzle-to-the-wall/2026-05-10-11-56-24.png" alt="The top right screw puzzle has been put back into the puzzle" /><figcaption>
      The top right screw puzzle has been put back into the puzzle

    </figcaption></figure>

<p>Even so I placed the screw puzzle tightly into the puzzle, I was able to extract it with a vacuum cleaner again. This way you can also take your puzzle with you, when you are moving. :)</p>]]></content><author><name>Daniel Krüger</name></author><category term="Private" /><summary type="html"><![CDATA[A step by step guide on how to glue a puzzle on wood with material links.]]></summary></entry><entry><title type="html">Displaying a maintenance page during WEBCON BPS updates</title><link href="https://daniels-notes.de/posts/2026/maintenance-page" rel="alternate" type="text/html" title="Displaying a maintenance page during WEBCON BPS updates" /><published>2026-05-10T00:00:00+00:00</published><updated>2026-05-10T00:00:00+00:00</updated><id>https://daniels-notes.de/posts/2026/maintenance-page</id><content type="html" xml:base="https://daniels-notes.de/posts/2026/maintenance-page"><![CDATA[<h1 id="overview">Overview</h1>

<p>Whenever I update a WEBCON BPS environment, users who try to open the BPS Portal run into a generic IIS error. That’s not a great experience. A dedicated maintenance page is a simple way to communicate what is happening, when the system will be back, and who to contact for urgent matters, without touching BPS at all.</p>

<p><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-20-20-24.png" alt="Display a maintenance page during a WEBCON update." /></p>

<h1 id="implementation">Implementation</h1>
<h2 id="general">General</h2>

<p>The implementation consists of two parts:</p>

<ol>
  <li>A PowerShell setup script <br />
  This creates the IIS sites and copies the bindings from the existing WEBCONBPS site.</li>
  <li>A static HTML maintenance page <br />
  Served by a separate IIS site.</li>
</ol>

<p>The files</p>

<h2 id="iis-setup-script">IIS setup script</h2>

<p>The PowerShell script needs to run once on the web server as Administrator. It reads the HTTPS binding from the existing WEBCON BPS IIS site (certificate hash, store, host header, IP address), creates a new IIS site at the configured maintenance port, assigns the same certificate, and sets <code class="language-plaintext highlighter-rouge">index.html</code> as the default document. The script accepts a few parameters for customization:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Script name: setup-maintenance-site.ps1

Parameters (all optional):
  -SourceWebsiteName      IIS site to copy the HTTPS binding from  (default: "WEBCONBPS")
  -MaintenanceWebsiteName Name for the new IIS site                (default: "MaintenancePage")
  -MaintenancePort        Idle port for the maintenance site        (default: 4443)
  -MaintenancePath        Physical path for the HTML files          (default: C:\inetpub\MaintenancePage)
</code></pre></div></div>

<p>Run with defaults:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="n">\setup-maintenance-site.ps1</span><span class="w">
</span></code></pre></div></div>

<p>Or override individual settings:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">.</span><span class="n">\setup-maintenance-site.ps1</span><span class="w"> </span><span class="nt">-SourceWebsiteName</span><span class="w"> </span><span class="s2">"MyWEBCONSite"</span><span class="w"> </span><span class="nt">-MaintenancePort</span><span class="w"> </span><span class="nx">8443</span><span class="w">
</span></code></pre></div></div>

<p>Make sure to unblock the downloaded files from the properties dialog of the file.</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-17-38-02.png" alt="Unblock the downloaded files to prevent any issues." /><figcaption>
      Unblock the downloaded files to prevent any issues.

    </figcaption></figure>

<p>Otherwise you may run into an error when executing the PowerShell script.</p>
<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-17-38-21.png" alt="An error which is thrown, if you want to execute the blocked file." /><figcaption>
      An error which is thrown, if you want to execute the blocked file.

    </figcaption></figure>

<p>This is a result when running the script without any parameters:</p>
<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-18-42-22.png" alt="Script has been executed successfully." /><figcaption>
      Script has been executed successfully.

    </figcaption></figure>

<p>You can also verify the settings in the IIS.</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-17-48-59.png" alt="Maintenance page has been created." /><figcaption>
      Maintenance page has been created.

    </figcaption></figure>

<p class="notice--info"><strong>Tip:</strong> Set this up right after your initial BPS environment deployment so the maintenance site is ready before you ever need it.</p>

<p class="notice--info"><strong>Remark</strong> If you execute the script a second time, the script will remove any created elements and recreate them, except the created folder in the file system.</p>

<h2 id="maintenance-page-html">Maintenance page (HTML)</h2>

<p>After running the script, copy <code class="language-plaintext highlighter-rouge">index.html</code> into <code class="language-plaintext highlighter-rouge">MaintenancePath</code>. This isn’t done automatically as you have to modify the content anyway.</p>
<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-17-52-10.png" alt="Copy the index.html to the directory of the maintenance page" /><figcaption>
      Copy the index.html to the directory of the maintenance page

    </figcaption></figure>

<p>At the bottom of the file you can configure:</p>
<ol>
  <li>Supported languages</li>
  <li>Header<br />
  This value is required.</li>
  <li>A message for explaining the downtime<br />
  This value is required.</li>
  <li>Contact information<br />
  It can be left empty.</li>
  <li>A message for expected end of the downtime. <br />
  I decided against an explicit time so that you can be vague. It can be left empty.</li>
</ol>

<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-20-04-52.png" alt="The texts and languages of the maintenance page can be configured." /><figcaption>
      The texts and languages of the maintenance page can be configured.

    </figcaption></figure>

<p>While I said that some information is required, this only means that the page may look strange without these.</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-18-12-14.png" alt="How the page looks, if only the header is defined." /><figcaption>
      How the page looks, if only the header is defined.

    </figcaption></figure>

<h1 id="usage">Usage</h1>

<p>Activating the maintenance page is quite easy</p>

<ol>
  <li>Stop the main <code class="language-plaintext highlighter-rouge">WEBCONBPS</code> IIS website.</li>
  <li>Change the <code class="language-plaintext highlighter-rouge">WEBCONBPS</code> HTTPS binding from port <strong>443</strong> → <strong>444</strong>.</li>
  <li>Change the <code class="language-plaintext highlighter-rouge">MaintenancePage</code> HTTPS binding from port <strong>4443</strong> → <strong>443</strong>.</li>
  <li>Ensure that the <code class="language-plaintext highlighter-rouge">MaintenancePage</code> is running and is not stopped from the last update.</li>
  <li>Restart the main <code class="language-plaintext highlighter-rouge">WEBCONBP</code> IIS website.</li>
</ol>

<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-19-06-32.png" alt="Switch the ports of the IIS sites." /><figcaption>
      Switch the ports of the IIS sites.

    </figcaption></figure>

<p>Users navigating to the default URL will now see the maintenance page.</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-18-16-42.png" alt="Maintenance page is now displayed for the default URL " /><figcaption>
      Maintenance page is now displayed for the default URL

    </figcaption></figure>

<p>This will allow you to install any update, do other maintenance actions in the Portal (1) and also open the Designer Studio (2).</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-19-04-23.png" alt="Adding the port will allow you to still access the WEBCON BPS page via Browser (1) and Designer Studio (2)." /><figcaption>
      Adding the port will allow you to still access the WEBCON BPS page via Browser (1) and Designer Studio (2).

    </figcaption></figure>

<p>When you are done you can just revert the steps.</p>
<ol>
  <li>Stop the main <code class="language-plaintext highlighter-rouge">WEBCONBPS</code> IIS website.</li>
  <li>Change the <code class="language-plaintext highlighter-rouge">MaintenancePage</code> HTTPS binding from port <strong>443</strong> → <strong>4443</strong>.</li>
  <li>Change the <code class="language-plaintext highlighter-rouge">WEBCONBPS</code> HTTPS binding from port <strong>4443</strong> → <strong>443</strong>.</li>
  <li>Restart the main <code class="language-plaintext highlighter-rouge">WEBCONBP</code> IIS website.</li>
  <li>Optionally, stop the <code class="language-plaintext highlighter-rouge">MaintenancePage</code></li>
</ol>

<p>This is a short checklist:</p>
<ol>
  <li>Update the completion time in the <code class="language-plaintext highlighter-rouge">index.html</code>.</li>
  <li>Switch the ports, ensure both pages are running.</li>
  <li>Install the WEBCON BPS update.</li>
  <li>Do other maintenance.</li>
  <li>Switch the ports again.</li>
</ol>

<h1 id="remarks">Remarks</h1>
<h2 id="microsoft-entra-authentication">Microsoft Entra authentication</h2>

<p>If the WEBCON BPS environment uses Microsoft Entra ID (Azure AD) for authentication you will need to add a redirect URI with the alternative port (4443) <strong>before</strong> the maintenance window. Without it, you won’t be able to login.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://&lt;your-bps-hostname&gt;:&lt;MaintenancePort&gt;/signin-aad
</code></pre></div></div>

<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-18-30-55.png" alt="Adding the RedirectURI to the App Registration in Microsoft Entra" /><figcaption>
      Adding the RedirectURI to the App Registration in Microsoft Entra

    </figcaption></figure>

<h2 id="infrastructure-considerations">Infrastructure considerations</h2>
<p>Depending on your network setup, additional changes may be required alongside the IIS binding swap:</p>

<ul>
  <li>Firewalls / NSGs <br />
The maintenance port (default <code class="language-plaintext highlighter-rouge">4443</code>) must be reachable from the internet if you want to access WEBCON BPS. The maintenance page would already be accessible after switching to port 443.<br />
The setup script will add a rule to allow inbound traffic 
<img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-18-46-40.png" alt="" /></li>
  <li>Reverse proxies / load balancers <br />
If BPS is fronted by a proxy, the port swap needs to happen at the right layer. Depending on your setup you may need to adjust the proxy upstream instead of (or in addition to) the IIS binding.</li>
  <li>SSL certificate <br />
We are only changing the port, we can simply reuse the same SSL certificate.</li>
</ul>

<h2 id="maintenance-banner">Maintenance banner</h2>
<p>Before entering the maintenance window you can show users an advance notification. There are two options in the WEBCON community:</p>
<ul>
  <li><a href="https://community.webcon.com/forum/thread/6817?messageid=6817">Maintenance banner</a></li>
  <li><a href="https://community.webcon.com/forum/thread/5925?messageid=5925">Schedule maintenance with notification on homepage</a></li>
</ul>

<p>This is an updated version for 2026 which can be added to the CSS:</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">.top-menu__content--center-node</span><span class="nd">:before</span> <span class="p">{</span>
<span class="nl">content</span><span class="p">:</span> <span class="s1">"Planned maintenance 2026-05-14"</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--colorStatusDangerStroke2</span><span class="p">);</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--fontSize36</span><span class="p">);</span>
<span class="p">}</span> 
<span class="nc">.top-menu__content--center-node</span> <span class="p">{</span>
    <span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Every user should take notice of it:</p>
<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-19-45-53.png" alt="Displaying maintenance information with CSS." /><figcaption>
      Displaying maintenance information with CSS.

    </figcaption></figure>

<p>Especially if they don’t use the browser in full screen mode.</p>
<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-19-45-47.png" alt="It&#39;s really noticeable in a smaller browser window." /><figcaption>
      It’s really noticeable in a smaller browser window.

    </figcaption></figure>

<h2 id="verify-the-maintenance-information">Verify the maintenance information</h2>
<p>Of course, you can update the maintenance information and verify that maintenance page is correctly served when you use the maintenance port.</p>

<figure class=""><img src="/assets/images/posts/2026-05-10-maintenance-page/2026-05-10-19-02-24.png" alt="Ensure the correct display of the maintenance page before entering the maintenance windows using the port" /><figcaption>
      Ensure the correct display of the maintenance page before entering the maintenance windows using the port

    </figcaption></figure>

<h1 id="download">Download</h1>

<p>You can find the files <a href="https://github.com/Daniel-Krueger/webcon_snippets/tree/main/maintenancePage">here</a>.</p>]]></content><author><name>Daniel Krüger</name></author><category term="WEBCON BPS" /><category term="Installation" /><category term="Governance" /><category term="User Experience" /><summary type="html"><![CDATA[Serve a branded, multilingual maintenance page to BPS Portal users during planned downtime so they know when the system will be back and whom to contact.]]></summary></entry><entry><title type="html">Local build of the blog on a new machine</title><link href="https://daniels-notes.de/posts/2026/setup-blog-on-new-machine" rel="alternate" type="text/html" title="Local build of the blog on a new machine" /><published>2026-05-09T00:00:00+00:00</published><updated>2026-05-09T00:00:00+00:00</updated><id>https://daniels-notes.de/posts/2026/Setup-blog-on.new-machine</id><content type="html" xml:base="https://daniels-notes.de/posts/2026/setup-blog-on-new-machine"><![CDATA[<h1 id="overview">Overview</h1>
<p>I should have been aware that building my blog on a new machine with the latest components would work. Since I don’t want to spend numerous hours the next time again, I’m creating this guide.</p>

<h1 id="original-approach">Original approach</h1>
<h2 id="install-ruby">Install ruby</h2>
<p>From a command line search for the available Ruby versions</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>winget search Ruby
</code></pre></div></div>
<p>Install the latest version with RubyWithDevKit</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>winget install RubyInstallerTeam.RubyWithDevKit.4.0
</code></pre></div></div>
<h2 id="install-jekyll-and-bundler">Install jekyll and bundler</h2>
<p>After the installation a new command line session needs to be started. Ruby will update the PATH variable.
Install jekyll and bundler from the blog root folder.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>gem install jekyll bundler
</code></pre></div></div>
<h2 id="update-bundler-verion">Update bundler verion</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle update --bundler 
</code></pre></div></div>
<h2 id="install-gems">Install gems</h2>
<p>Trigger the installation of all listed gems from the blog root folder.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bundle install
</code></pre></div></div>

<h2 id="result">Result</h2>
<p>Happy debugging in finding all issues which are caused due to old gem versions which are not compatible with the latest Ruby/jekyll/bundler combination. One of the symptoms I’ve run into:</p>
<ul>
  <li>Gem is not compatible with a version</li>
  <li>Gem building fails</li>
  <li>According to internet searches it’s fixed in the latest version</li>
  <li>Silently an older gem version was installed which was installed because a nested gem isn’t compatible with other components</li>
</ul>

<h1 id="future-approach">Future approach</h1>
<h2 id="installation">Installation</h2>
<ol>
  <li>Install the latest versions 
  Same approach as before but an improved process to find out all issues.</li>
  <li>Define latest github-pages gem
  Search for the latest version
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  gem search github-pages
</code></pre></div>    </div>
    <p>and force the latest version in the gemfile</p>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  gem "github-pages", "232"
</code></pre></div>    </div>
  </li>
  <li>Execute <code class="language-plaintext highlighter-rouge">bundle install</code>
  This may output a version conflict with the installed components. I don’t have any idea how to check the supported Ruby, jekyll version without installing a version before.
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> Because github-pages &gt;= 228, &lt; 232 depends on jekyll-commonmark-ghpages = 0.4.0
 and github-pages &gt;= 232 depends on jekyll-commonmark-ghpages = 0.5.1,
 github-pages &gt;= 228 requires jekyll-commonmark-ghpages = 0.4.0 OR = 0.5.1.
 And because jekyll-commonmark-ghpages &gt;= 0.2.0 depends on jekyll-commonmark ~&gt;
 1.4.0,
 github-pages &gt;= 228 requires jekyll-commonmark ~&gt; 1.4.0.
 And because jekyll-commonmark &gt;= 1.4.0 depends on commonmarker ~&gt; 0.22
 and commonmarker &gt;= 0.22.0, &lt; 1.0.0.pre depends on Ruby &gt;= 2.6, &lt; 4.0,
 github-pages &gt;= 228 requires Ruby &gt;= 2.6, &lt; 4.0.
 So, because Gemfile depends on github-pages &gt;= 230
 and current Ruby version is = 4.0.3,
 version solving has failed.
</code></pre></div>    </div>
  </li>
  <li>Install the allowed version of Ruby</li>
  <li>Execute <code class="language-plaintext highlighter-rouge">bundle install</code> again</li>
  <li>Execute <code class="language-plaintext highlighter-rouge">run_server.bat</code>file and take a look at the warnings regarding missing gems. There may be gems which are no longer installed by default.
` warning: fiddle/import is found in fiddle, which will no longer be part of the default gems starting from Ruby 4.0.0. `
    <h2 id="minimal-mistake-migration">Minimal mistake migration</h2>
    <p>In case there’s a new version of minimal mistakes:</p>
  </li>
  <li>Review the <a href="https://mmistakes.github.io/minimal-mistakes/docs/quick-start-guide/">Quick-Start Guide</a> for new gem files to reference</li>
  <li>Update the remote theme version</li>
  <li>Execute <code class="language-plaintext highlighter-rouge">run_serve_trace</code> as this does not use the <code class="language-plaintext highlighter-rouge">incremental</code> flag which causes problems when updating files in the next step</li>
  <li>The files in the <code class="language-plaintext highlighter-rouge">_includes</code> and <code class="language-plaintext highlighter-rouge">_layouts</code> should be compared and updated accordingly. The <code class="language-plaintext highlighter-rouge">custom.html</code> can be ignored, it contains only custom HTML.</li>
</ol>]]></content><author><name>Daniel Krüger</name></author><category term="Private" /><summary type="html"><![CDATA[Notes for my future self on how to setup/update the blog/gems on a new windows machine.]]></summary></entry><entry><title type="html">Display actions next to an item list</title><link href="https://daniels-notes.de/posts/2026/item-list-actions" rel="alternate" type="text/html" title="Display actions next to an item list" /><published>2026-03-22T00:00:00+00:00</published><updated>2026-03-22T00:00:00+00:00</updated><id>https://daniels-notes.de/posts/2026/Item-list-Actions</id><content type="html" xml:base="https://daniels-notes.de/posts/2026/item-list-actions"><![CDATA[<h1 id="overview">Overview</h1>
<p>I often have an item list, and the users want to do something with it. For example, it’s used to plan tasks and for some of those subworkflows should be spawned. This typically requires either a <a href="https://docs.webcon.com/docs/2026R1/Studio/Workflow/Step/Actions/Action_Button">Menu button</a> or a path. Since neither one is located near the item list, the user needs to be aware of it. Wouldn’t it be better to display those next to the item list?</p>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/QYS6NYqAcns?autoplay=1&amp;loop=1&amp;mute=1&amp;rel=0" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<p class="notice--info"><strong>Info:</strong> If you are confused by the strange button labels, I had to make sure that these characters don’t break anything. :)</p>

<p class="notice--warning"><strong>Remark:</strong> This won’t work in the multi-tenant SaaS environment due to the limited database access.</p>

<h1 id="implementation">Implementation</h1>
<h2 id="general">General</h2>
<p>This time there’s a slight deviation from the general approach.</p>

<p>We need:</p>
<ol>
  <li>A global business rule.</li>
  <li>A global form rule.</li>
  <li>A HTML field for the configuration</li>
  <li>A HTML field for the button generation</li>
</ol>

<p>Yes, we will need 1+x HTML fields, where x is the number of item lists for which buttons should be generated.</p>

<h2 id="global-business-rule">Global business rule</h2>

<p>You can <a href="#download">download</a> the business rule from the repository. When you are creating the business rule you need to use another data source with higher privileges than the <code class="language-plaintext highlighter-rouge">&lt;Current BPS database&gt;</code>. We need to read a table to which the bps_user doesn’t have access.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Rule name: ActionButtonData
Description: 
Will return the action (menu) button data of the current workflow instance. It's limited to the information which is needed for generating the HTML buttons.
This needs to be executed with higher privileges than the `&lt;Current BPS database&gt;`

</code></pre></div></div>

<figure class=""><img src="/assets/images/posts/2026-03-22-Item-list-actions/2026-03-15-19-37-23.png" alt="You need to change the `Rule data source` in the business rule." /><figcaption>
      You need to change the <code class="language-plaintext highlighter-rouge">Rule data source</code> in the business rule.

    </figcaption></figure>

<h2 id="global-form-rule">Global form rule</h2>

<p>You can <a href="#download">download</a> the JS for this form rule from the repository and paste it to the new form rule. Don’t forget to switch the type to <code class="language-plaintext highlighter-rouge">JavaScript mode</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Rule name: ItemListActions
Edit mode: JavaScript mode
Description: 
The recommended approach is to load this form rule in a configuration HTML field and trigger the button generation in another field
Configuration field example:
&lt;style id="dkrItemListActionCSSContainer"&gt;&lt;/style&gt;

&lt;script&gt;
InvokeRule(ThisFormRule_594});
    dkr.itemListActions.ActionButtonDefinitionString = 'ActionButtonDataBusinessRule';
    dkr.itemListActions.ItemListActionConfiguration = {
        "ItemListId": [
            {
                "menuActionId": ActionButtonVariable,
                "isVisible": true,
                "hideMenuAction": true
            }]}
        dkr.itemListActions.init();
&lt;/script&gt;

Button generation field:
&lt;script&gt;
dkr.itemListActions.execute(0,4,ItemListId);
&lt;/script&gt;

</code></pre></div></div>

<figure class=""><img src="/assets/images/posts/2026-03-22-Item-list-actions/2026-03-15-19-44-53.png" alt="The global form rule." /><figcaption>
      The global form rule.

    </figcaption></figure>

<h2 id="html-fields">HTML fields</h2>
<h3 id="why-are-there-multiple-fields">Why are there multiple fields?</h3>
<p>Before I explain the implementation let me explain why we at least two fields. I started working with a single field which is placed below the item list. This will ensure that there’s minimal impact when the logic for creating the buttons is executed.
In my test scenario, I have tabs with item lists, and I added one field for each item list below it.</p>

<figure class=""><img src="/assets/images/posts/2026-03-22-Item-list-actions/2026-03-15-19-52-03.png" alt="The button generation fields are placed below the item list." /><figcaption>
      The button generation fields are placed below the item list.

    </figcaption></figure>

<p>This was working fine, but hiding the menu buttons in the toolbar caused flickering. I prevented this by adding another HTML field which is displayed in the top panel.</p>

<figure class=""><img src="/assets/images/posts/2026-03-22-Item-list-actions/2026-03-15-19-54-10.png" alt="Adding a second field removed the flickering of the hidden menu buttons." /><figcaption>
      Adding a second field removed the flickering of the hidden menu buttons.

    </figcaption></figure>

<h3 id="configuration-field">Configuration field</h3>
<p>As mentioned, this HTML field should be placed in the top panel. This is the minimal content:</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;style </span><span class="na">id=</span><span class="s">"dkrItemListActionCSSContainer"</span><span class="nt">&gt;&lt;/style&gt;</span>

<span class="nt">&lt;script&gt;</span>
    <span class="nx">InvokeRule</span><span class="p">(</span><span class="err">#</span><span class="p">{</span><span class="nl">BRUX</span><span class="p">:</span><span class="mi">594</span><span class="p">:</span><span class="nx">ID</span><span class="p">}</span><span class="err">#</span><span class="p">);</span>
    <span class="nx">dkr</span><span class="p">.</span><span class="nx">itemListActions</span><span class="p">.</span><span class="nx">ActionButtonDefinitionString</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">#{BRD:586}#</span><span class="dl">'</span><span class="p">;</span>
    <span class="nx">dkr</span><span class="p">.</span><span class="nx">itemListActions</span><span class="p">.</span><span class="nx">ItemListActionConfiguration</span> <span class="o">=</span> <span class="p">{</span>
        <span class="dl">"</span><span class="s2">#{WFCON:812}#</span><span class="dl">"</span><span class="p">:</span> <span class="p">[</span>
            <span class="p">{</span>
                <span class="dl">"</span><span class="s2">menuActionId</span><span class="dl">"</span><span class="p">:</span> <span class="err">#</span><span class="p">{</span><span class="na">ACB</span><span class="p">:</span><span class="mi">23</span><span class="p">}</span><span class="err">#</span><span class="p">,</span>
                <span class="dl">"</span><span class="s2">isVisible</span><span class="dl">"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
                <span class="dl">"</span><span class="s2">hideMenuAction</span><span class="dl">"</span><span class="p">:</span> <span class="kc">true</span>
            <span class="p">}</span>
        <span class="p">]</span>
    <span class="p">}</span>
    <span class="nx">dkr</span><span class="p">.</span><span class="nx">itemListActions</span><span class="p">.</span><span class="nx">init</span><span class="p">();</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>
<figure class=""><img src="/assets/images/posts/2026-03-22-Item-list-actions/2026-03-15-20-07-38.png" alt="Referencing variables in the configuration." /><figcaption>
      Referencing variables in the configuration.

    </figcaption></figure>

<p>Here’s a short explanation of the content:</p>
<ul>
  <li>The <code class="language-plaintext highlighter-rouge">style</code> element will be used to hide the configured menu buttons.</li>
  <li>After invoking the global form rule, the relevant menu buttons are configured</li>
  <li>The initialization will hide the menu buttons and prepare the button generation.</li>
</ul>

<p>Don’t forget to mark the HTML field as visible in the field matrix.</p>

<p class="notice--warning"><strong>Remark:</strong> If your actions are only working in edit mode, you should activate a different HTML content in readonly mode. You could also define other actions for view mode.<br /></p>
<figure class=""><img src="/assets/images/posts/2026-03-22-Item-list-actions/2026-03-15-19-56-50.png" alt="" /><figcaption>
      

    </figcaption></figure>

<h3 id="button-generation-field">Button generation field</h3>
<p>This one is really simple, but it should be added below each item list. If you have multiple item list on one tab, you place one below the last item list, but this may cause flickering, when the buttons are added to the first item list. Creating multiple fields will ensure that they are generated after the item list is prepared. In case of tabs, the HTML field will ensure that the buttons are recreated when the user switches back to the item list. The only difference of the HTML content will be the passed item list id.</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script&gt;</span>
<span class="nx">dkr</span><span class="p">.</span><span class="nx">itemListActions</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="err">#</span><span class="p">{</span><span class="nl">WFCON</span><span class="p">:</span><span class="mi">812</span><span class="p">}</span><span class="err">#</span><span class="p">);</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>
<figure class=""><img src="/assets/images/posts/2026-03-22-Item-list-actions/2026-03-15-20-10-59.png" alt="Button generation field." /><figcaption>
      Button generation field.

    </figcaption></figure>

<p><strong>Remark:</strong> If your actions are only working in edit mode, you should activate a different HTML content in readonly mode. You could also define other actions for the view mode.</p>

<h2 id="usage">Usage</h2>
<p>Make sure that you don’t define a <code class="language-plaintext highlighter-rouge">Availability restriction</code> for a menu button.</p>
<figure class=""><img src="/assets/images/posts/2026-03-22-Item-list-actions/2026-03-15-20-15-07.png" alt="Don&#39;t use `Availability restrictions.`" /><figcaption>
      Don’t use <code class="language-plaintext highlighter-rouge">Availability restrictions.</code>

    </figcaption></figure>

<p>Otherwise, the generated button can’t execute it and will generate a silent error:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>installHook.js:1  [WARN] No action button with ID: 24 {message: 'Warning while executing rule: InvokeMenuAction'}
</code></pre></div></div>

<p>Besides this, there’s nothing to add. The user can simply click on the buttons. :)</p>

<h1 id="explanations">Explanations</h1>
<h2 id="using-menu-buttons">Using menu buttons</h2>
<p>This is the easiest way.</p>
<ul>
  <li>Configure the menu buttons as you are used to in the Designer Studio
    <ul>
      <li>Automation</li>
      <li>Icon</li>
    </ul>
  </li>
  <li>Configure the object in the configuration field
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
    "menuActionId": #{ACB:23}#,
    "isVisible": true,
    "hideMenuAction": true
}
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="custom-buttons">Custom buttons</h2>
<p>This was actually an unintended feature when I was working on minimizing the necessary configuration data for menu buttons.
These are the properties you need to define:</p>

<ul>
  <li>label <br />
If you need a multilingual label, you can use a <a href="/posts/2024/translations#business-rule-function-text">business rule</a>.</li>
  <li>icon <br />
You can use the <code class="language-plaintext highlighter-rouge">Icon</code> setting to search for an appropriate one. If you replace the spaces with underscores and use lower case, you have the icon name.</li>
  <li>isVisible <br /></li>
  <li>onClick <br />
This needs to be a function and will be executed when the button is clicked. You could also execute a form rule.</li>
</ul>

<p>Example:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
    <span class="dl">"</span><span class="s2">label</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Custom JS action</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">icon</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">arrow_turn_right_up</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">isVisible</span><span class="dl">"</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">onClick</span><span class="dl">"</span><span class="p">:</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span> <span class="nx">alert</span><span class="p">(</span><span class="dl">"</span><span class="s2">Custom action</span><span class="dl">"</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<figure class=""><img src="/assets/images/posts/2026-03-22-Item-list-actions/2026-03-15-20-31-30.png" alt="How to get the icon name." /><figcaption>
      How to get the icon name.

    </figcaption></figure>

<h2 id="nested-replace-functions-in-the-business-rule">Nested replace functions in the business rule</h2>
<p>I had to make sure that the data can successfully be transferred to the client and that JavaScript can process it. This required encoding special characters with their Unicode representation for the labels. I had my fair share of issues with these characters in labels when working in multilingual environments.</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">,</span> <span class="k">replace</span><span class="p">(</span><span class="k">replace</span><span class="p">(</span><span class="k">replace</span><span class="p">(</span><span class="k">replace</span><span class="p">(</span><span class="k">replace</span><span class="p">(</span><span class="k">replace</span><span class="p">(</span><span class="k">replace</span><span class="p">(</span><span class="k">replace</span><span class="p">(</span><span class="k">replace</span><span class="p">(</span><span class="n">DefaultName</span><span class="p">,</span> <span class="s1">'</span><span class="se">\'</span><span class="s1">, '</span><span class="err">\</span><span class="n">u005c</span><span class="s1">'), </span><span class="se">''''</span><span class="s1">, '</span><span class="err">\</span><span class="n">u0027</span><span class="s1">'), '</span><span class="nv">"', '</span><span class="se">\u</span><span class="nv">0022'), '/', '</span><span class="se">\u</span><span class="nv">002f'), '$', '</span><span class="se">\u</span><span class="nv">0024'), '{', '</span><span class="se">\u</span><span class="nv">007b'), '}', '</span><span class="se">\u</span><span class="nv">007d'), '[', '</span><span class="se">\u</span><span class="nv">005b'), ']', '</span><span class="se">\u</span><span class="nv">005d') as DefaultName
</span></code></pre></div></div>
<h2 id="style-element-in-configuration-field"><code class="language-plaintext highlighter-rouge">Style</code> element in configuration field</h2>
<p>This will be used to add the CSS for hiding the menu buttons. When the user leaves form, the element will be destroyed. This will ensure that it does only affect the current workflow instance.</p>

<h2 id="downgrading-for-2025">Downgrading for 2025</h2>
<p>If you are interested in this for version lower than 2026 R1, you will need to change the generated button html. At least the icon classes have changed in 2026 R1.
In addition, you need to ensure that the <code class="language-plaintext highlighter-rouge">icon</code> option is used. The <code class="language-plaintext highlighter-rouge">predefined</code> setting has been removed in 2026 R1.</p>

<figure class=""><img src="/assets/images/posts/2026-03-22-Item-list-actions/2026-03-15-20-22-11.png" alt="In 2026 R1 the `Predefined` option has been removed." /><figcaption>
      In 2026 R1 the <code class="language-plaintext highlighter-rouge">Predefined</code> option has been removed.

    </figcaption></figure>

<h1 id="download">Download</h1>
<p>You can find a version of the rules for WEBCON BPS 2026 R1 <a href="https://github.com/Daniel-Krueger/webcon_snippets/tree/main/itemListActions">here</a>.</p>]]></content><author><name>Daniel Krüger</name></author><category term="WEBCON BPS" /><category term="Private" /><category term="Item list" /><category term="Form rules" /><summary type="html"><![CDATA[If your user needs to interact with an item list with menu buttons, shouldn't these actions be displayed next to it?]]></summary></entry><entry><title type="html">Collapse item list groups</title><link href="https://daniels-notes.de/posts/2026/item-list-collapse-groups" rel="alternate" type="text/html" title="Collapse item list groups" /><published>2026-03-15T00:00:00+00:00</published><updated>2026-03-15T00:00:00+00:00</updated><id>https://daniels-notes.de/posts/2026/Item-list-Collapse-groups</id><content type="html" xml:base="https://daniels-notes.de/posts/2026/item-list-collapse-groups"><![CDATA[<h1 id="overview">Overview</h1>
<p>This is another little improvement for the item list. When we are activating the grouping for the item list WEBCON BPS will display the rows expanded (1). If you activated the summary for a column, you would get a summary for the group and the grand total (2).</p>

<figure class=""><img src="/assets/images/posts/2026-03-15-Collapse-item-list-groups/2026-03-14-07-57-36.png" alt="Standard rendering of item list with grouping enabled." /><figcaption>
      Standard rendering of item list with grouping enabled.

    </figcaption></figure>

<p>Depending on your use case, it would be sufficient to see the collapsed information and the grand total can be useless. For example, if you would display the quantity of different items.</p>

<figure class=""><img src="/assets/images/posts/2026-03-15-Collapse-item-list-groups/2026-03-14-08-04-49.png" alt="Customization options for item list with groups." /><figcaption>
      Customization options for item list with groups.

    </figcaption></figure>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/QYS6NYqAcns?autoplay=1&amp;loop=1&amp;mute=1&amp;rel=0" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<h1 id="implementation">Implementation</h1>
<h2 id="general">General</h2>
<p>I’m not sure how often I’ve written this already, but it’s the same approach as always. :)</p>

<p>We need:</p>
<ol>
  <li>A global form rule.</li>
  <li>A HTML field to pass some configuration</li>
  <li>Activate the grouping</li>
</ol>

<h2 id="global-form-rule">Global form rule</h2>

<p>You can <a href="#download">download</a> the JS for this form rule from the repository and paste it to the new form rule. Don’t forget to switch the type to <code class="language-plaintext highlighter-rouge">JavaScript mode</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Rule name: CollapseItemListGroups
Edit mode: JavaScript mode
Description: 
Provides the following features:
- option to collapse the groups of an item list by default
- Collapse/expand all groups
- Hide grand total summary row

Usage in an HTML field

&lt;script&gt;
InvokeRule({BRUX:1182:ID});
// ....execute = function (currentIteration, maxIterations, itemListId, collapseAllGroups, hideGrandTotalSummaryRow) {
// collapseAllGroups and hideGrandTotalSummaryRow are set to true by default.
// The WFCON variable is not in the correct format, to prevent any issues. 
dkr.collapseItemListsGroups.execute(0,4,{WFCON:425})
&lt;/script&gt;

</code></pre></div></div>

<figure class=""><img src="/assets/images/posts/2026-03-15-Collapse-item-list-groups/2026-03-14-08-11-47.png" alt="The global form rule." /><figcaption>
      The global form rule.

    </figcaption></figure>

<h2 id="html-field">HTML field</h2>
<p>This is also simple, we need to load the form rule within an HTML field, call the <code class="language-plaintext highlighter-rouge">execute</code>function and pass at least the item list id.</p>

<p>The HTML content:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script&gt;</span>
<span class="nx">InvokeRule</span><span class="p">(</span><span class="err">#</span><span class="p">{</span><span class="nl">BRUX</span><span class="p">:</span><span class="mi">585</span><span class="p">:</span><span class="nx">ID</span><span class="p">}</span><span class="err">#</span><span class="p">)</span>
<span class="c1">// currentIteration, maxIterations, itemListId, collapseAllGroups, hideGrandTotalSummaryRow) </span>
<span class="c1">//dkr.collapseItemListsGroups.execute(0,4,{WFCON:425},true,true)</span>
<span class="nx">dkr</span><span class="p">.</span><span class="nx">collapseItemListsGroups</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="err">#</span><span class="p">{</span><span class="nl">WFCON</span><span class="p">:</span><span class="mi">812</span><span class="p">}</span><span class="err">#</span><span class="p">);</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<figure class=""><img src="/assets/images/posts/2026-03-15-Collapse-item-list-groups/2026-03-14-08-19-54.png" alt="HTML field definition" /><figcaption>
      HTML field definition

    </figcaption></figure>

<p>Don’t forget to mark the HTML field as visible in the field matrix.</p>

<p>The HTML field should be placed below the Item list field. If you have an item list in a tab, you need to create multiple fields. The reason for this is, that switching to a new tab will destroy the fields in the previous one and create the new fields.</p>

<figure class=""><img src="/assets/images/posts/2026-03-15-Collapse-item-list-groups/2026-03-14-08-22-14.png" alt="In case of tabs, create multiple fields." /><figcaption>
      In case of tabs, create multiple fields.

    </figcaption></figure>

<h2 id="activate-grouping">Activate grouping</h2>
<p>For whatever reason, you have to activate the grouping in the form configuration of the step. I hope this will be redesigned when WEBCON has finished the transition to the web-based designer studio.</p>

<figure class=""><img src="/assets/images/posts/2026-03-15-Collapse-item-list-groups/2026-03-14-08-26-36.png" alt="Grouping needs to be activated for each step." /><figcaption>
      Grouping needs to be activated for each step.

    </figcaption></figure>

<h2 id="usage">Usage</h2>
<p>In this case the only ‘usable’ element is the <code class="language-plaintext highlighter-rouge">toggle all</code> icon in the top left corner.</p>

<figure class=""><img src="/assets/images/posts/2026-03-15-Collapse-item-list-groups/2026-03-14-08-33-39.png" alt="" /><figcaption>
      

    </figcaption></figure>

<h1 id="explanations">Explanations</h1>
<h2 id="parameters">Parameters</h2>

<p>The <code class="language-plaintext highlighter-rouge">execute</code> function will try a defined number of times to find the item list and execute the logic. It may take some time until it’s rendered. Placing the HTML field below the item list helps to reduce the number of executed tries</p>
<ul>
  <li>currentIteration
The current try number. This is passed as a parameter, so that the function can be called for different item lists.</li>
  <li>maxIterations
When the target element is not found after this number of tries, the script execution will stop.</li>
  <li>itemListId</li>
  <li>collapseAllGroups
The target initial state of the item list groups</li>
  <li>hideGrandTotalSummaryRow
Defines whether the grand total summary should be hidden.</li>
</ul>

<h2 id="when-do-i-use-configuration-variables-and-when-parameters">When do I use configuration variables and when parameters</h2>

<p>In my previous post <a href="/posts/2026/single-row-edit-add-new">Single row edit: Apply and add new</a> I used a property to configure multiple item lists:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script&gt;</span>
<span class="nx">InvokeRule</span><span class="p">(</span><span class="err">#</span><span class="p">{</span><span class="nl">BRUX</span><span class="p">:</span><span class="mi">1448</span><span class="p">:</span><span class="nx">ID</span><span class="p">}</span><span class="err">#</span><span class="p">);</span>

<span class="nx">dkr</span><span class="p">.</span><span class="nx">singleRowEditApplyAndAddNew</span><span class="p">.</span><span class="nx">itemLists</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">{</span><span class="na">listId</span><span class="p">:</span> <span class="err">#</span><span class="p">{</span><span class="na">WFCON</span><span class="p">:</span><span class="mi">1132</span><span class="p">}</span><span class="err">#</span><span class="p">,</span> <span class="na">columnId</span> <span class="p">:</span> <span class="err">#</span><span class="p">{</span><span class="na">DCN</span><span class="p">:</span><span class="mi">167</span><span class="p">}</span><span class="err">#</span> <span class="p">}</span>
  <span class="p">,</span> <span class="p">{</span><span class="na">listId</span><span class="p">:</span> <span class="err">#</span><span class="p">{</span><span class="na">WFCON</span><span class="p">:</span><span class="mi">1129</span><span class="p">}</span><span class="err">#</span><span class="p">,</span> <span class="na">columnId</span> <span class="p">:</span> <span class="err">#</span><span class="p">{</span><span class="na">DCN</span><span class="p">:</span><span class="mi">158</span><span class="p">}</span><span class="err">#</span> <span class="p">}</span>
<span class="p">];</span>

<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<p>In this post I’m using a different approach:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script&gt;</span>
<span class="nx">InvokeRule</span><span class="p">(</span><span class="err">#</span><span class="p">{</span><span class="nl">BRUX</span><span class="p">:</span><span class="mi">585</span><span class="p">:</span><span class="nx">ID</span><span class="p">}</span><span class="err">#</span><span class="p">)</span>
<span class="c1">// currentIteration, maxIterations, itemListId, collapseAllGroups, hideGrandTotalSummaryRow) </span>
<span class="c1">//dkr.collapseItemListsGroups.execute(0,4,#{WFCON:425}#,true,true)</span>
<span class="nx">dkr</span><span class="p">.</span><span class="nx">collapseItemListsGroups</span><span class="p">.</span><span class="nx">execute</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="err">#</span><span class="p">{</span><span class="nl">WFCON</span><span class="p">:</span><span class="mi">812</span><span class="p">}</span><span class="err">#</span><span class="p">);</span>
<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<p>The reason for this is the following. The first one doesn’t interact with an immediately available element. It needs to wait that something needs to happen. The second one should interact with the item list as soon as it is available. In addition, it needs to be executed again every time the item list is rendered again, for example if the user switches tabs.</p>

<h2 id="display-the-group-and-group-summary-in-one-row">Display the group and group summary in one row</h2>
<p>I tried to display the group summary in the same row as the group information, but I haven’t been able to achieve this in a way that it’s working reliable and in a technical way I liked. It would need support:</p>
<ul>
  <li>Initial rendering of the item list group</li>
  <li>Toggling of individual groups</li>
  <li>Toggling of all groups</li>
  <li>Working with item lists which have different numbers of columns
If someone comes up with a feasible solution, feel free to contact me.</li>
</ul>

<h1 id="download">Download</h1>
<p>You can find two different versions for WEBCON BPS 2025 and WEBCON BPS 2026 <a href="https://github.com/Daniel-Krueger/webcon_snippets/tree/main/itemListCollapseGroups">here</a>.</p>

<p>I’ve decided to split these as it turned out, that I don’t have the time to test them for different versions and make them compatible.</p>]]></content><author><name>Daniel Krüger</name></author><category term="WEBCON BPS" /><category term="Private" /><category term="Item list" /><category term="Form rules" /><summary type="html"><![CDATA[Adds the option to collapse all item list groups, set the initial state and hide the grand total summary]]></summary></entry><entry><title type="html">Upgrade WEBCON BPS 2023 R2 to 2026</title><link href="https://daniels-notes.de/posts/2026/upgrade-2023-to-2026" rel="alternate" type="text/html" title="Upgrade WEBCON BPS 2023 R2 to 2026" /><published>2026-02-08T00:00:00+00:00</published><updated>2026-02-08T00:00:00+00:00</updated><id>https://daniels-notes.de/posts/2026/Upgrade-2023-to-2026</id><content type="html" xml:base="https://daniels-notes.de/posts/2026/upgrade-2023-to-2026"><![CDATA[<h1 id="overview">Overview</h1>

<p>This post is an overview of all the topics we needed to take care of, when we upgrade an environment from 2023 R2 straight to 2026. Besides the points listed here, I’ve also checked these notes:</p>
<ul>
  <li><a href="/posts/2025/upgrade-2022-to-2025">Upgrade WEBCON BPS 2022 to 2025</a>.</li>
  <li><a href="/posts/2024/webcon-bps-2024-change-log">WEBCON BPS 2024 Change log excerpt and thoughts</a></li>
  <li><a href="/posts/2024/webcon-bps-2025-change-log">WEBCON BPS 2025 Change log excerpt and thoughts</a></li>
  <li><a href="/posts/2025/webcon-bps-2025-r2-changelog">WEBCON BPS 2025 R2 Change log excerpt and thoughts</a></li>
  <li><a href="/posts/2025/webcon-bps-2026-r1-changelog">WEBCON BPS 2026 R1 Change log excerpt and thoughts</a></li>
</ul>

<h1 id="new-licenses-starting-with-version-202614">New licenses starting with version 2026.1.4</h1>
<p>At the time of this writing the version 2026.1.4 hasn’t been released yet, but it’s still a must read for everyone.
https://community.webcon.com/posts/post/licenses-2026/598/3</p>

<h1 id="ui">UI</h1>
<h2 id="updating-reports-and-dashboards">Updating reports and dashboards</h2>
<p>In addition to the feature which 2025 offered we can now freely place the widgets on the dashboard in 2026. While you don’t need to, I still recommend taking use of the new options:</p>

<ul>
  <li>You can add folders in the navigation</li>
  <li>Add start buttons, dashboards and reports to the folders</li>
  <li>Add icons to the folders</li>
  <li>Define user privileges on the folder level</li>
  <li>Add filtering options on the dashboard, which will be applied to all elements.</li>
  <li>Place widgets freely on the dashboard <em>(new in 2026)</em>.</li>
</ul>

<p>For more details on these and other new features you can take a look at t <a href="https://webcon.com/wp-content/uploads/2024/11/WEBCON_BPS_2025_Release_Notes_EN-1.pdf">change log 2025</a> and <a href="https://webcon.com/wp-content/uploads/2025/11/WEBCON_2026_Release_Notes_ENG.pdf">White paper 2026</a>.</p>

<h2 id="new-admin-mode-icon">New Admin mode icon</h2>
<p>I really like this one:</p>

<p><img src="/assets/images/posts/2026-02-08-Upgrade-2023-to-2026/2026-02-08-22-18-38.png" alt="" /></p>

<h2 id="renamed-labels-parent-workflow-and-subworkflow-instances">Renamed labels <code class="language-plaintext highlighter-rouge">Parent workflow</code> and <code class="language-plaintext highlighter-rouge">Subworkflow instances</code></h2>
<p>At least the English translations of the parent and subworkflow instances elements in the info panel have been renamed.
<img src="/assets/images/posts/2026-02-08-Upgrade-2023-to-2026/2026-02-08-22-19-24.png" alt="" /></p>

<h2 id="css">CSS</h2>
<p>In  version 20261.1.2.91 I added a few CSS fixes. The CSS has been added to the themes, so that I don’t need to copy the themes and the global CSS when upgrading the production environment.</p>
<h3 id="ellipses--trailing-dots">Ellipses / trailing dots</h3>
<p>The used version rendered trailing dots, also they weren’t necessary.</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* remove ellipses at the end of an element */</span>

<span class="nc">.dashboard-tile__name</span> <span class="nc">.webcon-ui-tooltip-ellipsis--multiple</span>  <span class="p">{</span> <span class="nl">-webkit-line-clamp</span><span class="p">:</span><span class="m">2</span> <span class="cp">!important</span><span class="p">}</span>
<span class="nc">.start-tile__wrapper</span>  <span class="nc">.webcon-ui-tooltip-ellipsis--multiple</span>  <span class="p">{</span> <span class="nl">-webkit-line-clamp</span><span class="p">:</span><span class="m">3</span> <span class="cp">!important</span><span class="p">}</span>
<span class="nc">.dashboard-navigation-item__content-wrapper</span> <span class="nc">.webcon-ui-tooltip-ellipsis--multiple</span>  <span class="p">{</span> <span class="nl">-webkit-line-clamp</span><span class="p">:</span><span class="m">2</span> <span class="cp">!important</span><span class="p">}</span>
<span class="nc">.dashboard-navigation-item__action-wrapper</span> <span class="nc">.webcon-ui-tooltip-ellipsis--multiple</span>  <span class="p">{</span> <span class="nl">-webkit-line-clamp</span><span class="p">:</span><span class="m">2</span> <span class="cp">!important</span><span class="p">}</span>

</code></pre></div></div>
<p><img src="/assets/images/posts/2026-02-08-Upgrade-2023-to-2026/2026-02-08-22-12-01.png" alt="2026.1.2.91 renders unnecessary ellipses." /></p>

<h3 id="primary-button-class-not-applied-in-html-fields">.primary-button class not applied in HTML fields</h3>

<p>We had a HTML field which should be rendered similar to the standard (primary) button, but this wasn’t the case.</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* Copied css definition of a button-primary. The standard class has a lower precendece and doesn't apply to elemtns in an html-control */</span>
<span class="nc">.html-control</span> <span class="nt">button</span><span class="nc">.button--primary</span> <span class="p">{</span>
    <span class="nl">background-color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--colorBrandBackground1</span><span class="p">);</span>
    <span class="nl">border</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--strokeWidthThin</span><span class="p">)</span> <span class="nb">solid</span> <span class="nb">transparent</span><span class="p">;</span>
    <span class="nl">color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--colorNeutralForegroundOnBrand</span><span class="p">);</span>
    <span class="nl">transition-property</span><span class="p">:</span> <span class="n">background-color</span><span class="p">,</span><span class="n">color</span><span class="p">,</span><span class="n">border</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="top-alignment-of-indicators">Top alignment of indicators</h3>
<p>I don’t like the center alignment of the indicator fields in item lists.</p>

<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">/* Align indicators (red,yellow, green) in the center and top of an item list cell*/</span>
<span class="nc">.subelem-calculatedText--indicator</span>  <span class="p">{</span> <span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">top</span> <span class="cp">!important</span><span class="p">;}</span>
<span class="nc">.form-field-indicator</span> <span class="p">{</span><span class="nl">margin</span><span class="p">:</span> <span class="m">0</span> <span class="nb">auto</span><span class="p">;}</span>
</code></pre></div></div>

<p><img src="/assets/images/posts/2026-02-08-Upgrade-2023-to-2026/2026-02-08-22-16-18.png" alt="I prefer top aligned indicators" /></p>

<h3 id="ms-icon-replaced-with-fluent">ms-icon replaced with fluent</h3>
<p>If you used HTML elements with ms-icons, you will need to look for a replacement with the new fluent icons.</p>

<h1 id="increase-of-choose-fields-to-4000-characters">Increase of choose fields to 4000 characters</h1>
<p>Depending on your data the upgrade may take a while. If you have the option to do a test upgrade and want to take a note of the time the database upgrade took you can take a look at the table <code class="language-plaintext highlighter-rouge">AdminDBMigrationLogs</code> in the BPS_Config database.</p>

<h1 id="actions">Actions</h1>
<h2 id="review-attachment-actions">Review attachment actions</h2>
<p>We used attachment categories for some time and then got rid of them (1). Unfortunately, we didn’t update the actions. They still referenced the deleted attachments (2).</p>

<p><img src="/assets/images/posts/2026-02-08-Upgrade-2023-to-2026/2026-02-08-22-25-11.png" alt="" /></p>

<p>This was fine in 2023, but now caused errors like this:
<img src="/assets/images/posts/2026-02-08-Upgrade-2023-to-2026/2026-02-08-22-24-40.png" alt="Action references no longer existing attachment category." /></p>

<p>I used this SQL to get all actions which are not using the <code class="language-plaintext highlighter-rouge">Empty category</code> option.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">select</span> <span class="n">actionProcess</span><span class="p">.</span><span class="n">DEF_ID</span><span class="p">,</span> <span class="n">actionProcess</span><span class="p">.</span><span class="n">DEF_Name</span><span class="p">,</span> <span class="n">automationProcess</span><span class="p">.</span><span class="n">DEF_ID</span><span class="p">,</span> <span class="n">automationProcess</span><span class="p">.</span><span class="n">DEF_Name</span><span class="p">,</span> <span class="n">ACT_AUTMID</span><span class="p">,</span> <span class="n">AUTM_Name</span><span class="p">,</span> <span class="n">ACT_ID</span><span class="p">,</span> <span class="n">ACT_Name</span>
  <span class="k">from</span> <span class="n">WFActions</span> <span class="k">left</span> <span class="k">join</span> <span class="n">Automations</span> <span class="k">on</span> <span class="n">ACT_AUTMID</span> <span class="o">=</span> <span class="n">AUTM_ID</span> <span class="k">left</span> <span class="k">join</span> <span class="n">WFDefinitions</span> <span class="n">automationProcess</span> <span class="k">on</span> <span class="n">automationProcess</span><span class="p">.</span><span class="n">DEF_ID</span> <span class="o">=</span> <span class="n">AUTM_DEFID</span>
  <span class="k">left</span> <span class="k">join</span> <span class="n">WFDefinitions</span> <span class="n">actionProcess</span> <span class="k">on</span> <span class="n">actionProcess</span><span class="p">.</span><span class="n">DEF_ID</span> <span class="o">=</span> <span class="n">ACT_DEFID</span>
  <span class="cm">/* &lt;newFileCategory&gt;&amp;lt;EmptyCategoryKey&amp;gt;#Empty&lt;/newFileCategory&gt; is ok, but newFileCategory&gt;1#Template&lt;/newFileCategory&gt; needs to be verified */</span>
  <span class="k">where</span> <span class="n">ACT_Configuration</span> <span class="k">like</span> <span class="s1">'%newFileCategory&gt;%'</span> <span class="k">and</span> <span class="n">ACT_Configuration</span> <span class="k">not</span> <span class="k">like</span> <span class="s1">'%newFileCategory&gt;&amp;%'</span>

</code></pre></div></div>

<h2 id="changed-default-description-value-of-uploaded-attachments">Changed default description value of uploaded attachments</h2>
<p>I’m not sure whether this is related to 2026 but in 2023 attachments have been uploaded with an empty string value for description. In 2026 the default value is null.</p>

<p><img src="/assets/images/posts/2026-02-08-Upgrade-2023-to-2026/2026-02-08-22-28-31.png" alt="In 2023 default description value was an empty string and not null." /></p>

<p>This caused a problem for us because we have hidden the <code class="language-plaintext highlighter-rouge">Description</code> value in the UI and used it to identify generated documents. This failed in 2026</p>

<p>Previous version</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="k">select</span> <span class="n">ATT_ID</span><span class="p">,</span> <span class="n">ATT_IsDeleted</span><span class="p">,</span> <span class="n">Att_Name</span>
  <span class="k">from</span> <span class="n">WFDataAttachmets</span>
  <span class="k">where</span> <span class="n">ATT_IsDeleted</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="k">and</span> <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="s1">'{86}'</span>
  <span class="cm">/* the generated template has a description but no other document */</span>
  <span class="k">and</span> <span class="n">ATT_Description</span> <span class="o">=</span> <span class="s1">''</span>

</code></pre></div></div>

<p>New version makes use of isnull, so that it works with both variants.</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">select</span> <span class="n">ATT_ID</span><span class="p">,</span> <span class="n">ATT_IsDeleted</span><span class="p">,</span> <span class="n">Att_Name</span>
  <span class="k">from</span> <span class="n">WFDataAttachmets</span>
  <span class="k">where</span> <span class="n">ATT_IsDeleted</span> <span class="o">=</span> <span class="mi">0</span>
  <span class="k">and</span> <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="s1">'{86}'</span>
  <span class="cm">/* the generated template has a description but no other document */</span>
  <span class="k">and</span> <span class="k">isnull</span><span class="p">(</span><span class="n">ATT_Description</span><span class="p">,</span><span class="s1">''</span><span class="p">)</span> <span class="o">=</span> <span class="s1">''</span>
</code></pre></div></div>

<p>I used this SQL to identify all actions in which the ATT_Description is used.</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">select</span> <span class="n">actionProcess</span><span class="p">.</span><span class="n">DEF_ID</span><span class="p">,</span> <span class="n">actionProcess</span><span class="p">.</span><span class="n">DEF_Name</span><span class="p">,</span> <span class="n">automationProcess</span><span class="p">.</span><span class="n">DEF_ID</span><span class="p">,</span> <span class="n">automationProcess</span><span class="p">.</span><span class="n">DEF_Name</span><span class="p">,</span> <span class="n">ACT_AUTMID</span><span class="p">,</span> <span class="n">AUTM_Name</span><span class="p">,</span> <span class="n">ACT_ID</span><span class="p">,</span> <span class="n">ACT_Name</span>
  <span class="k">from</span> <span class="n">WFActions</span> <span class="k">left</span> <span class="k">join</span> <span class="n">Automations</span> <span class="k">on</span> <span class="n">ACT_AUTMID</span> <span class="o">=</span> <span class="n">AUTM_ID</span> <span class="k">left</span> <span class="k">join</span> <span class="n">WFDefinitions</span> <span class="n">automationProcess</span> <span class="k">on</span> <span class="n">automationProcess</span><span class="p">.</span><span class="n">DEF_ID</span> <span class="o">=</span> <span class="n">AUTM_DEFID</span>
  <span class="k">left</span> <span class="k">join</span> <span class="n">WFDefinitions</span> <span class="n">actionProcess</span> <span class="k">on</span> <span class="n">actionProcess</span><span class="p">.</span><span class="n">DEF_ID</span> <span class="o">=</span> <span class="n">ACT_DEFID</span>
  <span class="k">where</span> <span class="n">ACT_Configuration</span> <span class="k">like</span> <span class="s1">'%ATT_Description%'</span>

</code></pre></div></div>

<h1 id="sdk">SDK</h1>
<h2 id="migration">Migration</h2>
<p>Since WEBCON BPS 2024 any SDK needs to be rebuilt with the correct major release SDK.
I’ve blogged about it <a href="https://daniels-notes.de/posts/2024/webcon-bps-2024-change-log#sdks">here</a></p>

<h2 id="displayname-property-is-translated-in-sdks">DisplayName property is translated in SDKs</h2>
<p>Up to WEBCON BPS 2025 the DisplayName property of the field always returned the label /prompt of the field. Starting with 2026 this is now translated.</p>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">logger</span><span class="p">.</span><span class="nf">Log</span><span class="p">(</span><span class="s">$"Adding sql field'</span><span class="p">{</span><span class="n">sqlField</span><span class="p">.</span><span class="n">DisplayName</span><span class="p">}</span><span class="s">' as placeholder"</span><span class="p">);</span>
</code></pre></div></div>

<p>We have been using the DisplayName as a placeholder in a multilingual environment. Our workaround was to create dedicated “placeholder” fields which may not be translated.</p>]]></content><author><name>Daniel Krüger</name></author><category term="WEBCON BPS" /><category term="COSMO CONSULT" /><category term="Installation" /><summary type="html"><![CDATA[Installation]]></summary></entry><entry><title type="html">Single row edit: Apply and add new</title><link href="https://daniels-notes.de/posts/2026/single-row-edit-add-new" rel="alternate" type="text/html" title="Single row edit: Apply and add new" /><published>2026-02-01T00:00:00+00:00</published><updated>2026-02-01T00:00:00+00:00</updated><id>https://daniels-notes.de/posts/2026/Single-row-edit-add-new</id><content type="html" xml:base="https://daniels-notes.de/posts/2026/single-row-edit-add-new"><![CDATA[<h1 id="overview">Overview</h1>
<p>Adding the next/previous button to the single row editing modal has been one of my first <a href="https://community.webcon.com/forum/thread/123">user voices</a>. I added this, after implementing something like it for a customer. Almost five years have passed since then and I didn’t realize that something was still missing. Why do you need to close the dialog, if you want to add a new row? Wouldn’t it improve the user experience to just add a new row from the modal? I gave it a shot, and while it’s not optimal, I think it’s ok. :)</p>

<!-- Courtesy of embedresponsively.com -->

<div class="responsive-video-container">
    <iframe src="https://www.youtube-nocookie.com/embed/CXuDy0KZslk?autoplay=1&amp;loop=1&amp;mute=1&amp;rel=0" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
  </div>

<h1 id="implementation">Implementation</h1>
<h2 id="general">General</h2>
<p>I’m not sure how often I’ve written this already, but it’s the same approach as always. :)</p>

<p>We need:</p>
<ol>
  <li>A global form rule.</li>
  <li>An HTML field to pass some configuration</li>
</ol>

<h2 id="global-form-rule">Global form rule</h2>

<p>You can <a href="#download">download</a> the JS for this form rule from the repository and paste it to the new form rule. Don’t forget to switch the type to <code class="language-plaintext highlighter-rouge">JavaScript mode</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Rule name: SingleEditRowApplyAndNew
Edit mode: JavaScript mode
Description: 
After loading the form rule with InvokeRule in an HTML field this property needs to be set in the HTML field, but you should use the variables instead of fixed ids.
dkr.singleRowEditApplyAndAddNew.itemLists = [
    {listId: 1132, columnId : 167 }
  , {listId: 1129, columnId : 158 }

];

</code></pre></div></div>

<figure class=""><img src="/assets/images/posts/2025-10-03-Single-row-edit-add-new/2025-09-28-20-05-07.png" alt="The global form rule." /><figcaption>
      The global form rule.

    </figcaption></figure>

<h2 id="html-field">HTML field</h2>
<p>This is also simple, we need to  load the form rule within an HTML field and set the properties to define, for which item lists the logic should be applied.</p>

<ol>
  <li>Execute global form rule<br />
  The <code class="language-plaintext highlighter-rouge">InvokeRule</code> will load the global form rule and will monitor the form whether a modal dialog is displayed.</li>
  <li>Define the item lists<br />
  You will need to add on JS object for each item list and you need to pass the integer ids not the database names:<br />
  <code class="language-plaintext highlighter-rouge">{listId: #{WFCON:1132}#, columnId : #{DCN:167}# }</code></li>
</ol>
<figure class=""><img src="/assets/images/posts/2025-10-03-Single-row-edit-add-new/2025-09-28-20-12-40.png" alt="HTML field which configures the item lists to watch." /><figcaption>
      HTML field which configures the item lists to watch.

    </figcaption></figure>

<p>The HTML content of the example:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;script&gt;</span>
<span class="nx">InvokeRule</span><span class="p">(</span><span class="err">#</span><span class="p">{</span><span class="nl">BRUX</span><span class="p">:</span><span class="mi">1448</span><span class="p">:</span><span class="nx">ID</span><span class="p">}</span><span class="err">#</span><span class="p">);</span>

<span class="nx">dkr</span><span class="p">.</span><span class="nx">singleRowEditApplyAndAddNew</span><span class="p">.</span><span class="nx">itemLists</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">{</span><span class="na">listId</span><span class="p">:</span> <span class="err">#</span><span class="p">{</span><span class="na">WFCON</span><span class="p">:</span><span class="mi">1132</span><span class="p">}</span><span class="err">#</span><span class="p">,</span> <span class="na">columnId</span> <span class="p">:</span> <span class="err">#</span><span class="p">{</span><span class="na">DCN</span><span class="p">:</span><span class="mi">167</span><span class="p">}</span><span class="err">#</span> <span class="p">}</span>
  <span class="p">,</span> <span class="p">{</span><span class="na">listId</span><span class="p">:</span> <span class="err">#</span><span class="p">{</span><span class="na">WFCON</span><span class="p">:</span><span class="mi">1129</span><span class="p">}</span><span class="err">#</span><span class="p">,</span> <span class="na">columnId</span> <span class="p">:</span> <span class="err">#</span><span class="p">{</span><span class="na">DCN</span><span class="p">:</span><span class="mi">158</span><span class="p">}</span><span class="err">#</span> <span class="p">}</span>
<span class="p">];</span>

<span class="nt">&lt;/script&gt;</span>
</code></pre></div></div>

<p>Don’t forget to mark the HTML field as visible in the field matrix.</p>

<h2 id="usage">Usage</h2>
<p>Once everything is set up, any modal dialog will be checked, whether it is for an item list. This is done by testing whether the <code class="language-plaintext highlighter-rouge">Apply and next</code> button exists. If this is the case, a new button will be added. If the button already exists, it’s visibility will be modified as described below.</p>
<figure class=""><img src="/assets/images/posts/2025-10-03-Single-row-edit-add-new/2025-09-28-20-15-19.png" alt="`Apply and add new` button has been added." /><figcaption>
      <code class="language-plaintext highlighter-rouge">Apply and add new</code> button has been added.

    </figcaption></figure>

<p>The button will behave differently depending on the context:</p>
<ul>
  <li>If it’s not the last row, it will be disabled<br />
<img src="/assets/images/posts/2025-10-03-Single-row-edit-add-new/2025-09-28-20-18-08.png" alt="" /></li>
  <li>If it’s the last row it’s enabled<br />
<img src="/assets/images/posts/2025-10-03-Single-row-edit-add-new/2025-09-28-20-18-32.png" alt="" /></li>
  <li>It’s hidden if the <code class="language-plaintext highlighter-rouge">Add</code> row button is not displayed
<img src="/assets/images/posts/2025-10-03-Single-row-edit-add-new/2025-09-28-20-20-32.png" alt="" /></li>
</ul>

<h1 id="explanations">Explanations</h1>
<h2 id="how-it-works">How it works</h2>
<p>If you have watched the video closely, you will have noticed that the dialog closed and has been opened again. This is exactly what the script does:</p>
<ol>
  <li>Create a new row</li>
  <li>Click the <code class="language-plaintext highlighter-rouge">Apply</code> button</li>
  <li>Click the <code class="language-plaintext highlighter-rouge">Pen</code> (edit) button of the row</li>
</ol>

<p>This also means, if you have any logic which would hide the <code class="language-plaintext highlighter-rouge">Pen</code> for a new row, this wouldn’t work.
I’m not really satisfied with this solution, but this is the only way it is working fine. I’m not satisfied because the animation during the closing/opening is annoying.</p>

<p>I tested another solution before:</p>
<ol>
  <li>Create a new row</li>
  <li>Click the <code class="language-plaintext highlighter-rouge">Apply and next</code> button</li>
</ol>

<p>This was working but it had two issues:</p>
<ul>
  <li>The added row was referenced as row 0 in the header\<br />
<img src="/assets/images/posts/2025-10-03-Single-row-edit-add-new/2025-09-28-20-43-02.png" alt="" /></li>
  <li>The modal dialog wasn’t aware of the new row<br />
The <code class="language-plaintext highlighter-rouge">Apply and previous</code> and <code class="language-plaintext highlighter-rouge">Apply and next</code> did only navigate between the previously existing rows.</li>
</ul>

<h2 id="why-do-we-need-the-id-of-a-column">Why do we need the id of a column</h2>
<p>The modal dialog does not contain any information about the item list in which context it was opened. Ok, that’s not completely correct. It does contain the database names and id of the displayed fields. Since the database names are not sufficient to identify the displayed item list, we need the id of a column.</p>

<h2 id="visibility-of-the-new-button">Visibility of the new button</h2>
<p>The visibility of the new button is defined by the function <code class="language-plaintext highlighter-rouge">dkr.singleRowEditApplyAndAddNew.applyVisibility</code>.
If you want to have a different behavior, you need to modify this function. For example, if you always want to display the button, and not only when the last row is displayed, you could just uncomment / remove these lines:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">dkr</span><span class="p">.</span><span class="nx">singleRowEditApplyAndAddNew</span><span class="p">.</span><span class="nx">applyVisibility</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">currentItemListId</span><span class="p">,</span> <span class="nx">addApplyAndNewButton</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">const</span> <span class="nx">addRowAllowed</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s2">`div[id='SubElems_</span><span class="p">${</span><span class="nx">currentItemListId</span><span class="p">}</span><span class="s2">']  button.subelem-addRow`</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">addRowAllowed</span><span class="p">)</span> <span class="p">{</span>
        <span class="nx">addApplyAndNewButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">none</span><span class="dl">"</span><span class="p">;</span>

    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="nx">addApplyAndNewButton</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">inherit</span><span class="dl">"</span><span class="p">;</span>

        <span class="c1">// // Enable or disable the button based on whether it's the last row</span>
        <span class="c1">// const modalHeader = document.querySelector(".modal-window__header");</span>
        <span class="c1">// let isLastRow = modalHeader.innerText.endsWith(" " + SubelementCountRows(currentItemListId));</span>
        <span class="c1">// if (isLastRow) {</span>
        <span class="c1">//     addApplyAndNewButton.classList.remove("button--disabled");</span>
        <span class="c1">//     addApplyAndNewButton.removeAttribute('disabled');</span>

        <span class="c1">// }</span>
        <span class="c1">// else {</span>
        <span class="c1">//     addApplyAndNewButton.classList.add("button--disabled");</span>
        <span class="c1">//     addApplyAndNewButton.setAttribute('disabled', 'disabled');</span>

        <span class="c1">// }</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<h2 id="changing-the-label-of-the-button">Changing the label of the button</h2>
<p>You can change the label of the button in the global form rule by modifying the <code class="language-plaintext highlighter-rouge">applyAndNewButtonLabel</code> label</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">dkr</span><span class="p">.</span><span class="nx">singleRowEditApplyAndAddNew</span><span class="p">.</span><span class="nx">applyAndNewButtonLabel</span> <span class="o">=</span> <span class="p">{</span>
    <span class="dl">"</span><span class="s2">de</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Speichern und neu</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">en</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Apply and add new</span><span class="dl">"</span><span class="p">,</span>
    <span class="dl">"</span><span class="s2">pl</span><span class="dl">"</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Zastosuj i dodaj nowy</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="why-can-only-five-rows-be-added">Why can only five rows be added</h2>
<p>I added a form rule in the advanced configuration of the item list which hides the <code class="language-plaintext highlighter-rouge">Add row</code> button, when there are five rows.</p>
<figure class=""><img src="/assets/images/posts/2025-10-03-Single-row-edit-add-new/2025-09-28-20-40-58.png" alt="This logic hides the `Add row` button, when there are five rows" /><figcaption>
      This logic hides the <code class="language-plaintext highlighter-rouge">Add row</code> button, when there are five rows

    </figcaption></figure>

<h1 id="download">Download</h1>
<p>You can find two different versions for WEBCON BPS 2025 and WEBCON BPS 2026 <a href="https://github.com/Daniel-Krueger/webcon_snippets/tree/main/singleRowEditApplyAndAddNew">here</a>.</p>]]></content><author><name>Daniel Krüger</name></author><category term="WEBCON BPS" /><category term="Private" /><category term="Item list" /><category term="Form rules" /><summary type="html"><![CDATA[Adds an action to the single edit dialog to create a new row directly from the dialog.]]></summary></entry><entry><title type="html">WEBCON BPS 2026 R1 Change log excerpt and thoughts</title><link href="https://daniels-notes.de/posts/2025/webcon-bps-2026-r1-changelog" rel="alternate" type="text/html" title="WEBCON BPS 2026 R1 Change log excerpt and thoughts" /><published>2025-11-24T00:00:00+00:00</published><updated>2025-11-24T00:00:00+00:00</updated><id>https://daniels-notes.de/posts/2025/WEBCON-BPS-2026-R1-Changelog</id><content type="html" xml:base="https://daniels-notes.de/posts/2025/webcon-bps-2026-r1-changelog"><![CDATA[<h1 id="remark">Remark</h1>
<p>When I read through the change log I have copied over a few excerpts, these are displayed as quotes in this post. Anything not in quotes are my personal thoughts. The selection criteria for these excerpts from the change log are:</p>
<ul>
  <li>Will the changes have a direct impact on my work</li>
  <li>Do I have any thoughts about the change</li>
  <li>Is there a ‘hidden’ gem</li>
</ul>

<p>I won’t cover the new AI features or other new features listed in the <a href="https://webcon.com/wp-content/uploads/2025/11/WEBCON_2026_Release_Notes_ENG.pdf">whitepaper</a>. This is a must read anyway.</p>

<p>After reading the change log I would say that WEBCON focused on these areas in this order:</p>
<ol>
  <li>Obviously AI has been the main focus in this release, but I’m not hyped by AI. I need to see it in action for myself so I can put the prices into relation.</li>
  <li>The platform infrastructure has undergone another overhaul. I would guess that in the 2027 version all components will use .Net Core.</li>
  <li>Integrating WEBCON with other systems. There are numerous improvements in the areas of SDKs, User Defined Actions and Custom Controls.</li>
  <li>User experience improvements and UI enhancement, but I didn’t mention these.</li>
  <li>The <code class="language-plaintext highlighter-rouge">Related elements</code> will be an interesting feature</li>
</ol>

<h1 id="installation--upgrading-information">Installation / Upgrading information</h1>
<h2 id="requirements">Requirements</h2>
<ul>
  <li>Upgrading from versions older than 2019 R1 or from SharePoint classic is not possible</li>
  <li>Minimum SQL Server version is 2019</li>
  <li>For all-in-one Installations the required RAM did go up from 12 GB in 2025 R1 to 20 GB in 2026 R1</li>
  <li>ASP.Net Core 9 Hosting bundle is required</li>
</ul>

<p>SDKs need to be upgraded to .NEt9, but this check has already been introduced in 2025. <a href="https://community.webcon.com/community/public/uploads/editor/SDK_Migration_2026_1.pdf">SDK Migration 2026 R1</a></p>

<h2 id="things-to-check-before-upgrading">Things to check before upgrading</h2>
<p>Before you upgrade, check for any “End” blocks in your automation:</p>

<ul>
  <li>Fixed an issue where the End block in automations did not stop their execution. After the fix, the End block correctly terminates the automation without an error. To maintain backward compatibility, all existing instances of the End block were removed from automation definitions during the update. To take advantage of the corrected behavior, the End block must be readded in the automation editor.</li>
</ul>

<p>You cannot have SDK actions on dictionary or document template processes?</p>

<blockquote>
  <p>The new Service architecture has been adapted to work with SDK addons based on .NET 9 technology which are triggered on transition paths by Run an SDK action (this does not apply to add-ons triggered on transition paths in dictionary and document template processes [importing such processes is not possible]).</p>
</blockquote>

<p><code class="language-plaintext highlighter-rouge">Single line of text</code> fields are now using textareas instead of inputs. Everyone who added CSS /JS for some tweaks needs to check their implementation.</p>

<h2 id="during-the-upgrade">During the upgrade</h2>

<blockquote>
  <p>Improved the display of maintenance mode message during database updates. Previously, the Maintenance mode message wasn’t visible when updating WEBCON BPS, leading to HTTP 500.30 error.</p>
</blockquote>

<p>Upgrade from 2025 R2 to 2026 R1 will reindex the content database but not the attachments</p>

<h2 id="things-to-check-after-the-upgrade">Things to check after the upgrade</h2>
<p>It happened again, WEBCON changed the result of a function from textual <em>True/False</em>  to <em>1/0</em></p>
<blockquote>
  <p>Updated the context variable Is admin mode active? Previously, this variable returned text values (“True”/”False”). Now, it returns logic values (0 or 1). Consequently, to ensure that rules used in filters will work correctly, it will be necessary to use “TRUE”/”FALSE” or “0”/”1”. The previous use of “True”/”False” will no longer work.</p>
</blockquote>

<p>Parameter with defined types are now actually treated this way. This is something to look out for, as it may have an unintended side effect.</p>

<blockquote>
  <p>Improved handling of parameters passed from Form rules to Business rules. Values entered on the form are now interpreted according to the parameter 
type defined in the business rule. For example, text that looks like a date will no longer be automatically converted to a date if the parameter type is set to Text.</p>
</blockquote>

<p>MSSQL database connections timeout after 30 seconds, previously this had been 120 seconds. I hope no one did have long running statements.</p>

<blockquote>
  <p>Introduced access restrictions in the User Defined API (UDA) functionality. In the Get data from an element instance mode, it is now no longer possible to modify hidden form fields. In the Actions on a workflow instance mode, modifying both hidden and read-only fields has been blocked.</p>
</blockquote>

<p>If you are somehow monitoring the WEBCON windows processes you may need to take a different approach to check whether everything is working fine.</p>
<blockquote>
  <p>It now has a modular structure consisting of three separate processes, which means that in the event of a failure, the system automatically restarts only the malfunctioning module. 
<em>NOTE:</em> due to the new modular Service structure, the way it is started has changed. Now, the process WebCon.WorkFlow.Bootstrapper.exe is initiated first, which then launches the remaining Service modules. This means that starting the Bootstrapper process does not necessarily mean the full Service is already running — the Service is considered fully started only when all modules have been activated</p>
</blockquote>

<h2 id="logging-changes">Logging changes</h2>
<p>The way logs are generated is undergoing a restructuring process, which is not yet finished:</p>

<blockquote>
  <p>The error logging method has been changed from text-based logs (NLog) to structured logs (Serilog).</p>
</blockquote>

<blockquote>
  <p>Logs of level Warning and above are written to an MSSQL database configured in the MSsqlServer node (by default, this is the <em>Infrastructure.Logs</em> table in the WEBCON BPS <em>Configuration Database</em>). Note: For large environments, it is recommended to create a dedicated MSSQL database for logs, or use specialized logging tools (Sinks)</p>
</blockquote>

<blockquote>
  <p>Some loggers will continue writing to their respective tables as before. They will be replaced with Serilog-based logging in future versions.  Information about these updates will be included in the version roadmap. Components that remain unchanged in this version:</p>
  <ul>
    <li>Debug mode in Designer Studio</li>
    <li>AdminWfEventLogs</li>
    <li>logging by the Service</li>
    <li>Diagnostic sessions</li>
    <li>logs for REST API and UDA</li>
  </ul>
</blockquote>

<p>According to the documentation Serilog is better suited for OpenTelemetry which has undergone a few changes.</p>

<h1 id="user-experience">User Experience</h1>

<h2 id="form">Form</h2>

<ul>
  <li>Tabs are no longer displayed as a drop down on a small screen</li>
</ul>

<blockquote>
  <p>Restored the ability to change the size of the Multiple lines of text form field in read-only mode.</p>
</blockquote>

<p>An update for tooltips:</p>

<blockquote>
  <p>Previously, the target=”_blank” HTML form field was automatically removed, preventing links from opening in a new tab. It is now allowed in tooltips for form field descriptions, ensuring consistent behavior.</p>
</blockquote>

<h2 id="attachment-changes">Attachment changes</h2>
<blockquote>
  <p>It is now possible to overwrite attachments in workflows where the Attachments security level is set to High. In such cases, users can overwrite an attachment as long as the form settings have the Edit file and Add and delete options enabled.</p>
</blockquote>

<blockquote>
  <p>Enabled the option to add multiple attachments at once to the Local attachments column on the Item list by holding CTRL and selecting files. The option is available when the Allow adding more than one attachment
checkbox is selected in the Advanced configuration of Local attachments.</p>
</blockquote>

<p>Attachment icons have been changed</p>

<h2 id="access-authorization">Access authorization</h2>
<p>This <a href="https://community.webcon.com/posts/post/access-authorization-to-form-fields/514/3">features</a> hides some field values until the user authorized himself. In case the form was displayed in edit mode, this authorization is always required. This was kind of annoying, if you had a task but just wanted to view the data. Now form is displayed in view mode, even if you have a task. At least if the edit mode is set to <a href="https://docs.webcon.com/docs/2026R1/Studio/Workflow/Step/Step_FormView/#edit-mode">dynamic</a></p>

<blockquote>
  <p>For the Access authorization feature, it is now possible to add table Reports that contain columns requiring authorization to Dashboards. To facilitate this, columns requiring authorization can also be added to Datasets on Dashboards. Once added, the column’s name will display an icon with a tooltip, indicating its special status. Columns requiring authorization cannot be used by the Filter widget, and they remain unseen in the Filter panel configuration on Dashboards.
Added report access authorization session continuity. Upon returning to a previously authorized report, the authorization session timer continues from 
the initial authorization time.</p>
</blockquote>

<h2 id="history">History</h2>
<p>Finally, we get this option. My only other wish would be to have a tooltip for a choose field to display it’s id.</p>

<blockquote>
  <p>Expanded the time display format in tooltips for instance History. As a result, seconds are now shown in addition to hours and minutes.</p>
</blockquote>

<h2 id="reports">Reports</h2>

<blockquote>
  <p>Restored the ability to use calculated columns as a filter in the Data source URL parameter within Report configuration
When filtering the dashboard by a date scope, the results now also include the last day of the selected interval.</p>
</blockquote>

<blockquote>
  <p>Fixed incorrect system behavior when saving custom folder visibility settings. Due to a bug, newly granted folder access permissions were removed after clicking the Save button. These permissions were then restored after adding and saving permissions for other users or groups.</p>
</blockquote>

<blockquote>
  <p>Restored the addition of the returnUrl parameter to the instance address when launching a new workflow instance via a fully defined Start button (with 
a specified process and workflow) from the navigation menu. As a result, users are now returned to the previously opened view when clicking the Back button on the form.</p>
</blockquote>

<blockquote>
  <p>Restored the ability to switch to private views in Reports embedded in Dashboards via the HTML code widget. To enable view switching for users, the option Allow changing view must be selected (Settings → Embed)</p>
</blockquote>

<blockquote>
  <p>Fixed a bug where, despite selecting the Dynamic option (Rows per page) in the dashboard Report configuration, the number of rows wasn’t adjusting to the current report window size. The issue occurred if the report was initially displayed within a dashboard window and then opened in a separate window</p>
</blockquote>

<h1 id="actions">Actions</h1>
<h2 id="rest-web-service">REST Web Service</h2>

<blockquote>
  <p>Improved response header mapping in the Invoke REST Web service action. Previously, when headers occurred multiple times, only the first value was 
retrieved. Now, all header values are returned concatenated with a comma.</p>
</blockquote>

<p>We can now directly store response values in input parameters instead of saving them to a field.</p>

<h2 id="hotfolders-hotmailboxes-archiving">HotFolders, HotMailBoxes, Archiving</h2>

<blockquote>
  <p>The paths of temporary folders for HotFolders, HotMailBoxes, and Archiving functionalities have been shortened by replacing the full database name with its acronym. Additionally, the HotMailBoxes folder name has been shortened to HMB. The rules for processing (executing and saving) workflow instances by the HotFolders and HotMailBoxes modules have also been modified. Now, if the  values inserted into form fields through HotFolders or HotMailBoxes do not pass validation, the system prevents the workflow instance from transiting a path. In  such a case, the email or document sent via HotFolders is moved to the Error folder  for incorrectly processed file.</p>
</blockquote>

<blockquote>
  <p>Fixed an issue where attachments from signed email messages were corrupted after being added to a form via the HotMailBox functionality</p>
</blockquote>

<blockquote>
  <p>Improved the HotMailBoxes’ ability to find attachments based on RegEx. The fix now makes regular expression attachment searches case-insensitive.</p>
</blockquote>

<h2 id="waiting-for-sub-workflows">Waiting for sub-workflows</h2>
<p>There’s a change to the workflow is in the <code class="language-plaintext highlighter-rouge">waiting for sub-workflows</code></p>
<ul>
  <li>If the last workflow completes, the parent workflow will be moved forward asynchronously</li>
  <li>As I understand the text this means:
    <ul>
      <li>The workflow service is now recorded as the person moving the parent workflow</li>
      <li>I have no idea what will happen if the path transition fails. Will it stay in the step or will the user get an error message.</li>
    </ul>
  </li>
</ul>

<blockquote>
  <p>The path transition in the parent workflow, after all sub-workflows are completed, is now executed asynchronously by the system account (using a mechanism analogous to a Timer) on the Service account with a retry mechanism (5 attempts).</p>
</blockquote>

<h2 id="analytics">Analytics</h2>

<p>Has gotten an UI overhaul.</p>

<blockquote>
  <p>Fixed incorrect attribution of editing time in analytical reports. The update resolves an issue where the time spent editing a form was being assigned to users from the previous step instead of those currently assigned to the task. Now, the times are correctly attributed to the users involved in the given step.</p>
</blockquote>

<h1 id="removed-features">Removed features</h1>

<ul>
  <li>Searching structures has been removed<br />
I hope no one will be annoyed by it. When I asked about it in the community, I got no responses that others are using it.</li>
</ul>

<blockquote>
  <p>Removed UseWindowsAuthentication and ForceHttpsOnProxy flags from Portal configuration</p>
</blockquote>

<blockquote>
  <p>REST API 5.0 has been removed</p>
</blockquote>

<p>I copied the below text from the security chapter and I’m not sure in which context these parameters have been removed. I know URL parameters for (starting) workflow instances and reports. I wasn’t able to find the <em>parameterName</em> text in any documentation so I’m confused. I did find the <em>userLogin</em> parameter but not in the context of URL parameters.</p>

<blockquote>
  <p>The <em>parameterName</em> and <em>userLogin</em> URL parameters are no longer accepted.</p>
</blockquote>

<h1 id="performance-improvements">Performance improvements</h1>
<ul>
  <li>Reports viewed as subordinates</li>
  <li>If <code class="language-plaintext highlighter-rouge">working on-behalf</code> task and search result retrieval has been improved</li>
</ul>

<blockquote>
  <p>Improved filtering performance based on the Value filter in Report columns containing multilingual Choice fields by optimizing the SQL query, what prevents timeout errors during search.</p>
</blockquote>

<blockquote>
  <p>Improved performance of the All Active report in the substitution configuration. Optimized database queries, eliminating the possibility of timeouts and fixing a bug.</p>
</blockquote>

<blockquote>
  <p>Improved Report filter functionality. Now, changing the filter immediately cancels the previous filtering query, smoothing out how the Report works</p>
</blockquote>

<blockquote>
  <p>Improved loading performance for the Item list form field. Previously, enabling the Single row editing mode and importing a large number of rows  (&gt; 500) resulted in slow form field loading.</p>
</blockquote>

<h1 id="sdks">SDKs</h1>

<h2 id="net-9-or-net-framework">.Net 9 or .Net Framework</h2>
<p>2026 R1 requires of the plugins, but this time almost all actions will be run in the context of .Net 9. You can take a look at <a href="https://community.webcon.com/community/public/uploads/editor/SDK_Migration_2026_1.pdf">SDK Migration 2026 R1</a></p>

<blockquote>
  <p>Identifying the runtime environment:
Can be verified via the Context.Environment field:</p>
  <ul>
    <li>Environment.Web → .NET 9</li>
    <li>Environment.Service → .NET Framework</li>
  </ul>
</blockquote>

<blockquote>
  <p>If the migrated plugin package contains plugins used for operations outside of workflows, the project should continue to use the previous targeting: .NET Standard 2.0</p>

</blockquote>

<h2 id="form-field-extensions">Form Field Extensions</h2>

<p>React was updated to 19.1. I’m not a React guy, but this may be a good time to update your FormFieldExtensions.</p>

<blockquote>
  <p>New JavaScript API methods have been added to WEBCON BPS forms, enabling full attachment support for Form Field Extensions. Previously, it was only possible to check whether attachments existed. The API now also allows adding, modifying, overwriting, and deleting attachments, retrieving the full list of files, and dynamically showing or hiding the attachment section on the form.</p>
</blockquote>

<h2 id="custom-controls-external-as-cdns">Custom Controls (External) as CDNs?</h2>

<p>Maybe my thoughts go overboard, but do we now have a CDN?</p>
<blockquote>
  <p>For the Custom control (External) form field, a new mode has been added that allows hosting the control locally within Portal. The content must be uploaded as a .zip file, with a single entry point file named index.html. Files can be placed in subfolders, and the link to the associated file should be in the following format: “folder_name/file_name” or “./folder_name/file_name”.</p>
</blockquote>

<h1 id="user-defined-actions">User Defined Actions</h1>

<blockquote>
  <p>Introduced access restrictions in the User Defined API (UDA) functionality. In the Get data from an element instance mode, it is now no longer possible to  modify hidden form fields. In the Actions on a workflow instance mode, modifying both hidden and read-only fields has been blocked</p>
</blockquote>

<blockquote>
  <p>A new API definitions node has been added to the System settings tab. This new node provides System administrators with a view of all registered UDA endpoints</p>
</blockquote>

<p>Custom controls can now make use of UDAs:</p>
<blockquote>
  <p>Additionally, to enable integration with User Defined API (API Definitions), an API tab has been added to the field’s configuration. This  allows the control to communicate with endpoints defined in the API  Definition through JS API. The list of available API definitions is limited to those  that are active, have OAuth authentication enabled, and those that have  been explicitly selected beforehand.</p>
</blockquote>

<blockquote>
  <p>Improved the OpenAPI documentation generation for the API Definitions (UDA) module by adding parameter descriptions, streamlining the system integration process.</p>
</blockquote>

<blockquote>
  <p>The User Defined API (UDA) mechanism has been expanded to include a new Running mode: Actions on a workflow instance, which can be used to WEBCON BPS 
start and save a workflow instance, and to move it to the next step using a path.</p>
</blockquote>

<p>It’s time to take a look at the UDAs.</p>

<blockquote>
  <p>A BPS application administrator can now generate OpenAPI documentation for the Actions on a workflow instance mode including the [System] Comment and [System] Attachments fields.</p>
</blockquote>

<h1 id="some-fixes-which-caused-problems-for-me">Some fixes which caused problems for me</h1>

<blockquote>
  <p>Improved the Clear Item list action import. Previously, the functionality failed to operate correctly after transfer to a different environment, due to a missing Item list mapping</p>
</blockquote>

<blockquote>
  <p>Unified validation of choice fields on the form. Inconsistencies between how the Service and Portal handled unvalidated values potentially led to errors. Now, the service prevents the saving of unvalidated values.</p>
</blockquote>

<blockquote>
  <p>Improved the attachment sorting within PDF/DOCX files merged using the Merge attachments into Word/PDF file action. Previously, attachments were 
sorted disregarding the database order and not aligning with the Merge order: according to SQL configuration option. Now, attachments are sorted according to the chosen configuration.</p>
</blockquote>

<blockquote>
  <p>Fixed the behavior of the Remove attachment action in the Remove only history of binary data mode, which incorrectly removed the attachment from 
the SOLR index. After the fix, the action removes only historical data, leaving the current version correctly indexed</p>
</blockquote>

<blockquote>
  <p>Fixed licensing functionality in the cloud installation (WEBCONAPPS-SaaS). If the number of assigned Power User (Designer Studio) licenses exceeds the number available, users can still access the Portal, but Designer Studio access requires removal of the surplus assigned licenses via the Admin panel.</p>
</blockquote>

<h1 id="name-changes">Name changes</h1>

<table>
  <thead>
    <tr>
      <th>Area</th>
      <th>Old name</th>
      <th>New name</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Action</td>
      <td>Start a subworkflow</td>
      <td>Start workflow</td>
    </tr>
    <tr>
      <td>Action</td>
      <td>Start a subworkflow  (SQL)</td>
      <td>Start workflow (SQL)</td>
    </tr>
    <tr>
      <td>Logging</td>
      <td>ErrorGuid</td>
      <td>CorrelationID</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Daniel Krüger</name></author><category term="WEBCON BPS" /><summary type="html"><![CDATA[This is an excerpt of the change log and my thoughts on it.]]></summary></entry><entry><title type="html">Moving attachment files to other database</title><link href="https://daniels-notes.de/posts/2025/moving-attachment-files" rel="alternate" type="text/html" title="Moving attachment files to other database" /><published>2025-10-12T00:00:00+00:00</published><updated>2025-10-12T00:00:00+00:00</updated><id>https://daniels-notes.de/posts/2025/Moving-attachment-files</id><content type="html" xml:base="https://daniels-notes.de/posts/2025/moving-attachment-files"><![CDATA[<h1 id="overview">Overview</h1>

<p>If you are wondering why this is necessary, this post is probably not for you. But the reason is that you cannot change the attachment database after an attachment has been added using the Designer Studio. Nevertheless, with a little bit of understanding of the database structures and SQL, this isn’t a problem. :)</p>
<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-09-36-16.png" alt="If it doesn’t work from the Designer Studio we use another way." /><figcaption>
      If it doesn’t work from the Designer Studio we use another way.

    </figcaption></figure>

<h1 id="test-scenario">Test scenario</h1>
<p>I’ve set up a standard process which saves the attachments in the default attachment database.</p>
<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-07-45-45.png" alt="Standard process which stores attachment in content db." /><figcaption>
      Standard process which stores attachment in content db.

    </figcaption></figure>

<p>I added multiple documents to a workflow instance:</p>
<ul>
  <li>Quote for bikes 2023-09-15.docx
I’ve modified this in Word a few times
    <ul>
      <li>Version 1<br />
/attachments/db/23/preview/236?fileversion=0&amp;isdesignerdesk=0&amp;authkey=
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-07-59-21.png" alt="" /></li>
      <li>Version 2<br />
/attachments/db/23/preview/236?fileversion=1
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-00-54.png" alt="" /></li>
      <li>Version 3<br />
Has been modified in the same step
/attachments/db/23/preview/236?fileversion=2
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-01-33.png" alt="" />
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-01-49.png" alt="" /></li>
      <li>Version 4<br />
/attachments/db/23/preview/236?fileversion=3
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-02-23.png" alt="" /></li>
    </ul>
  </li>
  <li>Credit rating as of 2023-09-17.docx
Has been added in one step and deleted at a later step</li>
  <li>Overwriting Credit-rating as of 2023-09-17.pdf with capa-report.pdf<br />
The attachment id is the same
/attachments/db/23/preview/237?fileversion=0<br />
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-03-04.png" alt="" />
/attachments/db/23/preview/237?fileversion=1
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-04-28.png" alt="" /></li>
</ul>

<p>In the below screenshot we can see that the content database is accessed to fetch the attachment file. I’ve used this approach to verify that the copied files will be read from the correct database, before deleting them from the content database.</p>
<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-12-09.png" alt="SQL Profiler trace shows that the content database is used." /><figcaption>
      SQL Profiler trace shows that the content database is used.

    </figcaption></figure>

<p class="notice--info"><strong>Info:</strong> If you want to test this, you shouldn’t click on all attachments for the preview. It seems that WEBCON BPS somehow caches the previewed document.</p>

<p>This SQL queries returns all the attachment files</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Using the GUID will allow us to execute the same script on different environments */</span>
<span class="k">declare</span> <span class="o">@</span><span class="n">defId</span> <span class="nb">int</span> <span class="o">=</span> <span class="p">(</span><span class="k">select</span> <span class="n">DEF_ID</span> <span class="k">from</span> <span class="n">WFDefinitions</span>  <span class="k">where</span> <span class="n">DEF_Guid</span> <span class="o">=</span> <span class="s1">'ec4a610b-ca59-4058-9907-53f6b3877c9c'</span><span class="p">)</span>
<span class="k">select</span>  <span class="n">WFAttachmentFiles</span><span class="p">.</span><span class="n">ATF_ATTID</span><span class="p">,</span> <span class="n">WFAttachmentFiles</span><span class="p">.</span><span class="n">ATF_Version</span><span class="p">,</span> <span class="n">WFAttachmentFiles</span><span class="p">.</span><span class="n">ATF_OrginalName</span>
<span class="k">from</span> 
  <span class="n">WFAttachmentFiles</span> 
	<span class="k">join</span> <span class="n">WFDataAttachmets</span> <span class="k">on</span> <span class="n">ATF_ATTID</span> <span class="o">=</span> <span class="n">ATT_ID</span>		
	<span class="cm">/* The V_WFElements view would be another option, but the execution will be longer because of the numerous joins */</span>
	<span class="k">join</span> <span class="n">WFElements</span> <span class="k">on</span> <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="n">WFD_ID</span>
	<span class="k">join</span> <span class="n">WFDocTypes</span> <span class="k">on</span>  <span class="n">WFD_DTYPEID</span> <span class="o">=</span> <span class="n">DTYPE_ID</span>
<span class="k">where</span> 
 <span class="n">DTYPE_DEFID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span>


</code></pre></div></div>

<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-19-32.png" alt="The current attachment files rows in the content database." /><figcaption>
      The current attachment files rows in the content database.

    </figcaption></figure>

<h1 id="moving-files">Moving files</h1>
<h2 id="attachment-database-creation">Attachment database creation</h2>
<p>If you haven’t done so already, create a new attachment database from the <code class="language-plaintext highlighter-rouge">Tools for application management</code> dialog in the setup.</p>
<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-07-39-42.png" alt="Setup.exe -&gt; Tools for application management -&gt; Create attachment database" /><figcaption>
      Setup.exe -&gt; Tools for application management -&gt; Create attachment database

    </figcaption></figure>

<p>Follow the advice and recycle the application pool or run <code class="language-plaintext highlighter-rouge">iisreset</code> from the command line in admin mode.</p>

<h2 id="from-content-db-to-attachment-db">From content db to attachment db</h2>
<p>This very simple query will copy the files of the defined process from the current content database to the target attachment database and change the attachment database. Make sure to read the</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Using the GUID will allow us to execute the same script on different environments */</span>
<span class="k">declare</span> <span class="o">@</span><span class="n">defId</span> <span class="nb">int</span> <span class="o">=</span> <span class="p">(</span><span class="k">select</span> <span class="n">DEF_ID</span> <span class="k">from</span> <span class="n">WFDefinitions</span> <span class="k">where</span> <span class="n">DEF_Guid</span> <span class="o">=</span> <span class="s1">'ec4a610b-ca59-4058-9907-53f6b3877c9c'</span><span class="p">)</span>

<span class="k">insert</span> <span class="k">into</span> <span class="p">[</span><span class="n">DEV01_BPS_Content_DKR_Att</span><span class="p">].</span><span class="n">dbo</span><span class="p">.</span><span class="n">WFAttachmentFiles</span> <span class="p">(</span>
    <span class="n">ATF_WFDID</span><span class="p">,</span> <span class="n">ATF_ATTID</span><span class="p">,</span> <span class="n">ATF_Value</span><span class="p">,</span> <span class="n">ATF_TSInsert</span><span class="p">,</span> <span class="n">ATF_TSUpdate</span><span class="p">,</span> 
    <span class="n">ATF_IsDeleted</span><span class="p">,</span> <span class="n">ATF_Version</span><span class="p">,</span> <span class="n">ATF_AttachmentImage</span><span class="p">,</span> <span class="n">ATF_OrginalValueHash</span><span class="p">,</span> <span class="n">ATF_FileType</span><span class="p">,</span> 
    <span class="n">ATF_CreatedBy</span><span class="p">,</span> <span class="n">ATF_UpdatedBy</span><span class="p">,</span> <span class="n">ATF_FlexiData</span><span class="p">,</span> <span class="n">ATF_AttributesMapping</span><span class="p">,</span> <span class="n">ATF_OrginalName</span><span class="p">,</span> <span class="n">ATF_FRData</span>
<span class="p">)</span>
<span class="k">select</span> 
    <span class="n">ATF_WFDID</span><span class="p">,</span> <span class="n">ATF_ATTID</span><span class="p">,</span> <span class="n">ATF_Value</span><span class="p">,</span> <span class="n">ATF_TSInsert</span><span class="p">,</span> <span class="n">ATF_TSUpdate</span><span class="p">,</span> 
    <span class="n">ATF_IsDeleted</span><span class="p">,</span> <span class="n">ATF_Version</span><span class="p">,</span> <span class="n">ATF_AttachmentImage</span><span class="p">,</span> <span class="n">ATF_OrginalValueHash</span><span class="p">,</span> <span class="n">ATF_FileType</span><span class="p">,</span> 
    <span class="n">ATF_CreatedBy</span><span class="p">,</span> <span class="n">ATF_UpdatedBy</span><span class="p">,</span> <span class="n">ATF_FlexiData</span><span class="p">,</span> <span class="n">ATF_AttributesMapping</span><span class="p">,</span> <span class="n">ATF_OrginalName</span><span class="p">,</span> <span class="n">ATF_FRData</span>
<span class="k">from</span> 
    <span class="n">WFAttachmentFiles</span> 
    <span class="k">join</span> <span class="n">WFDataAttachmets</span> <span class="k">on</span> <span class="n">ATF_ATTID</span> <span class="o">=</span> <span class="n">ATT_ID</span>
    <span class="k">join</span> <span class="n">WFElements</span> <span class="k">on</span> <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="n">WFD_ID</span>
    <span class="k">join</span> <span class="n">WFDocTypes</span> <span class="k">on</span> <span class="n">WFD_DTYPEID</span> <span class="o">=</span> <span class="n">DTYPE_ID</span>
<span class="k">where</span> 
    <span class="n">DTYPE_DEFID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span>

<span class="k">update</span> <span class="n">WFDefinitions</span>
 <span class="k">set</span> <span class="n">DEF_AttachmentsDatabase</span> <span class="o">=</span> <span class="s1">'DEV01_BPS_Content_DKR_Att'</span>
<span class="k">where</span> <span class="n">DEF_ID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span>
</code></pre></div></div>

<p>Afterwards I would recommend:</p>
<ul>
  <li>Execute an IIS reset</li>
  <li>Restart the WEBCON service</li>
  <li>Reset all caches in the Designer Studio
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-10-24-55.png" alt="" /></li>
</ul>

<p>Even so I did all this and hit the <code class="language-plaintext highlighter-rouge">Refresh all</code> button the Designer studio the changed database name has only be visible after a restart</p>
<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-45-02.png" alt="The Designer Studio showed the new database name only after a restart" /><figcaption>
      The Designer Studio showed the new database name only after a restart

    </figcaption></figure>

<h2 id="verification">Verification</h2>
<p>Everything seems to be fine:</p>
<ul>
  <li>The profiler trace is looking good<br />
We now see the attachment database name
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-46-58.png" alt="" /></li>
  <li>Preview from the form works
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-49-23.png" alt="" /></li>
  <li>The previews from the history look fine
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-48-23.png" alt="" /></li>
  <li>Editing existing file works
<img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-50-07.png" alt="" /></li>
  <li>The new version is saved in the attachment database
  <img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-08-52-55.png" alt="" /></li>
</ul>

<h2 id="deletion-of-the-old-rows">Deletion of the old rows</h2>
<p>Since everything looks good, it’s time for the real test and delete the existing rows in the database.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Using the GUID will allow us to execute the same script on different environments */</span>
<span class="k">declare</span> <span class="o">@</span><span class="n">defId</span> <span class="nb">int</span> <span class="o">=</span> <span class="p">(</span><span class="k">select</span> <span class="n">DEF_ID</span> <span class="k">from</span> <span class="n">WFDefinitions</span> <span class="k">where</span> <span class="n">DEF_Guid</span> <span class="o">=</span> <span class="s1">'ec4a610b-ca59-4058-9907-53f6b3877c9c'</span><span class="p">)</span>

<span class="k">delete</span> <span class="k">from</span> <span class="n">WFAttachmentFiles</span>
<span class="k">where</span> <span class="n">ATF_ATTID</span> <span class="k">in</span> <span class="p">(</span>
    <span class="k">select</span> <span class="n">ATT_ID</span>
    <span class="k">from</span> <span class="n">WFDataAttachmets</span>
    <span class="k">join</span> <span class="n">WFElements</span> <span class="k">on</span> <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="n">WFD_ID</span>
    <span class="k">join</span> <span class="n">WFDocTypes</span> <span class="k">on</span> <span class="n">WFD_DTYPEID</span> <span class="o">=</span> <span class="n">DTYPE_ID</span>
    <span class="k">where</span> <span class="n">DTYPE_DEFID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span>
<span class="p">)</span>
</code></pre></div></div>

<h2 id="reindex-the-process">Reindex the process</h2>
<p>If you haven’t deactivated the <code class="language-plaintext highlighter-rouge">Add attachment content to SOLR</code> in the process settings, you should reindex the process</p>
<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-07-41-52.png" alt="Process settings defining, whether an attachment can be searched." /><figcaption>
      Process settings defining, whether an attachment can be searched.

    </figcaption></figure>

<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-07-43-50.png" alt="Select the process" /><figcaption>
      Select the process

    </figcaption></figure>

<h2 id="final-tests">Final tests</h2>
<p>I could open all the previews just fine and even the search is working:</p>

<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-09-11-30.png" alt="Even searching for the content works fine." /><figcaption>
      Even searching for the content works fine.

    </figcaption></figure>

<h2 id="from-one-attachment-database-to-another">From one attachment database to another</h2>
<p>I didn’t check this thoroughly and only wanted to test it because it’s just adding a database name to the SQL statements. :)</p>

<p>This is the statement for copying the files to another attachment database and the difference to the original one:</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Using the GUID will allow us to execute the same script on different environments */</span>
<span class="k">declare</span> <span class="o">@</span><span class="n">defId</span> <span class="nb">int</span> <span class="o">=</span> <span class="p">(</span><span class="k">select</span> <span class="n">DEF_ID</span> <span class="k">from</span> <span class="n">WFDefinitions</span> <span class="k">where</span> <span class="n">DEF_Guid</span> <span class="o">=</span> <span class="s1">'ec4a610b-ca59-4058-9907-53f6b3877c9c'</span><span class="p">)</span>

<span class="k">insert</span> <span class="k">into</span> <span class="p">[</span><span class="n">DEV01_BPS_Content_DKR_Att2</span><span class="p">].</span><span class="n">dbo</span><span class="p">.</span><span class="n">WFAttachmentFiles</span> <span class="p">(</span>
    <span class="n">ATF_WFDID</span><span class="p">,</span> <span class="n">ATF_ATTID</span><span class="p">,</span> <span class="n">ATF_Value</span><span class="p">,</span> <span class="n">ATF_TSInsert</span><span class="p">,</span> <span class="n">ATF_TSUpdate</span><span class="p">,</span> 
    <span class="n">ATF_IsDeleted</span><span class="p">,</span> <span class="n">ATF_Version</span><span class="p">,</span> <span class="n">ATF_AttachmentImage</span><span class="p">,</span> <span class="n">ATF_OrginalValueHash</span><span class="p">,</span> <span class="n">ATF_FileType</span><span class="p">,</span> 
    <span class="n">ATF_CreatedBy</span><span class="p">,</span> <span class="n">ATF_UpdatedBy</span><span class="p">,</span> <span class="n">ATF_FlexiData</span><span class="p">,</span> <span class="n">ATF_AttributesMapping</span><span class="p">,</span> <span class="n">ATF_OrginalName</span><span class="p">,</span> <span class="n">ATF_FRData</span>
<span class="p">)</span>
<span class="k">select</span> 
    <span class="n">ATF_WFDID</span><span class="p">,</span> <span class="n">ATF_ATTID</span><span class="p">,</span> <span class="n">ATF_Value</span><span class="p">,</span> <span class="n">ATF_TSInsert</span><span class="p">,</span> <span class="n">ATF_TSUpdate</span><span class="p">,</span> 
    <span class="n">ATF_IsDeleted</span><span class="p">,</span> <span class="n">ATF_Version</span><span class="p">,</span> <span class="n">ATF_AttachmentImage</span><span class="p">,</span> <span class="n">ATF_OrginalValueHash</span><span class="p">,</span> <span class="n">ATF_FileType</span><span class="p">,</span> 
    <span class="n">ATF_CreatedBy</span><span class="p">,</span> <span class="n">ATF_UpdatedBy</span><span class="p">,</span> <span class="n">ATF_FlexiData</span><span class="p">,</span> <span class="n">ATF_AttributesMapping</span><span class="p">,</span> <span class="n">ATF_OrginalName</span><span class="p">,</span> <span class="n">ATF_FRData</span>
<span class="k">from</span> 
   <span class="p">[</span><span class="n">DEV01_BPS_Content_DKR_Att</span><span class="p">].</span><span class="n">dbo</span><span class="p">.</span><span class="n">WFAttachmentFiles</span> 
    <span class="k">join</span> <span class="n">WFDataAttachmets</span> <span class="k">on</span> <span class="n">ATF_ATTID</span> <span class="o">=</span> <span class="n">ATT_ID</span>
    <span class="k">join</span> <span class="n">WFElements</span> <span class="k">on</span> <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="n">WFD_ID</span>
    <span class="k">join</span> <span class="n">WFDocTypes</span> <span class="k">on</span> <span class="n">WFD_DTYPEID</span> <span class="o">=</span> <span class="n">DTYPE_ID</span>
<span class="k">where</span> 
    <span class="n">DTYPE_DEFID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span>

<span class="k">update</span> <span class="n">WFDefinitions</span>
 <span class="k">set</span> <span class="n">DEF_AttachmentsDatabase</span> <span class="o">=</span> <span class="s1">'DEV01_BPS_Content_DKR_Att2'</span>
<span class="k">where</span> <span class="n">DEF_ID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span>
</code></pre></div></div>
<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-09-22-44.png" alt="Differences between the statements for copying the files" /><figcaption>
      Differences between the statements for copying the files

    </figcaption></figure>

<p>This is the statement for deleting the files to another attachment database and the difference to the original one:</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* Using the GUID will allow us to execute the same script on different environments */</span>
<span class="k">declare</span> <span class="o">@</span><span class="n">defId</span> <span class="nb">int</span> <span class="o">=</span> <span class="p">(</span><span class="k">select</span> <span class="n">DEF_ID</span> <span class="k">from</span> <span class="n">WFDefinitions</span> <span class="k">where</span> <span class="n">DEF_Guid</span> <span class="o">=</span> <span class="s1">'ec4a610b-ca59-4058-9907-53f6b3877c9c'</span><span class="p">)</span>

<span class="k">delete</span> <span class="k">from</span> <span class="p">[</span><span class="n">DEV01_BPS_Content_DKR_Att</span><span class="p">].</span><span class="n">dbo</span><span class="p">.</span><span class="n">WFAttachmentFiles</span>
<span class="k">where</span> <span class="n">ATF_ATTID</span> <span class="k">in</span> <span class="p">(</span>
    <span class="k">select</span> <span class="n">ATT_ID</span>
    <span class="k">from</span> <span class="n">WFDataAttachmets</span>
    <span class="k">join</span> <span class="n">WFElements</span> <span class="k">on</span> <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="n">WFD_ID</span>
    <span class="k">join</span> <span class="n">WFDocTypes</span> <span class="k">on</span> <span class="n">WFD_DTYPEID</span> <span class="o">=</span> <span class="n">DTYPE_ID</span>
    <span class="k">where</span> <span class="n">DTYPE_DEFID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span>
<span class="p">)</span>
</code></pre></div></div>
<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-09-23-36.png" alt="Difference between the statements for deleting the attachment files." /><figcaption>
      Difference between the statements for deleting the attachment files.

    </figcaption></figure>

<p>In my case SOLR throwed and error and I have no idea why. I needed to grant the WEBCON workflow service account permission to the database manually although it had been created using the setup.</p>
<figure class=""><img src="/assets/images/posts/2025-10-12-Moving-attachment-files/2025-10-12-09-20-04.png" alt="" /><figcaption>
      

    </figcaption></figure>

<p>Afterwards everything was working as expected.</p>

<h1 id="warnings">Warnings</h1>
<h2 id="its-your-risk">It’s your risk</h2>
<p>You should be aware that any database modifications can severely damage your environment.</p>

<p>You need to verify everything for yourself in your own environment. Especially, if you are using OCR. I don’t have this in my environment.</p>

<p>Remember, it would be best if you could recreate the production database on your test environment and test it there. You don’t know how? Of course, there’s a <a href="/posts/2023/copy-a-database">post</a> for this. ;)</p>

<p>After recreating the database, you should backup it too. This will save time, if there’s an issue with the script.</p>

<h2 id="onedrive">OneDrive</h2>
<p>While it shouldn’t make a difference you should also check whether any files are currently made available in OneDrive. While I don’t think that it will have an impact, it could have.</p>

<h2 id="verify-columns">Verify columns</h2>
<p>I’ve tested this using version 2025.2.1.179, if you are running a different version, you should verify that you copy all columns except:</p>
<ul>
  <li>ATF_ID</li>
  <li>ATF_RowVersion</li>
</ul>

<h1 id="script-improvements">Script improvements</h1>

<h2 id="handle-database-growth-with-transaction">Handle database growth with transaction</h2>
<p>Depending on the amount and size of your attachment files it will not only take a while but will bloat the log file if you don’t modify the scripts. 
It may be necessary to add some transaction /commit commands so that the log file won’t grow to the same size as the attachment files which will be transferred. Depending on your hard disk setup this may cause problems otherwise.
The below is an untested suggestion form AI</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">DECLARE</span> <span class="o">@</span><span class="n">defId</span> <span class="nb">INT</span> <span class="o">=</span> <span class="p">(</span><span class="k">SELECT</span> <span class="n">DEF_ID</span> <span class="k">FROM</span> <span class="n">WFDefinitions</span> <span class="k">WHERE</span> <span class="n">DEF_Guid</span> <span class="o">=</span> <span class="s1">'ec4a610b-ca59-4058-9907-53f6b3877c9c'</span><span class="p">);</span>
<span class="k">DECLARE</span> <span class="o">@</span><span class="n">BatchSize</span> <span class="nb">INT</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span>
<span class="k">DECLARE</span> <span class="o">@</span><span class="n">LastAttatchmenFileId</span> <span class="nb">INT</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">DECLARE</span> <span class="o">@</span><span class="n">RowsMoved</span> <span class="nb">INT</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="c1">-- Initialize to enter the loop</span>

<span class="n">WHILE</span> <span class="o">@</span><span class="n">RowsMoved</span> <span class="o">&gt;</span> <span class="mi">0</span>
<span class="k">BEGIN</span>
    <span class="k">BEGIN</span> <span class="n">TRANSACTION</span><span class="p">;</span>

    <span class="k">WITH</span> <span class="n">BatchToMove</span> <span class="k">AS</span> <span class="p">(</span>
        <span class="k">SELECT</span> <span class="n">TOP</span> <span class="p">(</span><span class="o">@</span><span class="n">BatchSize</span><span class="p">)</span>
            <span class="n">ATF_WFDID</span><span class="p">,</span> <span class="n">ATF_ATTID</span><span class="p">,</span> <span class="n">ATF_Value</span><span class="p">,</span> <span class="n">ATF_TSInsert</span><span class="p">,</span> <span class="n">ATF_TSUpdate</span><span class="p">,</span> 
            <span class="n">ATF_IsDeleted</span><span class="p">,</span> <span class="n">ATF_Version</span><span class="p">,</span> <span class="n">ATF_AttachmentImage</span><span class="p">,</span> <span class="n">ATF_OrginalValueHash</span><span class="p">,</span> <span class="n">ATF_FileType</span><span class="p">,</span> 
            <span class="n">ATF_CreatedBy</span><span class="p">,</span> <span class="n">ATF_UpdatedBy</span><span class="p">,</span> <span class="n">ATF_FlexiData</span><span class="p">,</span> <span class="n">ATF_AttributesMapping</span><span class="p">,</span> <span class="n">ATF_OrginalName</span><span class="p">,</span> <span class="n">ATF_FRData</span>
        <span class="k">FROM</span> 
            <span class="n">WFAttachmentFiles</span> 
            <span class="k">JOIN</span> <span class="n">WFDataAttachmets</span> <span class="k">ON</span> <span class="n">ATF_ATTID</span> <span class="o">=</span> <span class="n">ATT_ID</span>
            <span class="k">JOIN</span> <span class="n">WFElements</span> <span class="k">ON</span> <span class="n">ATT_WFDID</span> <span class="o">=</span> <span class="n">WFD_ID</span>
            <span class="k">JOIN</span> <span class="n">WFDocTypes</span> <span class="k">ON</span> <span class="n">WFD_DTYPEID</span> <span class="o">=</span> <span class="n">DTYPE_ID</span>
        <span class="k">WHERE</span> 
            <span class="n">DTYPE_DEFID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span>
            <span class="k">AND</span> <span class="n">ATF_ID</span> <span class="o">&gt;</span> <span class="o">@</span><span class="n">LastAttatchmenFileId</span>
        <span class="k">ORDER</span> <span class="k">BY</span> <span class="n">ATF_ID</span>
    <span class="p">)</span>
    <span class="k">INSERT</span> <span class="k">INTO</span> <span class="p">[</span><span class="n">DEV01_BPS_Content_DKR_Att</span><span class="p">].</span><span class="n">dbo</span><span class="p">.</span><span class="n">WFAttachmentFiles</span> <span class="p">(</span>
        <span class="n">ATF_WFDID</span><span class="p">,</span> <span class="n">ATF_ATTID</span><span class="p">,</span> <span class="n">ATF_Value</span><span class="p">,</span> <span class="n">ATF_TSInsert</span><span class="p">,</span> <span class="n">ATF_TSUpdate</span><span class="p">,</span> 
        <span class="n">ATF_IsDeleted</span><span class="p">,</span> <span class="n">ATF_Version</span><span class="p">,</span> <span class="n">ATF_AttachmentImage</span><span class="p">,</span> <span class="n">ATF_OrginalValueHash</span><span class="p">,</span> <span class="n">ATF_FileType</span><span class="p">,</span> 
        <span class="n">ATF_CreatedBy</span><span class="p">,</span> <span class="n">ATF_UpdatedBy</span><span class="p">,</span> <span class="n">ATF_FlexiData</span><span class="p">,</span> <span class="n">ATF_AttributesMapping</span><span class="p">,</span> <span class="n">ATF_OrginalName</span><span class="p">,</span> <span class="n">ATF_FRData</span>
    <span class="p">)</span>
    <span class="k">SELECT</span> 
        <span class="n">ATF_WFDID</span><span class="p">,</span> <span class="n">ATF_ATTID</span><span class="p">,</span> <span class="n">ATF_Value</span><span class="p">,</span> <span class="n">ATF_TSInsert</span><span class="p">,</span> <span class="n">ATF_TSUpdate</span><span class="p">,</span> 
        <span class="n">ATF_IsDeleted</span><span class="p">,</span> <span class="n">ATF_Version</span><span class="p">,</span> <span class="n">ATF_AttachmentImage</span><span class="p">,</span> <span class="n">ATF_OrginalValueHash</span><span class="p">,</span> <span class="n">ATF_FileType</span><span class="p">,</span> 
        <span class="n">ATF_CreatedBy</span><span class="p">,</span> <span class="n">ATF_UpdatedBy</span><span class="p">,</span> <span class="n">ATF_FlexiData</span><span class="p">,</span> <span class="n">ATF_AttributesMapping</span><span class="p">,</span> <span class="n">ATF_OrginalName</span><span class="p">,</span> <span class="n">ATF_FRData</span>
    <span class="k">FROM</span> <span class="n">BatchToMove</span><span class="p">;</span>

    <span class="k">SET</span> <span class="o">@</span><span class="n">RowsMoved</span> <span class="o">=</span> <span class="o">@@</span><span class="n">ROWCOUNT</span><span class="p">;</span>

    <span class="c1">-- Update the last max ATT_ID for the next batch</span>
    <span class="k">SELECT</span> <span class="o">@</span><span class="n">LastAttatchmenFileId</span> <span class="o">=</span> <span class="k">MAX</span><span class="p">(</span><span class="n">ATF_ID</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">BatchToMove</span><span class="p">;</span>

    <span class="k">COMMIT</span> <span class="n">TRANSACTION</span><span class="p">;</span>
<span class="k">END</span>

<span class="c1">-- Update after all batches</span>
<span class="k">UPDATE</span> <span class="n">WFDefinitions</span>
<span class="k">SET</span> <span class="n">DEF_AttachmentsDatabase</span> <span class="o">=</span> <span class="s1">'DEV01_BPS_Content_DKR_Att'</span>
<span class="k">WHERE</span> <span class="n">DEF_ID</span> <span class="o">=</span> <span class="o">@</span><span class="n">defId</span><span class="p">;</span>
</code></pre></div></div>

<h2 id="working-in-batches">Working in batches</h2>
<p>If you can’t execute the migration during offline times, it may be necessary to split the movement into batches. This will require us to check which files have already been copied to the target database and then continue with the missing ones.</p>

<p>If you are doing this, wait with the change of the attachment database until the final batch.</p>]]></content><author><name>Daniel Krüger</name></author><category term="COSMO CONSULT" /><category term="WEBCON BPS" /><category term="Governance" /><summary type="html"><![CDATA[Yes, you can move the attachment files.]]></summary></entry></feed>