diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae5c058e15f82a98df92e6822efe174fe54edece..085bef71c307588c0a2826685d9b2da88549c4ca 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,20 +4,71 @@ 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).
 
+## [Unreleased]
+
+### Added (for new features, dependecies etc.)
+
+* Documentation link in standard footer
+* Build properties for footer elements:
+  * `BUILD_FOOTER_CONTACT_HREF`
+  * `BUILD_FOOTER_IMPRINT_HREF`
+  * `BUILD_FOOTER_DOCS_HREF`
+  * `BUILD_FOOTER_SOURCES_HREF`
+  * `BUILD_FOOTER_LICENCE_HREF`
+  See `build.properties.d/00_default.properties` for more information
+* Start editing an entity/creating a new record directly by adding an `#edit` or
+  `#new_record` URI fragment, respectively.
+* Optional WYSIWYG editor with markdown output for text properties. Controled by
+  the `BUILD_MODULE_EXT_EDITMODE_WYSIWYG_TEXT` build variable which is set do
+  `DISABLED` by default.
+  - Added button to version history panel that allows restoring old versions
+
+### Changed (for changes in existing functionality)
+
+* Default footer elements contain invalid links for imprint, contact, and data-policy now
+
+### Deprecated (for soon-to-be removed features)
+
+### Removed (for now removed features)
+
+* Build property `BUILD_CUSTOM_IMPRINT`. Please use BUILD_FOOTER_IMPRINT_HREF
+  and link a document instead. You can always put a html page to
+  `src/ext/html/` and link to that.
+
+### Fixed (for any bug fixes)
+- #156
+- #251
+
+### Security (in case of vulnerabilities)
+
+### Documentation (for notable additions or changes of the documentation)
+
+
 ## [0.4.1] - 2021-11-04
 
 ### Added (for new features, dependecies etc.)
 
 * `form_panel` module for conveniently creating a panel for web forms.
+* `restore_old_version` function to base functionality (caosdb.js)
+* buttons to the version history modal that allow restoring older versions
 
 ### Changed (for changes in existing functionality)
 
+* Default footer elements contain invalid links for imprint, contact, and data-policy now
+
 ### Deprecated (for soon-to-be removed features)
 
 ### Removed (for now removed features)
 
+* Build property `BUILD_CUSTOM_IMPRINT`. Please use BUILD_FOOTER_IMPRINT_HREF
+  and link a document instead. You can always put a html page to
+  `src/ext/html/` and link to that.
+
 ### Fixed (for any bug fixes)
 
+* Auto-completion and edit_mode can handle entity names with empty spaces better now
+* [#251](https://gitlab.indiscale.com/caosdb/src/caosdb-webui/-/issues/251) - Data loss when editing Entities with URL-like properties
+
 ### Security (in case of vulnerabilities)
 
 ### Documentation (for notable additions or changes of the documentation)
diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md
index c6043d09d049b91cfc0a03560970537feb8bbc06..f3e1d1706dd8dd9bdbdab16377c2c75ed0299f7f 100644
--- a/DEPENDENCIES.md
+++ b/DEPENDENCIES.md
@@ -24,5 +24,12 @@
 * proj4js-2.5.0
 * Proj4Leaflet-1.0.1
 
+## For CKEditor (WYSIWYG editor in edit mode)
+
+* we're using a custom-built ckeditor 31.0.0 from
+  https://ckeditor.com/ckeditor-5/online-builder/ with a customized set of
+  editor plugins. Please refer to the `package.json` within
+  `libs/ckeditor...zip` for a full list of said plugins.
+
 ## For testing
 * qunit-2.9.2
diff --git a/Makefile b/Makefile
index 4c64a38d01f0273059e29f6dac0448967b005b86..5cca686319c617006007e4884cfb269796ac7af4 100644
--- a/Makefile
+++ b/Makefile
@@ -43,7 +43,7 @@ LIBS_DIR = $(abspath libs)
 TEST_CORE_DIR = $(abspath test/core/)
 TEST_EXT_DIR = $(abspath test/ext)
 TEST_SSS_DIR =$(abspath test/server_side_scripting)
-LIBS = fonts css/fonts css/bootstrap.css css/bootstrap-icons.css js/state-machine.js js/jquery.js js/showdown.js js/dropzone.js css/dropzone.css js/loglevel.js js/leaflet.js css/leaflet.css css/images js/leaflet-latlng-graticule.js js/proj4.js js/proj4leaflet.js js/leaflet-coordinates.js css/leaflet-coordinates.css js/leaflet-graticule.js js/bootstrap-select.js css/bootstrap-select.css js/bootstrap-autocomplete.min.js js/plotly.js js/pako.js js/utif.js js/bootstrap.js js/qrcode.js
+LIBS = fonts css/fonts css/bootstrap.css css/bootstrap-icons.css js/state-machine.js js/jquery.js js/showdown.js js/dropzone.js css/dropzone.css js/loglevel.js js/leaflet.js css/leaflet.css css/images js/leaflet-latlng-graticule.js js/proj4.js js/proj4leaflet.js js/leaflet-coordinates.js css/leaflet-coordinates.css js/leaflet-graticule.js js/bootstrap-select.js css/bootstrap-select.css js/bootstrap-autocomplete.min.js js/plotly.js js/pako.js js/utif.js js/bootstrap.js js/qrcode.js js/ckeditor.js
 
 
 TEST_LIBS = $(LIBS) js/qunit.js css/qunit.css $(subst $(TEST_CORE_DIR)/,,$(shell find $(TEST_CORE_DIR)/))
@@ -73,7 +73,7 @@ merge_js:
 	JS_DIST_BUNDLE=$${JS_DIST_BUNDLE} AUTO_DISCOVER_MODULES=$$AUTO_DISCOVER_MODULES misc/merge_js.sh $${MODULE_DEPENDENCIES[*]}
 
 EXCLUDE_EXPR = %~ %.backup
-BUILDFILELIST = $(filter-out $(EXCLUDE_EXPR),$(wildcard build.properties.d/*))
+BUILDFILELIST = $(sort $(filter-out $(EXCLUDE_EXPR),$(wildcard build.properties.d/*)))
 build_properties:
 	@set -a -e ; \
 	pushd build.properties.files ; \
@@ -176,6 +176,9 @@ cp-ext:
 	for f in $(wildcard $(SRC_EXT_DIR)/xsl/*) ; do \
 		echo "y" | $(CMD_COPY_EXT_FILES) "$$(realpath "$$f")" $(PUBLIC_DIR)/xsl/ ; \
 	done
+	for f in $(wildcard $(SRC_EXT_DIR)/include/*) ; do \
+		echo "y" | $(CMD_COPY_EXT_FILES) "$$(realpath "$$f")" "$(PUBLIC_DIR)/$$(basename "$$f")" ; \
+	done
 
 cp-ext-test:
 	for f in $(wildcard $(TEST_EXT_DIR)/js/*) ; do \
@@ -302,6 +305,9 @@ $(LIBS_DIR)/js/utif.js: unzip $(LIBS_DIR)/js
 $(LIBS_DIR)/js/qrcode.js: unzip $(LIBS_DIR)/js
 	ln -s $(LIBS_DIR)/qrcode-1.4.4/qrcode.min.js $@
 
+$(LIBS_DIR)/js/ckeditor.js: unzip $(LIBS_DIR)/js
+	ln -s $(LIBS_DIR)/ckeditor5-31.0.0-k356w86hp13l/build/ckeditor.js $@
+
 
 $(addprefix $(LIBS_DIR)/, js css):
 	mkdir $@ || true
diff --git a/build.properties.d/00_default.properties b/build.properties.d/00_default.properties
index df68f1012cd71b49538b073a4c9d17e85b2214e4..535a6c846a5c39c48937dee43f087501f43285ae 100644
--- a/build.properties.d/00_default.properties
+++ b/build.properties.d/00_default.properties
@@ -60,6 +60,8 @@ BUILD_MODULE_USER_MANAGEMENT_CHANGE_OWN_PASSWORD_REALM=CaosDB
 BUILD_MODULE_EXT_RESOLVE_REFERENCES=ENABLED
 BUILD_EXT_REFERENCES_CUSTOM_RESOLVER=person_reference
 
+BUILD_MODULE_EXT_EDITMODE_WYSIWYG_TEXT=DISABLED
+
 ##############################################################################
 # Navbar properties
 ##############################################################################
@@ -77,7 +79,24 @@ BUILD_FAVICON=pics/caosdb_logo_42.png
 ##############################################################################
 
 # Link to the data policy statement document.
-BUILD_FOOTER_DATA_POLICY_HREF=https://missing-domain.com/missing-page
+BUILD_FOOTER_DATA_POLICY_HREF="Please configure me!"
+
+# Contact mail or link to contact information for the responsible administrator of this caosdb server instance.
+BUILD_FOOTER_CONTACT_HREF="Please configure me!"
+#BUILD_FOOTER_CONTACT_HREF=mailto:info@indiscale.com
+
+# Link to imprint for this caosdb server instance.
+BUILD_FOOTER_IMPRINT_HREF="Please configure me!"
+#BUILD_FOOTER_IMPRINT_HREF=https://www.indiscale.com/imprint/
+
+# Link to docs (should rarely be changed (maybe for additional docs))
+BUILD_FOOTER_DOCS_HREF="https://docs.indiscale.com"
+
+# Link to sources (should almost never be changed (maybe for additional sources))
+BUILD_FOOTER_SOURCES_HREF="https://gitlab.com/caosdb"
+
+# Link to license (should almost never be changed (maybe for other languages))
+BUILD_FOOTER_LICENCE_HREF="https://www.gnu.org/licenses/agpl-3.0.en.html"
 
 # Custom footer elements can be placed here (will be placed inside a <div>
 # element).
@@ -90,7 +109,6 @@ BUILD_FOOTER_CUSTOM_ELEMENT_ONE=
 # ${BUILD_NUMBER}.
 BUILD_FOOTER_CUSTOM_ELEMENT_TWO=
 # BUILD_FOOTER_CUSTOM_ELEMENT_TWO=$(cat footer_element_2.html)
-BUILD_CUSTOM_IMPRINT='<p> Put an imprint note here </p>'
 
 ##############################################################################
 # ext_trigger_crawler_form properties
@@ -157,4 +175,6 @@ MODULE_DEPENDENCIES=(
     qrcode.js
     ext_qrcode.js
     form_panel.js
+    ckeditor.js
+    ext_editmode_wysiwyg_text.js
 )
diff --git a/libs/ckeditor5-31.0.0-k356w86hp13l.zip b/libs/ckeditor5-31.0.0-k356w86hp13l.zip
new file mode 100644
index 0000000000000000000000000000000000000000..71523482a2bdfa7c0a9776eeed7aa7be010e053b
Binary files /dev/null and b/libs/ckeditor5-31.0.0-k356w86hp13l.zip differ
diff --git a/misc/entity_state_test_data.py b/misc/entity_state_test_data.py
index 72ffe278d0ad90cb5223966bcd52c79cd420f160..400b73749011834fb5390be2e00eebdde1a91062 100755
--- a/misc/entity_state_test_data.py
+++ b/misc/entity_state_test_data.py
@@ -153,9 +153,8 @@ def setup_state_model():
         "Transition").add_property("from", "under review").add_property("to", "unpublished").insert()
 
     # 1->1
-    db.Record("Edit").add_parent(
-        "Transition",
-        description="Edit this entity. The changes are not publicly available until this entity will have been reviewed and published.").add_property(
+    db.Record("Edit", description="Edit this entity. The changes are not publicly available until this entity will have been reviewed and published.").add_parent(
+        "Transition").add_property(
         "from",
         "unpublished").add_property(
             "to",
diff --git a/src/core/js/caosdb.js b/src/core/js/caosdb.js
index 90d6bfd23b989bbff133a63d65e1e68d99f4b3bb..7c3c4300f971eae16d30748e6787843334708baa 100644
--- a/src/core/js/caosdb.js
+++ b/src/core/js/caosdb.js
@@ -1071,7 +1071,7 @@ function createFileXML(name, id, parents,
  * Update, Response, Delete.
  *
  * @param {string} root - The name of the newly created document root node.
- * @param {(Document|XMLDocumentFragment)} xmls The xml documents.
+ * @param {Document[]|XMLDocumentFragment[]} xmls The xml documents.
  * @return {Document} A new xml document.
  */
 function wrapXML(root, xmls) {
@@ -1164,6 +1164,36 @@ async function update(xml) {
     return await transaction.updateEntitiesXml(wrapped);
 }
 
+
+/**
+ * Restore an old version of an entity using an xml representation.
+ * First, the old version is retrieved and the current version is set to the
+ * old one.
+ * @param versionid The version id (e.g. 123@abbabbaeff23322) of the version of
+ * the entity which shall be restored.
+ */
+async function restore_old_version(versionid){
+    // retrieve entity
+    var ent = await transaction.retrieveEntityById(versionid);
+    if (ent === undefined){
+        throw new Error(`Entity with version id ${versionid} could not be retrieved.`);
+    }
+    // remove unwanted tags (Version and Permissions)
+    ent.getElementsByTagName("Version")[0].remove();
+    var permissions = ent.getElementsByTagName("Permissions");
+    for (let i = permissions.length-1; i >=0 ; i--) {
+        permissions[i].remove();
+    }
+
+    // use XML to update entity/restore old version
+    const doc = _createDocument("Request");
+    doc.firstElementChild.appendChild(ent);
+    reps = await transaction.updateEntitiesXml(doc);
+    if (reps.getElementsByTagName("Error").length>0) {
+        throw new Error(`Could not restore the Entity to the version ${versionid}.`);
+    }
+}
+
 /**
  * Insert an entity in xml representation.
  *
diff --git a/src/core/js/edit_mode.js b/src/core/js/edit_mode.js
index 85645aabaf7d05cb7abe928b4792b5de6ef9e352..87e588aab11a681e49ff4e6596a81f4037628316 100644
--- a/src/core/js/edit_mode.js
+++ b/src/core/js/edit_mode.js
@@ -76,11 +76,52 @@ var edit_mode = new function () {
         }
     }
 
+    this.has_edit_fragment = function () {
+        const fragment = window.location.hash.substr(1);
+        return fragment === "edit";
+    }
+
+    this.has_new_record_fragment = function () {
+        const fragment = window.location.hash.substr(1);
+        return fragment === "new_record";
+    }
+
     this._init = function () {
         var target = $("#top-navbar").find("ul").first();
         this.add_edit_mode_button(target, edit_mode.toggle_edit_mode);
+
+        var after_setup_callback = () => {} // do nothing
+        if (this.has_edit_fragment()) {
+            // find first entity
+            const first_entity = $(".caosdb-entity-panel")[0];
+            if (first_entity) {
+                window.localStorage["edit_mode"] = true;
+                after_setup_callback = () => {
+                    logger.debug("Edit this entity after #edit in the uri", first_entity);
+                    edit_mode.edit(first_entity);
+                }
+            }
+        } else if (this.has_new_record_fragment()) {
+            for (let entity of $(".caosdb-entity-panel")) {
+                // find first record type
+                if (getEntityRole(entity) === "RecordType") {
+                    window.localStorage["edit_mode"] = true;
+                    const new_record = edit_mode.create_new_record(getEntityID(entity));
+                    after_setup_callback = () => {
+                        logger.debug("Create a new record after #new_record in the uri", entity);
+                        new_record.then((new_record) => {
+                            edit_mode.app.newEntity(new_record);
+                        }, edit_mode.handle_error);
+                    }
+                    break;
+                }
+            }
+        }
+
+        // intialize the edit mode panel and add all necessary buttons if the edit mode is 
         if (this.is_edit_mode()) {
-            edit_mode.enter_edit_mode();
+
+            edit_mode.enter_edit_mode().then(after_setup_callback);
             edit_mode.toggle_edit_panel();
             // This is for the very specific case of reloading the
             // page while the edit mode is active on small screens
@@ -88,6 +129,9 @@ var edit_mode = new function () {
             $(".caosdb-edit-min-width-warning").addClass("d-block");
         }
         $('.caosdb-f-edit').css("transition", "top 1s");
+
+        // add drag-n-drop listener (needed for the edit_mode toolbox).
+        edit_mode.init_dragable();
     }
 
 
@@ -444,9 +488,6 @@ var edit_mode = new function () {
             file_path = getEntityPath(entity_form);
             file_checksum = getEntityChecksum(entity_form);
             file_size = getEntitySize(entity_form);
-            console.log(file_path);
-            console.log(file_checksum);
-            console.log(file_size);
         }
         return createEntityXML(
             entityRole,
@@ -580,7 +621,6 @@ var edit_mode = new function () {
             $(".caosdb-f-btn-toggle-edit-mode").text("Leave Edit Mode");
 
             edit_mode.init_tool_box();
-            edit_mode.init_dragable();
 
             var nextEditApp = editApp;
             if (typeof nextEditApp == "undefined") {
@@ -639,6 +679,7 @@ var edit_mode = new function () {
      * the model given by model.
      */
     this.init_tool_box = async function () {
+
         // remove previously added model
         $(".caosdb-f-edit-mode-existing").remove()
 
@@ -649,6 +690,12 @@ var edit_mode = new function () {
 
         removeAllWaitingNotifications(editPanel[0]);
         editPanel.children()[0].appendChild(model);
+
+        if (edit_mode.app && edit_mode.app.entity && edit_mode.app.entity.parentElement && (edit_mode.app.state === "changed")) {
+            // an entity is being editted
+            $(".caosdb-f-edit-mode-existing").toggleClass("d-none", false);
+            $(".caosdb-f-edit-mode-create-buttons").toggleClass("d-none", true);
+        }
     }
 
 
@@ -1485,6 +1532,7 @@ var edit_mode = new function () {
             init_drag_n_drop();
         }
         app.onEnterInitial = async function (e) {
+
             $(".caosdb-f-edit-mode-existing").toggleClass("d-none", true);
             $(".caosdb-f-edit-mode-create-buttons").toggleClass("d-none", false);
             app.old = undefined;
@@ -1559,6 +1607,7 @@ var edit_mode = new function () {
             }, edit_mode.handle_error);
         };
         app.onEnterChanged = function (e) {
+
             // show existing entities in toolbox
             $(".caosdb-f-edit-mode-existing").toggleClass("d-none", false);
             $(".caosdb-f-edit-mode-create-buttons").toggleClass("d-none", true);
@@ -1755,7 +1804,7 @@ var edit_mode = new function () {
      *     which can be referenced by the property.
      */
     this.retrieve_datatype_list = async function (datatype) {
-        var find_entity = ["FILE", "REFERENCE"].includes(datatype) ? "" : datatype;
+        var find_entity = ["FILE", "REFERENCE"].includes(datatype) ? "" : `"${datatype}"`;
         var entities = datatype !== "FILE" ? await edit_mode.query(`FIND Record ${find_entity}`) : [];
         var files = await edit_mode.query(`FIND File ${find_entity}`);
 
diff --git a/src/core/js/ext_autocomplete.js b/src/core/js/ext_autocomplete.js
index 9a639fb4387a32bad128406bb7bd1f036bed5fda..2f6fa0dde729907f7248bd05367f26633872f39e 100644
--- a/src/core/js/ext_autocomplete.js
+++ b/src/core/js/ext_autocomplete.js
@@ -158,8 +158,16 @@ var ext_autocomplete = new function () {
         var start = newValue.slice(0, beginning_of_word + 1);
         var end = origJQElement[0].value.slice(cursorpos);
         var result = resultsFromServer.map(x => {
+            var x_quoted = x;
+            if (x.indexOf(" ") > -1) {
+              if(x.indexOf("\"") > -1) {
+                x_quoted = `'${x}'`;
+              } else {
+                x_quoted = `"${x}"`;
+              }
+            }
             return {
-                text: start + x + end,
+                text: start + x_quoted + end,
                 html: x
             }
         });
@@ -196,4 +204,4 @@ $(document).ready(function () {
     if ("${BUILD_MODULE_EXT_AUTOCOMPLETE}" == "ENABLED") {
         caosdb_modules.register(ext_autocomplete);
     }
-});
\ No newline at end of file
+});
diff --git a/src/core/js/ext_cosmetics.js b/src/core/js/ext_cosmetics.js
index f4f281123b39a87b7ef6848db4e84a81b5e30d9c..77556437394df6a6763661ce5c0d5001f68ce61a 100644
--- a/src/core/js/ext_cosmetics.js
+++ b/src/core/js/ext_cosmetics.js
@@ -47,7 +47,8 @@ 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>`;
                 });
 
-                $(this).html(result);
+                $(this).hide();
+                $(this).after(result);
             }
         });
     }
diff --git a/src/core/js/ext_editmode_wysiwyg_text.js b/src/core/js/ext_editmode_wysiwyg_text.js
new file mode 100644
index 0000000000000000000000000000000000000000..380d8b1ec298ed0fa5c68af50b8b0e9dad333b89
--- /dev/null
+++ b/src/core/js/ext_editmode_wysiwyg_text.js
@@ -0,0 +1,118 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2021 Florian Spreckelsen <f.spreckelsen@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
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+"use strict";
+
+/**
+ * Replaces textareas in the edit mode by a wysiwyg editor
+ *
+ * @module ext_editmode_wysiwyg_text
+ * @version 0.1
+ *
+ * @param jQuery - well-known library.
+ * @param log - singleton from loglevel library or javascript console.
+ * @param {class} ClassicEditor - ClassicEditor class from ckEditor
+ * @param {module} edit_mode - caosdb's edit-mode module
+ * @param {function} getPropertyElements - caosdb's function to extract the
+ *     property HTML elements from an entity HTML element
+ * @param {function} getPropertyDatatype - caosdb's function to extract the
+ *     data type from a property HTML element
+ * @param {function} getPropertyName - caosdb's function to extract the
+ *     name from a property HTML element
+ */
+var ext_editmode_wysiwyg_text = function ($, logger, ClassicEditor, edit_mode, getPropertyElements, getPropertyDatatype, getPropertyName) {
+
+    var insertEditorInProperty = async function (prop) {
+        if (!(getPropertyDatatype(prop) === 'TEXT')) {
+            // Ignore anything that isn't a list property, even LIST<TEXT>
+            return;
+        }
+
+        try{
+            const editor = await ClassicEditor
+                .create(prop.querySelector('textarea'), {
+                    // use all plugins since we built the editor dependency to
+                    // contain only those we need.
+                    plugins: ClassicEditor.builtinPlugins,
+                    // Markdown needs a header row so enforce this
+                    table: {
+                        defaultHeadings: {
+                            rows: 1,
+                            columns: 0
+                        },
+                    },
+                })
+            logger.debug('Initialized editor for ' + getPropertyName(prop));
+            // Manually implement saving the data since edit mode is not
+            // a form to be submitted.
+            editor.model.document.on("change:data", (e) => {
+                editor.updateSourceElement();
+            });
+        } catch(error) {
+            logger.error(error.stack);
+        }
+    }
+
+    var replaceTextAreas = function (entity) {
+        const properties = getPropertyElements(entity);
+        for (let prop of properties) {
+            // TODO(fspreck): This will be replaced by a whitelist of properties
+            // in the future.
+            if (getPropertyName(prop)) {
+                insertEditorInProperty(prop);
+            }
+        }
+    }
+
+    var init = function () {
+
+        // Insert an editor into all TEXT properties of the entity which is
+        // being edited.
+        document.body.addEventListener(edit_mode.start_edit.type, (e) => {
+            logger.debug('Replacing text areas ...');
+            ext_editmode_wysiwyg_text.replaceTextAreas(e.target);
+        }, true);
+
+        // Listen to added properties and replace the textarea if necessary
+        document.body.addEventListener(edit_mode.property_added.type, (e) => {
+            logger.debug('Replacing textarea in ' + getPropertyName(e.target));
+            ext_editmode_wysiwyg_text.insertEditorInProperty(e.target);
+        }, true)
+
+        // Listen to properties, the data type of which has changed. Mainly
+        // because of change from list to scalar and vice versa.
+        document.body.addEventListener(edit_mode.property_data_type_changed.type, (e) => {
+            logger.debug('Re-rendering ' + getPropertyName(e.target));
+            ext_editmode_wysiwyg_text.insertEditorInProperty(e.target);
+        }, true);
+    };
+
+    return {
+        init: init,
+        replaceTextAreas: replaceTextAreas,
+        insertEditorInProperty: insertEditorInProperty,
+    };
+}($, log.getLogger("ext_editmode_wysiwyg_text"), ClassicEditor, edit_mode, getPropertyElements, getPropertyDatatype, getPropertyName);
+
+$(document).ready(() => {
+    if ("${BUILD_MODULE_EXT_EDITMODE_WYSIWYG_TEXT}" == "ENABLED") {
+        caosdb_modules.register(ext_editmode_wysiwyg_text);
+    }
+});
diff --git a/src/core/js/form_panel.js b/src/core/js/form_panel.js
index dabb28c9d9dad79a3768fde7552d483bdd3bf570..9728a4ccea54c36d85399a3148373b7372108db0 100644
--- a/src/core/js/form_panel.js
+++ b/src/core/js/form_panel.js
@@ -47,7 +47,7 @@ var form_panel = new function () {
         if (existing.length > 0) {
             return existing[0];
         }
-        const panel = $('<div id="' + panel_id + '" class="caosdb-f-form-panel bg-light container"/>');
+        const panel = $('<div id="' + panel_id + '" class="caosdb-f-form-panel bg-light container mb-1"/>');
         const header = $('<h2 class="text-center">' + title + '</h2>');
         panel.append(header);
 
diff --git a/src/core/js/webcaosdb.js b/src/core/js/webcaosdb.js
index efa28c9b39921df3e02a25ec95d4601f752fa288..03cc7bfbb399e898a88cb99dc0a05cc90289f96a 100644
--- a/src/core/js/webcaosdb.js
+++ b/src/core/js/webcaosdb.js
@@ -359,6 +359,8 @@ this.caosdb_utils = new function () {
  * connection module contains all ajax calls.
  */
 this.connection = new function () {
+    const logger = log.getLogger("connection");
+
     this._init = function () {
         /**
          * Send a get request.
@@ -376,7 +378,7 @@ this.connection = new function () {
                 if (error.status == 414) {
                     throw new Error("UriTooLongException for GET " + uri);
                 } else if (error.status == 0) {
-                    console.log(error);
+                    logger.error(error);
                 } else if (error.status != null) {
                     throw new Error("GET " + uri + " returned with HTTP status " + error.status + " - " + error.statusText);
                 } else {
@@ -400,7 +402,7 @@ this.connection = new function () {
                 });
             } catch (error) {
                 if (error.status == 0) {
-                    console.log(error);
+                    logger.error(error);
                 } else if (error.status != null) {
                     throw new Error("PUT " + uri + " returned with HTTP status " + error.status + " - " + error.statusText);
                 } else {
@@ -435,7 +437,7 @@ this.connection = new function () {
                 });
             } catch (error) {
                 if (error.status == 0) {
-                    console.log(error);
+                    logger.error(error);
                 } else if (error.status != null) {
                     throw new Error(
                         "POST scripting returned with HTTP status " + error.status +
@@ -461,7 +463,7 @@ this.connection = new function () {
                 });
             } catch (error) {
                 if (error.status == 0) {
-                    console.log(error);
+                    logger.error(error);
                 } else if (error.status != null) {
                     throw new Error("POST " + uri + " returned with HTTP status " + error.status + " - " + error.statusText);
                 } else {
@@ -486,7 +488,7 @@ this.connection = new function () {
                 });
             } catch (error) {
                 if (error.status == 0) {
-                    console.log(error);
+                    logger.error(error);
                 } else if (error.status != null) {
                     throw new Error("DELETE " + "Entity/" + idline + " returned with HTTP status " + error.status + " - " + error.statusText);
                 } else {
@@ -985,6 +987,9 @@ this.transaction = new function () {
  */
 var version_history = new function () {
 
+    const logger = log.getLogger("version_history");
+    this.logger = logger;
+
     this._get = connection.get;
     /**
      * Retrieve the version history of an entity and return a table with the
@@ -1035,6 +1040,7 @@ var version_history = new function () {
                         .retrieve_history(entity_id_version);
                     sparse.replaceWith(history_table);
                     version_history.init_export_history_buttons(entity);
+                    version_history.init_restore_version_buttons(entity);
                 });
         }
     }
@@ -1072,7 +1078,7 @@ var version_history = new function () {
         for (let version_info of $(entity)
                 .find(".caosdb-f-entity-version-info")) {
             $(version_info).find(".caosdb-f-entity-version-export-history-btn")
-                .click(async () => {
+                .click(() => {
                     const html_table = $(version_info).find("table")[0];
                     const history_tsv = this.get_history_tsv(html_table);
                     version_history._download_tsv(history_tsv);
@@ -1080,6 +1086,72 @@ var version_history = new function () {
         }
     }
 
+    /**
+     * Initialize the restore old version buttons of `entity`.
+     *
+     * The buttons are only visible when the user is allowed to update the
+     * entity.
+     *
+     * The causes a retrieve of the specified version of the entity and then an
+     * update that restores that version.
+     *
+     * @param {HTMLElement} [entity] - if undefined, the export buttons of all
+     *     page entities are initialized.
+     */
+    this.init_restore_version_buttons = function (entity) {
+        var entities = [entity] || $(".caosdb-entity-panel");
+
+        for (let _entity of entities) {
+            // initialize buttons only if the user is allowed to update the entity
+            if (hasEntityPermission(_entity, "UPDATE:*") || hasEntityPermission(_entity, "UPDATE:DESCRIPTION")) {
+                for (let version_info of
+                        $(_entity).find(".caosdb-f-entity-version-info")) {
+                    // find the restore button
+                    $(version_info).find(".caosdb-f-entity-version-restore-btn")
+                        .toggleClass("d-none", false) // show button
+                        .click(async (eve) => {
+                            // the version id is stored in the restore button's
+                            // data-version-id attribute
+                            const versionid = eve.delegateTarget.getAttribute("data-version-id")
+                            const reload = () => {
+                                window.location.reload();
+                            }
+                            const _alert = form_elements.make_alert({
+                                title: "Warning",
+                                message: "You are going to restore this version of the entity.",
+                                proceed_callback: async () => {
+                                    try {
+                                        await restore_old_version(versionid);
+                                        $(_alert).remove();
+                                        // reload after sucessful update
+                                        $(version_info).find(".modal-body").prepend(
+                                            $(`<div class="alert alert-success" role="alert">Restore successful! <p>You are being forwarded to the latest version of this entity or you can click <a href="#" onclick="window.location.reload()">here</a>.</p></div>`));
+                                        setTimeout(reload, 5000);
+                                    } catch (e) {
+                                        logger.error(e);
+                                        // print errors in an alert div
+                                        $(version_info).find(".modal-body").prepend(
+                                            $(`<div class="alert alert-danger alert-dismissible " role="alert"> <button class="btn-close" data-bs-dismiss="alert" aria-label="close"></button> Restore failed! <p>${e.message}</p></div>`));
+
+                                    }
+                                },
+                                cancel_callback: () => {
+                                    // do nothing
+                                    $(_alert).remove();
+                                    $(version_info).find("table").show();
+                                },
+                                proceed_text: "Yes, restore!",
+                                remember_my_decision_id: "restore_entity",
+                            });
+
+                            $(version_info).find("table").after(_alert).hide();
+                            $(_alert).addClass("text-end");
+                        });
+                }
+            }
+        }
+    }
+
     this._download_tsv = function (tsv_link) {
         window.location.href = tsv_link;
     }
@@ -1088,6 +1160,7 @@ var version_history = new function () {
     this.init = function () {
         this.init_load_history_buttons();
         this.init_export_history_buttons();
+        this.init_restore_version_buttons();
     }
 }
 
diff --git a/src/core/xsl/entity.xsl b/src/core/xsl/entity.xsl
index 92f08ea70645a8282f97f364c9fc4143f37afd6a..d8ba00a9810e9861bfba741055ea113c7f2b88bc 100644
--- a/src/core/xsl/entity.xsl
+++ b/src/core/xsl/entity.xsl
@@ -644,11 +644,14 @@
     <div class="modal-body">
       <table class="table table-hover">
         <thead>
-          <tr><th><div class="export-data">Entity ID</div></th>
+          <tr>
+            <th></th>
+            <th class="invisible"><div class="export-data">Entity ID</div></th>
             <th class="export-data">Version ID</th>
             <th class="export-data">Date</th>
             <th class="export-data">User</th>
             <th class="invisible"><div class="export-data">URI</div></th>
+            <th></th>
           </tr></thead>
         <tbody>
           <xsl:apply-templates mode="entity-version-modal-successor" select="Successor">
@@ -664,6 +667,13 @@
               <xsl:value-of select="@username"/>@<xsl:value-of select="@realm"/>
             </td>
             <td class="invisible"><div class="export-data"><xsl:value-of select="concat($entitypath, $entityId, '@', @id)"/></div></td>
+            <td>
+              <xsl:if test="not(@head='true')">
+              <button type="button" class="caosdb-f-entity-version-restore-btn btn btn-secondary d-none" title="Restore this version of the entity.">
+                <xsl:attribute name="data-version-id"><xsl:value-of select="$entityId"/>@<xsl:value-of select="@id"/></xsl:attribute>
+                <i class="bi-arrow-counterclockwise"></i></button>
+              </xsl:if>
+            </td>
           </tr>
           <xsl:apply-templates mode="entity-version-modal-predecessor" select="Predecessor">
             <xsl:with-param name="entityId" select="$entityId"/>
@@ -672,7 +682,7 @@
       </table>
     </div>
     <div class="modal-footer">
-      <button type="button" class="caosdb-f-entity-version-export-history-btn btn btn-secondary">Export history</button>
+      <button type="button" class="caosdb-f-entity-version-export-history-btn btn btn-secondary" title="Export this history table as a CSV file.">Export history</button>
     </div>
   </xsl:template>
 
@@ -745,6 +755,14 @@
         <xsl:value-of select="@username"/>@<xsl:value-of select="@realm"/>
       </td>
       <td class="invisible"><div class="export-data"><xsl:value-of select="concat($entitypath, $entityId, '@', @id)"/></div></td>
+      <td>
+        <!-- include button if it is not head, i.e. Predecessors are always old and Successors if they do have a Successor Member -->
+        <xsl:if test="(name()='Predecessor' or Successor)">
+        <button type="button" class="caosdb-f-entity-version-restore-btn btn btn-secondary d-none" title="Restore this version of the entity.">
+          <xsl:attribute name="data-version-id"><xsl:value-of select="$entityId"/>@<xsl:value-of select="@id"/></xsl:attribute>
+          <i class="bi-arrow-counterclockwise"></i></button>
+        </xsl:if>
+      </td>
     </tr>
   </xsl:template>
 
diff --git a/src/core/xsl/footer.xsl b/src/core/xsl/footer.xsl
index 0231649d4ff4deda68822eecf25576175a5eed00..ce491222ca8e018ced18167bdfc9e934c37fa4d8 100644
--- a/src/core/xsl/footer.xsl
+++ b/src/core/xsl/footer.xsl
@@ -35,15 +35,17 @@
       </div>
     </div>
     <div class="container d-flex flex-md-row flex-column justify-content-center">
-      <a href="mailto:info@indiscale.com">Contact</a>
+      <a href="${BUILD_FOOTER_CONTACT_HREF}">Contact</a>
       <span class="caosdb-bulletsep d-none d-md-inline">•</span>
-      <a href="https://www.indiscale.com/imprint/">Imprint/Impressum</a>
+      <a href="${BUILD_FOOTER_IMPRINT_HREF}">Imprint/Impressum</a>
       <span class="caosdb-bulletsep d-none d-md-inline">•</span>
       <a href="${BUILD_FOOTER_DATA_POLICY_HREF}">Data Policy</a>
       <span class="caosdb-bulletsep d-none d-md-inline">•</span>
-      <a href="https://www.gnu.org/licenses/agpl-3.0.en.html" target="_blank">License (AGPL-v3)</a>
+      <a href="${BUILD_FOOTER_LICENCE_HREF}" target="_blank">License (AGPL-v3)</a>
       <span class="caosdb-bulletsep d-none d-md-inline">•</span>
-      <a href="https://gitlab.com/caosdb" target="_blank">Sources</a>
+      <a href="${BUILD_FOOTER_SOURCES_HREF}" target="_blank">Sources</a>
+      <span class="caosdb-bulletsep d-none d-md-inline">•</span>
+      <a href="${BUILD_FOOTER_DOCS_HREF}" target="_blank">Documentation</a>
     </div>
   </xsl:template>
 </xsl:stylesheet>
diff --git a/test/core/js/modules/edit_mode.js.js b/test/core/js/modules/edit_mode.js.js
index 3a87ee73d167d114d0b7db2b0abe50e4a643f8fa..ae1a51d837348ba0ba9c31f48a28a69ef2c9ad7b 100644
--- a/test/core/js/modules/edit_mode.js.js
+++ b/test/core/js/modules/edit_mode.js.js
@@ -400,8 +400,28 @@ QUnit.test("unfreeze", function (assert) {
     assert.ok(edit_mode.unfreeze);
 });
 
-QUnit.test("retrieve_datatype_list", function (assert) {
+QUnit.test("retrieve_datatype_list", async function (assert) {
     assert.ok(edit_mode.retrieve_datatype_list);
+    var query_done;
+
+    edit_mode.query = function (query) {
+        var re = /^FIND (Record|File) "IceCore"$/g;
+        assert.ok(query.match(re), `${query} should match ${re}`);
+        query_done();
+        return [];
+    }
+    query_done = assert.async(2);
+    await edit_mode.retrieve_datatype_list("IceCore");
+
+
+    edit_mode.query = function (query) {
+        var re = /^FIND (Record|File) "Ice Core"$/g;
+        assert.ok(query.match(re), `${query} should match ${re}`);
+        query_done();
+        return [];
+    }
+    query_done = assert.async(2);
+    await edit_mode.retrieve_datatype_list("Ice Core");
 });
 
 QUnit.test("highlight", function (assert) {
diff --git a/test/core/js/modules/ext_autocomplete.js.js b/test/core/js/modules/ext_autocomplete.js.js
index e8776f945b7bb46a0d431eb2d0ac0f7fe21419fc..96cab766fb848b74b04677f9b3312b574b9a3844 100644
--- a/test/core/js/modules/ext_autocomplete.js.js
+++ b/test/core/js/modules/ext_autocomplete.js.js
@@ -25,7 +25,7 @@
 QUnit.module("ext_autocomplete.js", {
     before: function (assert){
         ext_autocomplete.retrieve_names = async function () {
-            return ['IceCore', 'Bag', 'IceSample', 'IceCream', 'Palette'];
+            return ['IceCore', 'Bag', 'IceSample', 'IceCream', 'Palette', 'Ice Core'];
         }
         ext_autocomplete.init();
 
@@ -60,12 +60,34 @@ QUnit.test("search", async function(assert) {
         };
     };
     await ext_autocomplete.search("Ice", 
-        gcallback( ['IceCore', 'IceSample', 'IceCream'])
+        gcallback( ['IceCore', 'IceSample', 'IceCream', 'Ice Core'])
     );
 
     await ext_autocomplete.search("Core", gcallback([]));
 });
 
+QUnit.test("searchPost", async function(assert) {
+  const resultsFromServer = ["Ice Core", "IceCore"];
+  const origJQElement = [{
+    selectionEnd: 8,
+    value: "FIND Ice WHERE",
+  }];
+
+  const expected = [
+      {
+        "html": "Ice Core",
+        "text": "FIND \"Ice Core\" WHERE"
+      },
+      {
+        "html": "IceCore",
+        "text": "FIND IceCore WHERE"
+      }
+  ];
+
+  const result = ext_autocomplete.searchPost(resultsFromServer, origJQElement);
+  assert.propEqual(result, expected);
+});
+
 QUnit.test("class", function(assert) {
     assert.ok(ext_autocomplete.switch_on_completion , "toggle available");
     assert.ok(ext_autocomplete.switch_on_completion() , "toggle runs");
diff --git a/test/core/js/modules/ext_cosmetics.js.js b/test/core/js/modules/ext_cosmetics.js.js
index d5d4df7f10a2859bcd7318680d4f6720aedc6127..969c8297b8b5cf85d0a668d7c30e8b0f45e34d4d 100644
--- a/test/core/js/modules/ext_cosmetics.js.js
+++ b/test/core/js/modules/ext_cosmetics.js.js
@@ -46,13 +46,16 @@ QUnit.test("linkify - https", function (assert) {
         ["this is other text https://link", 1],
         ["this is other text https://link and here comes another link https://link and more text", 2],
     ];
+
     for (let test_case of test_cases) {
-        var text_value = $(`<div class="caosdb-f-property-text-value">${test_case[0]}</div>`);
-        $(document.body).append(text_value);
-        assert.equal($(text_value).find("a[href='https://link']").length, 0, "no link present");
+        const container = $('<div></div>');
+        $(document.body).append(container);
+        const text_value = $(`<div class="caosdb-f-property-text-value">${test_case[0]}</div>`);
+        container.append(text_value);
+        assert.equal($(container).find("a[href='https://link']").length, 0, "no link present");
         cosmetics.linkify();
-        assert.equal($(text_value).find("a[href='https://link']").length, test_case[1], "link is present");
-        text_value.remove();
+        assert.equal($(container).find("a[href='https://link']").length, test_case[1], "link is present");
+        container.remove();
     }
 });
 
@@ -66,22 +69,26 @@ QUnit.test("linkify - http", function (assert) {
         ["this is other text http://link and here comes another link http://link and more text", 2],
     ];
     for (let test_case of test_cases) {
-        var text_value = $(`<div class="caosdb-f-property-text-value">${test_case[0]}</div>`);
-        $(document.body).append(text_value);
-        assert.equal($(text_value).find("a[href='http://link']").length, 0, "no link present");
+        const container = $('<div></div>');
+        $(document.body).append(container);
+        const text_value = $(`<div class="caosdb-f-property-text-value">${test_case[0]}</div>`);
+        $(container).append(text_value);
+        assert.equal($(container).find("a[href='http://link']").length, 0, "no link present");
         cosmetics.linkify();
-        assert.equal($(text_value).find("a[href='http://link']").length, test_case[1], "link is present");
-        text_value.remove();
+        assert.equal($(container).find("a[href='http://link']").length, test_case[1], "link is present");
+        container.remove();
     }
 });
 
 QUnit.test("linkify cut-off (40)", function (assert) {
-    var test_case = "here is some text https://this.is.a.link/with/more/than/40/characters/ this is more text";
-    var text_value = $(`<div class="caosdb-f-property-text-value">${test_case}</div>`);
-    $(document.body).append(text_value);
-    assert.equal($(text_value).find("a").length, 0, "no link present");
+    const container = $('<div></div>');
+    $(document.body).append(container);
+    const test_case = "here is some text https://this.is.a.link/with/more/than/40/characters/ this is more text";
+    const text_value = $(`<div class="caosdb-f-property-text-value">${test_case}</div>`);
+    $(container).append(text_value);
+    assert.equal($(container).find("a").length, 0, "no link present");
     cosmetics.linkify();
-    assert.equal($(text_value).find("a").length, 1, "link is present");
-    assert.equal($(text_value).find("a").text(), "https://this.is.a.link/with/more/th[...] ", "link text has been cut off");
-    text_value.remove();
-});
\ No newline at end of file
+    assert.equal($(container).find("a").length, 1, "link is present");
+    assert.equal($(container).find("a").text(), "https://this.is.a.link/with/more/th[...] ", "link text has been cut off");
+    container.remove();
+});
diff --git a/test/core/js/modules/webcaosdb.js.js b/test/core/js/modules/webcaosdb.js.js
index d2ef27952e41142a62eb70e144571bc9d30c52d2..5f45c32adb9d171c5d362467d0bba0840f02836f 100644
--- a/test/core/js/modules/webcaosdb.js.js
+++ b/test/core/js/modules/webcaosdb.js.js
@@ -1827,6 +1827,7 @@ QUnit.test("available", function (assert) {
     assert.equal(typeof version_history.init, "function");
     assert.equal(typeof version_history.get_history_tsv, "function");
     assert.equal(typeof version_history.init_export_history_buttons, "function");
+    assert.equal(typeof version_history.init_restore_version_buttons, "function");
     assert.equal(typeof version_history.init_load_history_buttons, "function");
     assert.equal(typeof version_history.retrieve_history, "function");
 })
@@ -1887,6 +1888,76 @@ QUnit.test("init_load_history_buttons and init_load_history_buttons", async func
     $(html).remove();
 });
 
+QUnit.test("available", function (assert) {
+    assert.equal(typeof restore_old_version, "function");
+})
+
+QUnit.test("init_restore_version_buttons", async function (assert) {
+    var xml_str = `<Response username="user1" realm="Realm1" srid="bc2f8f6b-71d6-49ca-890c-eebea3e38e18" timestamp="1606253365632" baseuri="https://localhost:10443" count="1">
+  <UserInfo username="user1" realm="Realm1">
+    <Roles>
+      <Role>role1</Role>
+    </Roles>
+  </UserInfo>
+  <Record id="8610" name="TestRecord1-6thVersion" description="This is the 6th version.">
+    <Permissions>
+      <Permission name="RETRIEVE:HISTORY" />
+      <Permission name="UPDATE:*" />
+    </Permissions>
+    <Version id="efa5ac7126c722b3f43284e150d070d6deac0ba6" >
+      <Predecessor id="f09114b227d88f23d4e23645ae471d688b1e82f7" />
+      <Successor id="5759d2bccec3662424db5bb005acea4456a299ef" />
+    </Version>
+    <Parent id="8609" name="TestRT" />
+  </Record>
+</Response>
+`;
+    var done = assert.async(1);
+    var xml = str2xml(xml_str);
+    version_history._get = async function (entity) {
+        assert.equal(entity, "Entity/8610@efa5ac7126c722b3f43284e150d070d6deac0ba6?H");
+        done();
+        $(xml).find("Version").attr("completeHistory", "true");
+        return xml;
+    }
+    var html = await transformation.transformEntities(xml);
+    var load_button = $(html).find(".caosdb-f-entity-version-load-history-btn");
+    $("body").append(html);
+
+    assert.notOk(load_button.is(":visible"), "load_button hidden");
+    load_button.click(); // nothing happens
+
+    version_history.init_load_history_buttons();
+    assert.ok(load_button.is(":visible"), "load_button is not hidden anymore");
+
+    //console.log(xml2str(restore_button[0]));
+    //assert.ok(restore_button.hasClass("d-none"), "restore_button is hidden");
+
+
+    // load_button triggers retrieval of history
+    load_button.click();
+    await sleep(500);
+
+    //console.log(xml2str(restore_button[0]));
+    //version_history.init_restore_version_buttons();
+
+    var restore_button = $("body").find(".caosdb-f-entity-version-restore-btn");
+    assert.ok(!restore_button.hasClass("d-none"), "restore_button is not hidden anymore");
+
+    // restore_button triggers retrieval of history
+    localStorage["form_elements.alert_decision.restore_entity"] = "proceed";
+    restore_button.first().click();
+    localStorage.removeItem("form_elements.alert_decision.restore_entity");
+    await sleep(500);
+
+    // restore is not possible in the unit test
+    alertdiv = $(html).find(".alert-danger");
+    assert.equal(alertdiv.length, 1, "on alert div");
+    assert.ok(alertdiv.text().indexOf("Restore failed") > 0, "Restore failed");
+
+    $(html).remove();
+});
+
 
 /* SETUP tests for user_management */
 QUnit.module("webcaosdb.js - user_management", {