diff --git a/CHANGELOG.md b/CHANGELOG.md
index 198686f9b98c1bef0d5f2134d68db1ec896c0776..f6447b0141ccc619fc0c9cb58fda982a4479c10a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [0.11.1] - 2023-05-31 ##
+
+### Fixed ###
+
+* Styling problems introduced by a corrupted merge in the previous release
+
 ## [0.11.0] - 2023-05-31
 
 ### Added
diff --git a/build.properties.d/00_default.properties b/build.properties.d/00_default.properties
index 0a370664344017060df14390de16859b03b657c5..62b1b65ab7619e963093602886f17335bbd6295a 100644
--- a/build.properties.d/00_default.properties
+++ b/build.properties.d/00_default.properties
@@ -52,6 +52,7 @@ BUILD_MODULE_EXT_BOOKMARKS=ENABLED
 BUILD_MODULE_EXT_ADD_QUERY_TO_BOOKMARKS=DISABLED
 BUILD_MODULE_EXT_ANNOTATION=ENABLED
 BUILD_MODULE_EXT_COSMETICS_LINKIFY=DISABLED
+BUILD_MODULE_EXT_COSMETICS_CUSTOMDATETIME=DISABLED
 BUILD_MODULE_EXT_QRCODE=ENABLED
 BUILD_MODULE_SHOW_ID_IN_LABEL=DISABLED
 BUILD_MODULE_LEGACY_QUERY_FORM=DISABLED
@@ -173,6 +174,7 @@ MODULE_DEPENDENCIES=(
     loglevel.js
     plotly.js
     webcaosdb.js
+    query_form.js
     pako.js
     utif.js
     ext_version_history.js
diff --git a/src/core/js/ext_cosmetics.js b/src/core/js/ext_cosmetics.js
index 77556437394df6a6763661ce5c0d5001f68ce61a..c29f07e42294ff3d8c0b4b86da829865b8a650c5 100644
--- a/src/core/js/ext_cosmetics.js
+++ b/src/core/js/ext_cosmetics.js
@@ -1,8 +1,10 @@
 /*
  * This file is a part of the CaosDB Project.
  *
- * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021-2023 IndiScale GmbH <info@indiscale.com>
  * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2023 Florian Spreckelsen <f.spreckelsen@indiscale.com>
+ * Copyright (C) 2023 Daniel Hornung <d.hornung@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -30,14 +32,38 @@ var cosmetics = new function () {
 
     /**
      * Cut-off length of links. When linkify processes the links any href
-     * longer than this will be cut off at character 25 and "[...]" will be
+     * longer than this will be cut off at the end and "[...]" will be
      * appended for the link text.
      */
     var _link_cut_off_length = 40;
 
+    var _custom_datetime = function () {
+        $('.caosdb-f-property-datetime-value').each(function (index) {
+            if (!($(this).hasClass("caosdb-v-property-datetime-customized"))) {
+                var result = this.innerText.replace(/T/, " ").replace(/\+.*/, "");
+                result = `<span class="caosdb-v-property-datetime-customized-newvalue">${result}</span>`;
+
+                // add class to highlight that this has been customized already
+                $(this).addClass("caosdb-v-property-datetime-customized")
+                $(this).hide();
+                $(this).after(result);
+            }
+        });
+    }
+
+  /**
+   * Remove all the custom datetime elements again, for example when entering the edit mode.
+   */
+  var _custom_datetime_clear = function() {
+    $('.caosdb-v-property-datetime-customized-newvalue').each(function () {
+      $(this).remove();
+    }
+                                                             )
+  }
+
     var _linkify = function () {
         $('.caosdb-f-property-text-value').each(function (index) {
-            if (/https?:\/\//.test(this.innerText)) {
+            if (!($(this).hasClass("caosdb-v-property-linkified")) && (/https?:\/\//.test(this.innerText))) {
                 var result = this.innerText.replace(/https?:\/\/[^\s]*/g, function (href, index) {
                     var link_text = href;
                     if (_link_cut_off_length > 4 && link_text.length > _link_cut_off_length) {
@@ -47,12 +73,31 @@ var cosmetics = new function () {
                     return `<a title="Open ${href} in a new tab." target="_blank" class="caosdb-v-property-href-value" href="${href}">${link_text} <i class="bi bi-box-arrow-up-right"></i></a>`;
                 });
 
+                // add class to highlight that this has been linkified already
+                // (see https://gitlab.com/caosdb/caosdb-webui/-/issues/199).
+                $(this).addClass("caosdb-v-property-linkified")
                 $(this).hide();
                 $(this).after(result);
             }
         });
     }
 
+    /**
+     * Customize datetime formatting.
+     *
+     * A listener detects edit-mode changes and previews
+     */
+    var custom_datetime = function () {
+        _custom_datetime();
+
+        // edit-mode-listener to delete replacement
+        document.body.addEventListener(edit_mode.start_edit.type, _custom_datetime_clear, true);
+        // edit-mode-listener to recreate
+        document.body.addEventListener(edit_mode.end_edit.type, _custom_datetime, true);
+        // preview listener
+        document.body.addEventListener(preview.previewReadyEvent.type, _custom_datetime, true);
+    }
+
     /**
      * Convert any substring of a text-value beginning with 'http(s)://' into a
      * link.
@@ -69,6 +114,10 @@ var cosmetics = new function () {
     }
 
     this.init = function () {
+        this.custom_datetime = custom_datetime;
+        if ("${BUILD_MODULE_EXT_COSMETICS_CUSTOMDATETIME}" == "ENABLED") {
+            custom_datetime();
+        }
         this.linkify = linkify;
         if ("${BUILD_MODULE_EXT_COSMETICS_LINKIFY}" == "ENABLED") {
             linkify();
@@ -80,4 +129,4 @@ var cosmetics = new function () {
 
 $(document).ready(function () {
     caosdb_modules.register(cosmetics);
-});
\ No newline at end of file
+});
diff --git a/src/core/js/ext_entity_acl.js b/src/core/js/ext_entity_acl.js
index ce6d4160afefffa11a346804a565f832a0aca84e..9fef31fac6f4ff27a4da12f4019d49b0e258f87d 100644
--- a/src/core/js/ext_entity_acl.js
+++ b/src/core/js/ext_entity_acl.js
@@ -32,10 +32,15 @@
  *
  *   BUILD_MODULE_EXT_ENTITY_ACL_URI_ROOT=[scheme://host:port]/what/evs
  *
+ * Enable/disable the creation of a user-management link that is shown to
+ * administrators with
+ *
+ *   BUILD_MODULE_EXT_ENTITY_ACL_USER_MANAGEMENT_BUTTON=ENABLED
+ *
  *
  * @author Timm Fitschen
  */
-var ext_entity_acl = function ($, connection, getEntityVersion, getEntityID, logger) {
+var ext_entity_acl = function ($, connection, getEntityVersion, getEntityID, logger, navbar, userIsAdministrator) {
 
     const BUILD_MODULE_EXT_ENTITY_ACL_URI_ROOT = connection.getBasePath() + "webinterface/acm/entityacl/";
     const _buttons_list_class = "caosdb-v-entity-header-buttons-list";
@@ -69,6 +74,14 @@ var ext_entity_acl = function ($, connection, getEntityVersion, getEntityID, log
         $(entity).find(`.${_buttons_list_class} .${_entity_acl_link_class}`).remove();
     }
 
+    var showUserManagementLink = function () {
+        if (userIsAdministrator()) {
+            navbar.add_button($('<a class="nav-link" href="/webinterface/acm/">User Administration</a>')[0], {
+                title: "Go to user administration"
+            });
+        }
+    }
+
     var _init = function () {
         for (let entity of $(".caosdb-entity-panel")) {
             remove_entity_acl_link(entity);
@@ -82,6 +95,10 @@ var ext_entity_acl = function ($, connection, getEntityVersion, getEntityID, log
      * Removes all respective buttons if present before adding a new one.
      */
     var init = function () {
+        if ("${BUILD_MODULE_EXT_ENTITY_ACL_USER_MANAGEMENT_BUTTON}" == "ENABLED") {
+            showUserManagementLink();
+        }
+
         _init();
 
         // edit-mode-listener
@@ -95,7 +112,7 @@ var ext_entity_acl = function ($, connection, getEntityVersion, getEntityID, log
         init: init
     };
 
-}($, connection, getEntityVersion, getEntityID, log.getLogger("ext_entity_acl"));
+}($, connection, getEntityVersion, getEntityID, log.getLogger("ext_entity_acl"), navbar, userIsAdministrator);
 
 $(document).ready(function () {
     if ("${BUILD_MODULE_EXT_ENTITY_ACL}" == "ENABLED") {
diff --git a/src/core/js/ext_prop_display.js b/src/core/js/ext_prop_display.js
index bc4dc049b409fc1628704a6c778a2ec1c55ad638..3a8c616f503caadd4c0df04800bc8c15f0c068b2 100644
--- a/src/core/js/ext_prop_display.js
+++ b/src/core/js/ext_prop_display.js
@@ -21,6 +21,9 @@
 'use strict';
 
 /**
+ * Hide or show properties for specific roles and users
+ * see src/doc/extension/display_of_properties.rst for documentation
+ *
  * @requires jQuery (library)
  * @requires log (singleton from loglevel library)
  * @requires load_config (function from webcaosdb.js)
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 9ae616daaf8ee79ab4b2a3600565ae540ae33f39..4b5bdf55e555a5755280b558770e4b2995731de7 100644
--- a/src/core/js/form_elements.js
+++ b/src/core/js/form_elements.js
@@ -1437,6 +1437,10 @@ var form_elements = new function () {
     /**
      * Return a select field.
      *
+     * IMPORTANT: The select picker has to be initialized by the client by
+     * calling ``form_elements.init_select_picker(ret, config.value)`` (see
+     * below and https://gitlab.com/caosdb/caosdb-webui/-/issues/208).
+     *
      * @param {SelectFieldConfig} config
      * @returns {HTMLElement} a select field.
      */
@@ -1454,7 +1458,7 @@ var form_elements = new function () {
         // case when this method is called and is controlled by the client. So
         // there is currently no other work-around than to call
         // init_select_picker after the form creation explicitely :(
-        //form_elements.init_select_picker(select[0], config.value);
+        // form_elements.init_select_picker(ret, config.value);
 
         return ret;
     }
@@ -1563,6 +1567,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
diff --git a/src/core/js/form_panel.js b/src/core/js/form_panel.js
index 9728a4ccea54c36d85399a3148373b7372108db0..a745f949f98d6219e377783d7ca6c854908df573 100644
--- a/src/core/js/form_panel.js
+++ b/src/core/js/form_panel.js
@@ -65,15 +65,33 @@ var form_panel = new function () {
     };
 
     /**
-     * Creates a callback function that toggles the form panel which
+     * Creates a callback function that creates the form inside 
+     * the form panel when called for the first time.
+     *
+     * You may supply 'undefined' as form_config and use form_creator
+     * instead which is supposed to be a function without argument that
+     * return the form to be added to the panel
      */
-    this.create_show_form_callback = function (panel_id, title, form_config) {
+    this.create_show_form_callback = function (
+        panel_id, title, form_config, form_creator=undefined
+    ) {
         return (e) => {
             logger.trace("enter show_form_panel", e);
 
             const panel = $(form_panel.get_form_panel(panel_id, title));
             if (panel.find("form").length === 0) {
-                const form = form_elements.make_form(form_config);
+                if (form_config != undefined && form_creator!=undefined){
+                    throw new Error("create_show_form_callback takes either a FormConfig or a function that creates the form");
+                }
+
+                var form;
+                if (form_config != undefined ){
+                    form = form_elements.make_form(form_config);
+                } else if (form_creator != undefined ){
+                    form = form_creator();
+                } else {
+                    throw new Error("create_show_form_callback takes a FormConfig or a function that creates the form");
+                }
                 panel.append(form);
                 $(form).find(".selectpicker").selectpicker();
 
diff --git a/src/core/js/query_form.js b/src/core/js/query_form.js
new file mode 100644
index 0000000000000000000000000000000000000000..6775fee677660b32387dcd82346753eb98c94da0
--- /dev/null
+++ b/src/core/js/query_form.js
@@ -0,0 +1,270 @@
+/**
+ * Extend the functionality of the pure html query panel.
+ *
+ * Deprecated. This is to be replaced by the query form of caosdb-webui-core-components.
+ *
+ * @deprecated
+ * @module queryForm
+ * @global
+ */
+var queryForm = (function () {
+  const init = function (form) {
+    queryForm.restoreLastQuery(form, () => window.sessionStorage.lastQuery);
+    queryForm.bindOnClick(form, (set) => {
+      window.sessionStorage.lastQuery = set;
+    });
+    const BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS = "";
+    queryForm.initFreeSearch(
+      form,
+      `${BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS}`
+    );
+  };
+
+  const logger = log.getLogger("queryForm");
+  var role_name_facet_select = undefined;
+
+  const _isCql = function (query) {
+    query = query.toUpperCase().trim();
+    return (
+      query.startsWith("FIND") ||
+      query.startsWith("COUNT") ||
+      query.startsWith("SELECT")
+    );
+  };
+
+  /**
+   * Initialize the free search to generate search queries which search only
+   * within one of several options of roles or entity names.
+   *
+   * E.g. when `options` is "Person, Experiment, Sample" the user can select
+   * one of these options before submitting the query. When the user types in
+   * something that doesn't looks like a CQL-query, a query is generated
+   * instead which goes like: FIND Persion WHICH HAS A PROPERTY LIKE
+   * "*something*".
+   *
+   * Note: This feature is disabled by default. Enable it by specifying the
+   * build variable BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS as a
+   * comma-separated list of **properly quoted** expressions, e.g.
+   * "FILE 'numpy array', 'Plant Experiemnt', 'quotes_not_necessary'".
+   * Otherwise, the server will throw a syntax error.
+   *
+   * @function initFreeSearch
+   * @param {HTMLElement} form - the form which will be initialized for the
+   *     free search.
+   * @param {string} options - comma-separated list of options.
+   * @return {boolean} - true if the initialization was successful, false
+   *     otherwise.
+   */
+  const initFreeSearch = function (form, options) {
+    const textArea = $(form).find(".caosdb-f-query-textarea");
+    logger.trace("initFreeSearch", form, textArea, options);
+    if (textArea.length > 0 && options && options != "") {
+      const lastQuery =
+        window.localStorage["freeTextQuery:" + textArea[0].value];
+      if (lastQuery) {
+        textArea[0].value = lastQuery;
+      }
+
+      const selected = window.localStorage["role_name_facet_option"];
+      const select = $(`<select class="btn btn-secondary"/>`);
+      for (let option of options.split(",")) {
+        select.append(
+          `<option ${
+            selected === option.trim() ? "selected" : ""
+          }>${option}</option>`
+        );
+      }
+      $(form).find(".input-group").prepend(select);
+      role_name_facet_select = select[0];
+
+      const switchFreeSearch = (text_area) => {
+        if (_isCql(text_area.value)) {
+          select.hide();
+          $(text_area).css({
+            "border-top-left-radius": "0.375rem",
+            "border-bottom-left-radius": "0.375rem",
+          });
+        } else {
+          select.show();
+          $(text_area).css({});
+        }
+      };
+
+      switchFreeSearch(textArea[0]);
+
+      textArea.on("keydown", (e) => {
+        switchFreeSearch(e.target);
+      });
+      return true;
+    }
+    role_name_facet_select = undefined;
+    return false;
+  };
+
+  /**
+   * @function restoreLastQuery
+   */
+  const restoreLastQuery = function (form, getter) {
+    if (form == null) {
+      throw new Error("form was null");
+    }
+    if (getter()) {
+      form.query.value = getter();
+    }
+  };
+
+  /**
+   * @function redirect
+   * @param {string} query - the query string.
+   * @param {string} paging - the paging string, e.g. 0L10.
+   */
+  const redirect = function (query, paging) {
+    var pagingparam = "";
+    if (paging && paging.length > 0) {
+      pagingparam = "P=" + paging + "&";
+    }
+    location.href =
+      connection.getBasePath() + "Entity/?" + pagingparam + "query=" + encodeURIComponent(query);
+  };
+
+  /**
+   * Read out the selector for the role/name facet of the query. Return
+   * "RECORD" if the selector is disabled.
+   * @function getRoleNameFacet
+   */
+  const getRoleNameFacet = function () {
+    if (role_name_facet_select) {
+      const result = role_name_facet_select.value;
+      window.localStorage["role_name_facet_option"] = result;
+      return result;
+    }
+    return "RECORD";
+  };
+
+  const _splitSearchTermsPattern =
+    /"(?<dq>[^"]*)" |'(?<sq>[^']*)' |(?<nq>[^ ]+)/g;
+
+  /**
+   * Split a query string into single terms.
+   *
+   * Terms are separated by white spaces. Terms which contain white spaces
+   * which are to be preserved must be enclosed in " or ' quotes. The
+   * enclosing quotation marks are being stripped. Currently no support for
+   * escape sequences for quotation marks.
+   *
+   * @function splitSearchTerms
+   * @param {string} query - complete query string.
+   * @return {string[]} array of the search terms.
+   */
+  const splitSearchTerms = function (query) {
+    // add empty space at the end, so every matching group ends with it -> easier regex. Also, undefined is filtered out
+    return Array.from(
+      (query + " ").matchAll(_splitSearchTermsPattern),
+      (m) => m[1] || m[2] || m[3]
+    ).filter((word) => word);
+  };
+
+  /**
+   * Is the query a SELECT field,... FROM entity query?
+   *
+   * @function isSelectQuery
+   * @param {HTMLElement} query, the query to be tested.
+   * @return {Boolean}
+   */
+  const isSelectQuery = function (query) {
+    return query.toUpperCase().startsWith("SELECT");
+  };
+
+  /**
+   * @function bindOnClick
+   */
+  const bindOnClick = function (form, setter) {
+    if (setter == null || typeof setter !== "function" || setter.length !== 1) {
+      throw new Error("setter must be a function with one param");
+    }
+
+    /*
+          Here a submit handler is created that is attached to both the form submit handler
+          and the click handler of the button.
+          See https://developer.mozilla.org/en-US/docs/Web/Events/submit why this is necessary.
+          */
+    var submithandler = function () {
+      // store current query
+      var queryField = form.query;
+      var value = queryField.value;
+      if (typeof value == "undefined" || value.length == 0) {
+        return;
+      }
+      if (!_isCql(value)) {
+        // split words in query field at space and create query fragments
+        var words = splitSearchTerms(queryField.value).map(
+          (word) => `A PROPERTY LIKE '*${word.replaceAll("'", `\\'`)}*'`
+        );
+        if (!words.length) {
+          return false;
+        }
+        const e = getRoleNameFacet();
+        const query_string = `FIND ${e} WHICH HAS ` + words.join(" AND ");
+        queryField.value = query_string;
+
+        // store original value of the text field
+        window.localStorage["freeTextQuery:" + query_string] = value;
+      }
+      setter(queryField.value);
+
+      var paging = "";
+      if (form.P && !isSelectQuery(queryField.value)) {
+        paging = form.P.value;
+      }
+
+      queryForm.redirect(queryField.value.trim(), paging);
+
+      var btn = $(form).find(".caosdb-search-btn");
+      btn.html(
+        `<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>`
+      );
+      btn.prop("disabled", true);
+
+      var textField = $(form).find(".caosdb-f-query-textarea");
+      textField.blur();
+      textField.prop("disabled", true);
+    };
+
+    // handler for the form
+    form.onsubmit = function (e) {
+      e.preventDefault();
+      submithandler();
+      return false;
+    };
+
+    // same handler for the button
+    form.getElementsByClassName("caosdb-search-btn")[0].onclick = function () {
+      submithandler();
+    };
+  };
+
+  /**
+   * @function getRoleNameFacetSelect
+   */
+
+  return {
+    init: init,
+    initFreeSearch: initFreeSearch,
+    isSelectQuery: isSelectQuery,
+    restoreLastQuery: restoreLastQuery,
+    redirect: redirect,
+    bindOnClick: bindOnClick,
+    splitSearchTerms: splitSearchTerms,
+    getRoleNameFacet: getRoleNameFacet,
+    getRoleNameFacetSelect: () => role_name_facet_select,
+  };
+})();
+
+$(document).ready(function () {
+  if (`${BUILD_MODULE_LEGACY_QUERY_FORM}` == "ENABLED") {
+    var form = document.getElementById("caosdb-query-form");
+    if (form != null) {
+      queryForm.init(form);
+    }
+  }
+});
diff --git a/src/core/js/webcaosdb.js b/src/core/js/webcaosdb.js
index 1f7d9991633aedd554a79000ad99f17b06189d73..1d6d5c8326d3f7dc0f311404f825206c9303462f 100644
--- a/src/core/js/webcaosdb.js
+++ b/src/core/js/webcaosdb.js
@@ -1200,242 +1200,6 @@ var paging = new function () {
     }
 };
 
-/**
- * Extend the functionality of the pure html query panel.
- *
- * @module queryForm
- * @global
- */
-var queryForm = function () {
-
-    const init = function (form) {
-        queryForm.restoreLastQuery(form, () => window.sessionStorage.lastQuery);
-        queryForm.bindOnClick(form, (set) => {
-            window.sessionStorage.lastQuery = set;
-        });
-        const BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS = "";
-        queryForm.initFreeSearch(form, `${BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS}`);
-    };
-
-    const logger = log.getLogger("queryForm");
-    var role_name_facet_select = undefined;
-
-    const _isCql = function (query) {
-       query = query.toUpperCase().trim();
-       return (query.startsWith("FIND") || query.startsWith("COUNT") || query.startsWith("SELECT"));
-    }
-
-    /**
-     * Initialize the free search to generate search queries which search only
-     * within one of several options of roles or entity names.
-     *
-     * E.g. when `options` is "Person, Experiment, Sample" the user can select
-     * one of these options before submitting the query. When the user types in
-     * something that doesn't looks like a CQL-query, a query is generated
-     * instead which goes like: FIND Persion WHICH HAS A PROPERTY LIKE
-     * "*something*".
-     *
-     * Note: This feature is disabled by default. Enable it by specifying the
-     * build variable BUILD_FREE_SEARCH_ROLE_NAME_FACET_OPTIONS as a
-     * comma-separated list of **properly quoted** expressions, e.g.
-     * "FILE 'numpy array', 'Plant Experiemnt', 'quotes_not_necessary'".
-     * Otherwise, the server will throw a syntax error.
-     *
-     * @function initFreeSearch
-     * @param {HTMLElement} form - the form which will be initialized for the
-     *     free search.
-     * @param {string} options - comma-separated list of options.
-     * @return {boolean} - true if the initialization was successful, false
-     *     otherwise.
-     */
-    const initFreeSearch = function (form, options) {
-        const textArea = $(form).find(".caosdb-f-query-textarea");
-        logger.trace("initFreeSearch", form, textArea, options);
-        if (textArea.length > 0 && options && options != "") {
-            const lastQuery = window.localStorage["freeTextQuery:" + textArea[0].value];
-            if (lastQuery) {
-                textArea[0].value = lastQuery;
-            }
-
-            const selected = window.localStorage["role_name_facet_option"];
-            const select = $(`<select class="btn btn-secondary"/>`);
-            for (let option of options.split(",")) {
-              select.append(`<option ${selected === option.trim() ? "selected" : ""}>${option}</option>`);
-            }
-            $(form).find(".input-group").prepend(select);
-            role_name_facet_select = select[0];
-
-            const switchFreeSearch = (text_area) => {
-                if(_isCql(text_area.value)) {
-                    select.hide();
-                    $(text_area).css({"border-top-left-radius": "0.375rem", "border-bottom-left-radius": "0.375rem"});
-                } else {
-                    select.show();
-                    $(text_area).css({});
-                }
-            }
-
-            switchFreeSearch(textArea[0]);
-
-            textArea.on("keydown", (e) => {
-                switchFreeSearch(e.target);
-            });
-            return true;
-        }
-        role_name_facet_select = undefined;
-        return false;
-    }
-
-    /**
-     * @function restoreLastQuery
-     */
-    const restoreLastQuery = function (form, getter) {
-        if (form == null) {
-            throw new Error("form was null");
-        }
-        if (getter()) {
-            form.query.value = getter();
-        }
-    };
-
-    /**
-     * @function redirect
-     * @param {string} query - the query string.
-     * @param {string} paging - the paging string, e.g. 0L10.
-     */
-    const redirect = function (query, paging) {
-        var pagingparam = ""
-        if (paging && paging.length > 0) {
-            pagingparam = "P=" + paging + "&";
-        }
-        location.href = connection.getBasePath() + "Entity/?" + pagingparam + "query=" + query;
-    }
-
-    /**
-     * Read out the selector for the role/name facet of the query. Return
-     * "RECORD" if the selector is disabled.
-     * @function getRoleNameFacet
-     */
-    const getRoleNameFacet = function () {
-        if (role_name_facet_select) {
-            const result = role_name_facet_select.value;
-            window.localStorage["role_name_facet_option"] = result;
-            return result;
-        }
-        return "RECORD";
-    }
-
-    const _splitSearchTermsPattern = /"(?<dq>[^"]*)" |'(?<sq>[^']*)' |(?<nq>[^ ]+)/g;
-
-    /**
-     * Split a query string into single terms.
-     *
-     * Terms are separated by white spaces. Terms which contain white spaces
-     * which are to be preserved must be enclosed in " or ' quotes. The
-     * enclosing quotation marks are being stripped. Currently no support for
-     * escape sequences for quotation marks.
-     *
-     * @function splitSearchTerms
-     * @param {string} query - complete query string.
-     * @return {string[]} array of the search terms.
-     */
-    const splitSearchTerms = function (query) {
-        // add empty space at the end, so every matching group ends with it -> easier regex. Also, undefined is filtered out
-        return Array.from((query + " ").matchAll(_splitSearchTermsPattern), (m) => m[1] || m[2] || m[3]).filter((word) => word);
-    }
-
-    /**
-     * Is the query a SELECT field,... FROM entity query?
-     *
-     * @function isSelectQuery
-     * @param {HTMLElement} query, the query to be tested.
-     * @return {Boolean}
-     */
-    const isSelectQuery = function (query) {
-        return query.toUpperCase().startsWith("SELECT");
-    }
-
-    /**
-     * @function bindOnClick
-     */
-    const bindOnClick = function (form, setter) {
-        if (setter == null || typeof (setter) !== 'function' || setter.length !== 1) {
-            throw new Error("setter must be a function with one param");
-        }
-
-        /*
-          Here a submit handler is created that is attached to both the form submit handler
-          and the click handler of the button.
-          See https://developer.mozilla.org/en-US/docs/Web/Events/submit why this is necessary.
-          */
-        var submithandler = function () {
-            // store current query
-            var queryField = form.query;
-            var value = queryField.value;
-            if (typeof value == "undefined" || value.length == 0) {
-                return;
-            }
-            if (!_isCql(value)) {
-                // split words in query field at space and create query fragments
-                var words = splitSearchTerms(queryField.value).map(word => `A PROPERTY LIKE '*${word.replaceAll("'", `\\'`)}*'`);
-                if (!words.length) {
-                    return false;
-                }
-                const e = getRoleNameFacet();
-                const query_string = `FIND ${e} WHICH HAS ` + words.join(" AND ");
-                queryField.value = query_string;
-
-                // store original value of the text field
-                window.localStorage["freeTextQuery:" + query_string] = value;
-            }
-            setter(queryField.value);
-
-            var paging = "";
-            if (form.P && !isSelectQuery(queryField.value)) {
-                paging = form.P.value
-            }
-
-            queryForm.redirect(queryField.value.trim(), paging);
-
-            var btn =  $(form).find(".caosdb-search-btn");
-            btn.html(`<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>`);
-            btn.prop("disabled", true);
-
-            var textField =  $(form).find(".caosdb-f-query-textarea");
-            textField.blur();
-            textField.prop("disabled", true);
-        };
-
-        // handler for the form
-        form.onsubmit = function (e) {
-            e.preventDefault();
-            submithandler();
-            return false;
-        };
-
-        // same handler for the button
-        form.getElementsByClassName("caosdb-search-btn")[0].onclick = function () {
-            submithandler();
-        };
-    };
-
-    /**
-     * @function getRoleNameFacetSelect
-     */
-
-    return {
-        init: init,
-        initFreeSearch: initFreeSearch,
-        isSelectQuery: isSelectQuery,
-        restoreLastQuery: restoreLastQuery,
-        redirect: redirect,
-        bindOnClick: bindOnClick,
-        splitSearchTerms: splitSearchTerms,
-        getRoleNameFacet: getRoleNameFacet,
-        getRoleNameFacetSelect: () => role_name_facet_select,
-    }
-}();
-
 
 /*
  * Small module containing only a converter from markdown to html.
@@ -1912,12 +1676,6 @@ function initOnDocumentReady() {
     paging.init();
     hintMessages.init();
 
-    // init query form
-    var form = document.getElementById("caosdb-query-form");
-    if (form != null) {
-        queryForm.init(form);
-    }
-
     // show image 100% width
     $(".entity-image-preview").click(function () {
         $(this).css('max-width', '100%');
diff --git a/src/core/xsl/entity.xsl b/src/core/xsl/entity.xsl
index 8202ccea2a40d304943556612de40b00931f9648..85c6f0b4944872ef270a8d64089af720514e1b06 100644
--- a/src/core/xsl/entity.xsl
+++ b/src/core/xsl/entity.xsl
@@ -387,6 +387,9 @@
             <xsl:with-param name="boolean">
               <xsl:value-of select="'false'"/>
             </xsl:with-param>
+            <xsl:with-param name="datetime">
+              <xsl:value-of select="'false'"/>
+            </xsl:with-param>
           </xsl:call-template>
         </xsl:for-each>
         <xsl:for-each select="Record|RecordType|File|Property">
@@ -400,6 +403,9 @@
             <xsl:with-param name="boolean">
               <xsl:value-of select="'false'"/>
             </xsl:with-param>
+            <xsl:with-param name="datetime">
+              <xsl:value-of select="'false'"/>
+            </xsl:with-param>
           </xsl:call-template>
         </xsl:for-each>
       </xsl:element>
diff --git a/src/core/xsl/navbar.xsl b/src/core/xsl/navbar.xsl
index 7c1d8bc897eb1e51344d54cfa81a62e4328b0467..1817df4056bc98f79c7a26a7540383ee8898e400 100644
--- a/src/core/xsl/navbar.xsl
+++ b/src/core/xsl/navbar.xsl
@@ -51,7 +51,7 @@
     <nav class="navbar navbar-expand-lg navbar-light bg-light sticky-top mb-2 flex-column" id="caosdb-navbar-full">
       <noscript>Please enable JavaScript!</noscript>
       <div class="container-fluid">
-        <a class="navbar-brand">
+        <a class="navbar-brand d-lg-none d-inline">
           <xsl:attribute name="href">
             <xsl:value-of select="$basepath"/>
           </xsl:attribute>
@@ -69,6 +69,20 @@
           <span class="navbar-toggler-icon"></span>
         </button>
         <div class="collapse navbar-collapse" id="top-navbar">
+          <a class="navbar-brand d-none d-lg-inline">
+            <xsl:attribute name="href">
+              <xsl:value-of select="$basepath"/>
+            </xsl:attribute>
+            <xsl:element name="img">
+              <xsl:if test="'${BUILD_NAVBAR_BRAND_NAME}' != ''">
+                <xsl:attribute name="class">caosdb-logo</xsl:attribute>
+              </xsl:if>
+              <xsl:attribute name="src">
+                  <xsl:value-of select="concat($basepath,'webinterface/${BUILD_NUMBER}/${BUILD_NAVBAR_LOGO}')"/>
+              </xsl:attribute>
+            </xsl:element>
+            ${BUILD_NAVBAR_BRAND_NAME}
+          </a>
           <ul class="navbar-nav caosdb-navbar me-auto">
             <li class="nav-item dropdown" id="caosdb-navbar-entities">
               <a class="nav-link dropdown-toggle" role="button" id="navbarEntitiesMenuLink" data-bs-toggle="dropdown" aria-expanded="false" href="#">
diff --git a/src/doc/conf.py b/src/doc/conf.py
index 0b092979a7dc8c501a439a280d179491e94db6fc..fa77c9eef6e4d93d491e17473186a880bfcc0d20 100644
--- a/src/doc/conf.py
+++ b/src/doc/conf.py
@@ -26,9 +26,9 @@ copyright = '2022, IndiScale GmbH'
 author = 'Daniel Hornung'
 
 # The short X.Y version
-version = '0.11.0'
+version = '0.11.1'
 # The full version, including alpha/beta/rc tags
-release = '0.11.0'
+release = '0.11.1'
 
 
 # -- General configuration ---------------------------------------------------
diff --git a/test/core/js/modules/ext_cosmetics.js.js b/test/core/js/modules/ext_cosmetics.js.js
index 969c8297b8b5cf85d0a668d7c30e8b0f45e34d4d..f51226ee01a1607c9ba1580c3760500fdec760b1 100644
--- a/test/core/js/modules/ext_cosmetics.js.js
+++ b/test/core/js/modules/ext_cosmetics.js.js
@@ -1,8 +1,9 @@
 /*
  * This file is a part of the CaosDB Project.
  *
- * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021-2023 IndiScale GmbH <info@indiscale.com>
  * Copyright (C) 2021 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2023 Daniel Hornung <d.hornung@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -36,6 +37,30 @@ QUnit.module("ext_cosmetics.js", {
     }
 });
 
+QUnit.test("custom datetime", function (assert) {
+    assert.ok(cosmetics.custom_datetime, "custom_datetime available");
+    var test_cases = [
+        ["1234-56-78", "1234-56-78"],
+        ["9876-54-32T12:34:56", "9876-54-32 12:34:56"],
+        ["2023-03-78T99:99:99+0800", "2023-03-78 99:99:99"],
+    ];
+
+    for (let test_case of test_cases) {
+        const container = $('<div></div>');
+        $(document.body).append(container);
+        const text_value = $(`<span class="caosdb-f-property-datetime-value">${test_case[0]}</span>`);
+        container.append(text_value);
+        assert.equal($(container).find(" ").length, 0, "Test original datetime.");
+      cosmetics.custom_datetime();
+      const newValueElement =
+            container[0].querySelector("span.caosdb-v-property-datetime-customized-newvalue");
+      assert.ok(newValueElement, "Datetime customization: Test if result exists.");
+      assert.equal(newValueElement.innerHTML, test_case[1],
+                   "Datetime customization: compared result.");
+        container.remove();
+    }
+});
+
 QUnit.test("linkify - https", function (assert) {
     assert.ok(cosmetics.linkify, "linkify available");
     var test_cases = [
diff --git a/test/core/js/modules/form_panel.js.js b/test/core/js/modules/form_panel.js.js
index be4c4ad8e66402adedf8c386ff086be4d7cb7955..a810fb8711f14bc3b3637297c9f937884022fc15 100644
--- a/test/core/js/modules/form_panel.js.js
+++ b/test/core/js/modules/form_panel.js.js
@@ -55,9 +55,18 @@ QUnit.test("create_show_form_callback ", function (assert) {
             help: help_text,
         }, ],
     };
-    const cb = form_panel.create_show_form_callback( panel_id, title, csv_form_config);
+    const cb = form_panel.create_show_form_callback(panel_id, title, csv_form_config);
     assert.equal(typeof cb, "function", "function created");
     cb()
+
+    const creator = function() {
+        return form_elements.make_form(csv_form_config);
+    }
+    const cb2 = form_panel.create_show_form_callback(
+        panel_id, title, undefined, creator
+    );
+    assert.equal(typeof cb2, "function", "function created");
+    cb2()
 });