diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0f11b523cb90f1ca7ac96e4891d426d78d033d17..e9634999b439bece89911323a4ffbd7bb93c8914 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 * `caosdb-v-property-linkified` css class to denote properties that have been
   linkified already.
+* `form_elements.make_form_modal` and
+  `form_elements.make_scripting_submission_button` functions to create a form
+  modal and an SSS submission button, respectively.
 
 ### Changed (for changes in existing functionality)
 
diff --git a/src/core/js/ext_trigger_crawler_form.js b/src/core/js/ext_trigger_crawler_form.js
index 0796ef77da36e730b05d70dbbd2e8728c6e65c79..a6e1e3a18a3582cc9b3e511880b72d15f730b346 100644
--- a/src/core/js/ext_trigger_crawler_form.js
+++ b/src/core/js/ext_trigger_crawler_form.js
@@ -41,7 +41,7 @@
  * variable `BUILD_MODULE_EXT_TRIGGER_CRAWLER_FORM_TOOLBOX`. The default is
  * `Tools`.
  */
-var ext_trigger_crawler_form = function () {
+var ext_trigger_crawler_form = function ($, form_elements) {
 
     var init = function (toolbox) {
         const _toolbox = toolbox || "${BUILD_MODULE_EXT_TRIGGER_CRAWLER_FORM_TOOLBOX}";
@@ -52,7 +52,7 @@ var ext_trigger_crawler_form = function () {
 
         const crawler_form = make_scripting_caller_form(
             script, button_name);
-        const modal = make_form_modal(crawler_form);
+        const modal = form_elements.make_form_modal(crawler_form, "Trigger the crawler", "Crawl the selected path");
 
 
         navbar.add_tool(button_name, _toolbox, {
@@ -63,32 +63,6 @@ var ext_trigger_crawler_form = function () {
         });
     }
 
-    /**
-     * Wrap the form into a Bootstrap modal.
-     */
-    var make_form_modal = function (form) {
-        const title = "Trigger the Crawler";
-        const modal = $(`
-          <div class="modal fade" tabindex="-1" role="dialog">
-            <div class="modal-dialog" role="document">
-              <div class="modal-content">
-                <div class="modal-header">
-                  <button type="button"
-                    class="btn-close"
-                    data-bs-dismiss="modal"
-                    aria-label="Close">
-                    <span aria-hidden="true">&times;</span>
-                  </button>
-                  <h4 class="modal-title">${title}</h4>
-                </div>
-                <div class="modal-body">
-                </div>
-              </div>
-            </div>`);
-        modal.find(".modal-body").append(form);
-        return modal[0];
-    }
-
     /**
      * Create the trigger crawler form.
      */
@@ -104,15 +78,7 @@ var ext_trigger_crawler_form = function () {
         });
         $(warning_checkbox).find("input").attr("value", "TRUE");
 
-        const scripting_caller = $(`
-        <form method="POST" action="/scripting">
-          <input type="hidden" name="call" value="${script}"/>
-          <input type="hidden" name="-p0" value=""/>
-          <div class="form-control">
-            <input type="submit"
-              class="form-control btn btn-primary" value="${button_name}"/>
-          </div>
-        </form>`);
+        const scripting_caller = form_elements.make_scripting_submission_button(script, button_name);
 
         scripting_caller.prepend(warning_checkbox).prepend(path_field);
 
@@ -123,7 +89,7 @@ var ext_trigger_crawler_form = function () {
         init: init,
     };
 
-}();
+}($, form_elements);
 
 $(document).ready(function () {
     if ("${BUILD_MODULE_EXT_TRIGGER_CRAWLER_FORM}" === "ENABLED") {
diff --git a/src/core/js/form_elements.js b/src/core/js/form_elements.js
index 6815acd791213c6b239a693c3c64667965c369ed..f59f64416993d4fc908c5f2df4fabf5dfee2699e 100644
--- a/src/core/js/form_elements.js
+++ b/src/core/js/form_elements.js
@@ -1563,6 +1563,58 @@ var form_elements = new function () {
         }
     }
 
+    /**
+     * Return a modal HTML element containing the given form.
+     *
+     * @param {HTMLElement} form - the form to be shown in the modal
+     * @param {string} title - the title of the form modal
+     * @param {string} explanationText - An optional paragraph shown between
+     *                                    modal title and form.
+     */
+    this.make_form_modal = function (form, title, explanationText) {
+        const modal = $(`
+              <div class="modal fade" tabindex="-1" role="dialog">
+                <div class="modal-dialog" role="document">
+                  <div class="modal-content">
+                    <div class="modal-header">
+                      <h4 class="modal-title">${title}</h4>
+                      <button type="button"
+                        class="btn-close"
+                        data-bs-dismiss="modal"
+                        aria-label="Close">
+                      </button>
+                    </div>
+                    <div class="modal-body">
+                      <p>${explanationText}</p>
+                    </div>
+                  </div>
+                </div>`);
+
+        modal.find(".modal-body").append(form);
+        return modal[0];
+    }
+
+    /**
+     * Return a submission button that triggers a given server-side-script.
+     *
+     * @param {string} script - Name of the server-side script to be triggered
+     * @param {string} buttonName - Display name of the submission button
+     */
+    this.make_scripting_submission_button = function (script, buttonName) {
+        let button_name = buttonName || "Submit";
+        const scripting_caller = $(`
+            <form method="POST" action="/scripting">
+              <input type="hidden" name="call" value="${script}"/>
+              <input type="hidden" name="-p0" value=""/>
+              <div class="form-group">
+                <input type="submit"
+                  class="form-control btn btn-primary" value="${button_name}"/>
+              </div>
+            </form>`);
+
+        return scripting_caller
+    }
+
 
     /**
      * Return an input and a label, wrapped in a div with class