diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 07f72610a0165bb0c221671c922a0ce9232bfbe3..548f86457c3216f5eed7b75b4b81d7f048351248 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -21,9 +21,10 @@
 # along with this program. If not, see <https://www.gnu.org/licenses/>.
 
 variables:
-   CI_REGISTRY_IMAGE: $CI_REGISTRY/caosdb/caosdb-webui/testenv
-   # When using dind, it's wise to use the overlayfs driver for
-   # improved performance.
+  DEPLOY_REF: dev
+  CI_REGISTRY_IMAGE: $CI_REGISTRY/caosdb/caosdb-webui/testenv
+  # When using dind, it's wise to use the overlayfs driver for
+  # improved performance.
 
 image: $CI_REGISTRY_IMAGE:latest
 
@@ -66,10 +67,11 @@ trigger_build:
     - echo $TOKEN
     - /usr/bin/curl -X POST
        -F token=$DEPLOY_TRIGGER_TOKEN
+       -F "variables[F_BRANCH]=$CI_COMMIT_REF_NAME"
        -F "variables[WEBUI]=$CI_COMMIT_REF_NAME"
        -F "variables[TriggerdBy]=WEBUI"
        -F "variables[TriggerdByHash]=$CI_COMMIT_SHORT_SHA"
-       -F ref=dev https://gitlab.indiscale.com/api/v4/projects/14/trigger/pipeline
+       -F ref=$DEPLOY_REF https://gitlab.indiscale.com/api/v4/projects/14/trigger/pipeline
 
 # Build a docker image in which tests for this repository can run
 build-testenv:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 645616eb054a3099dafd1f18717d621d05a66246..323abc3f602827cfc8967cd639e14f1528eb9f50 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added (for new features, dependecies etc.)
 
+* basic support for entity versioning:
+    * entity.xsl generates a versioning button which opens a versioning info modal.
+    * `preview` also works for versioned references
+    * `edit_mode` prevents the user from editing old versions of an entity.
+
 ### Changed (for changes in existing functionality)
 - added a layout argument to the create_plot function of ext_bottom_line
 
diff --git a/misc/versioning_test_data.py b/misc/versioning_test_data.py
new file mode 100755
index 0000000000000000000000000000000000000000..eaa83e46f61ea2f20263b487e4bb42c37678c94f
--- /dev/null
+++ b/misc/versioning_test_data.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# ** header v3.0
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2020 IndiScale GmbH <info@indiscale.com>
+# Copyright (C) 2020 Timm Fitschen <t.fitschen@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/>.
+#
+# ** end header
+
+""" Insert test data for manually testing the versioning feature in the
+webinterface.
+"""
+# pylint: disable=no-member
+
+import caosdb
+
+# clean
+old = caosdb.execute_query("FIND Test*")
+if len(old) > 0:
+    old.delete()
+
+# data model
+
+rt = caosdb.RecordType("TestRT")
+rt.insert()
+
+
+# test data
+## record with several versions
+rec1 = caosdb.Record("TestRecord1-firstVersion").add_parent("TestRT")
+rec1.description = "This is the first version."
+rec1.insert()
+
+rec1.name = "TestRecord1-secondVersion"
+rec1.description = "This is the second version."
+rec1.update()
+if rec1.version:
+    ref = str(rec1.id) + "@" + rec1.version.id
+else:
+    ref = rec1.id
+
+rec1.name = "TestRecord1-thirdVersion"
+rec1.description = "This is the third version."
+rec1.update()
+
+rec2 = caosdb.Record("TestRecord2").add_parent("TestRT")
+rec2.description = ("This record references the TestRecord1 in the second "
+                    "version where the name and the description of the record "
+                    "should indicate the referenced version.")
+rec2.add_property("TestRT", ref)
+rec2.insert()
+
+rec3 = caosdb.Record("TestRecord3").add_parent("TestRT")
+rec3.description = ("This record references the TestRecord1 without "
+                    "specifying a version. Therefore the latest (at least the "
+                    "third version) is being openend when you click on the "
+                    "reference link.")
+rec3.add_property("TestRT", rec1.id)
+rec3.insert()
+
+rec4 = caosdb.Record("TestRecord4").add_parent("TestRT")
+rec4.description = ("This record has a list of references to several versions "
+                    "of TestRecord1. The first references the record without "
+                    "specifying the version, the other each reference a "
+                    "different version of that record.")
+if rec1.version:
+    rec4.add_property("TestRT", datatype=caosdb.LIST("TestRT"),
+                      value=[rec1.id,
+                             str(rec1.id) + "@HEAD",
+                             str(rec1.id) + "@HEAD~1",
+                             str(rec1.id) + "@HEAD~2"])
+else:
+    rec4.add_property("TestRT", datatype=caosdb.LIST("TestRT"),
+                      value=[rec1.id,
+                             str(rec1.id),
+                             str(rec1.id),
+                             str(rec1.id)])
+rec4.insert()
diff --git a/src/core/css/webcaosdb.css b/src/core/css/webcaosdb.css
index a7a17cd18018c2f5a3de84d5099011a40ef91ca6..ea7ca2a83970aa7d55b5fa58c1b58a6449e17933 100644
--- a/src/core/css/webcaosdb.css
+++ b/src/core/css/webcaosdb.css
@@ -27,6 +27,17 @@ body {
     flex-direction: column;
 }
 
+button.caosdb-v-entity-version-button {
+    height: 15px;
+}
+
+.caosdb-v-entity-header-buttons-list > * {
+    margin: 0;
+    margin-left: 8px;
+    padding: 0;
+    vertical-align: middle;
+}
+
 .caosdb-v-main-col {
     flex-grow: 1;
     max-width: 90vw;
diff --git a/src/core/js/caosdb.js b/src/core/js/caosdb.js
index ac2b1c7574cd2b43d05ee56fa24d4b10732ec5bb..1c1318fe840702d979e64616fe380abb874f958d 100644
--- a/src/core/js/caosdb.js
+++ b/src/core/js/caosdb.js
@@ -245,6 +245,35 @@ function getEntityID(element) {
     throw new Error("id is ambigous for this element!");
 }
 
+
+/**
+ * Get the version string of an entity.
+ *
+ * @param {HTMLElement} an Entity in HTML representation
+ *     (div.caosdb-entity-panel)
+ * @return {string} the version
+ */
+var getEntityVersion = function (entity) {
+    return entity.getAttribute("data-version-id");
+}
+
+
+/**
+ * Get the id and, if present, the version of an entity.
+ *
+ * @param {HTMLElement} an Entity in HTML representation
+ *     (div.caosdb-entity-panel)
+ * @return {string} <id>[@<version>]
+ */
+var getEntityIdVersion = function (entity) {
+    const id = getEntityID(entity);
+    const version = getEntityVersion(entity);
+    if (version) {
+        return `${id}@${version}`;
+    }
+    return id;
+}
+
 /**
  * Take a date and a time and format it into a CaosDB compatible representation.
  * @param date A date
diff --git a/src/core/js/edit_mode.js b/src/core/js/edit_mode.js
index b68d8ca4d9d5d309f3374c3717fe001806241173..cc935bd3c0ba75a300453c69d5a8caeed28af17a 100644
--- a/src/core/js/edit_mode.js
+++ b/src/core/js/edit_mode.js
@@ -1168,6 +1168,10 @@ var edit_mode = new function() {
             const state = app.state;
             $('.caosdb-entity-panel').each(function(index) {
                 let entity = this;
+                if ($(entity).is("[data-version-successor]")) {
+                    // not the latest version -> ignore
+                    return;
+                }
 
                 // remove listeners from all entities
                 edit_mode.unset_entity_dropable(entity, edit_mode.dragover, edit_mode.dragleave, edit_mode.parent_drop_listener, edit_mode.property_drop_listener);
@@ -1202,6 +1206,10 @@ var edit_mode = new function() {
                 edit_mode.enable_new_buttons();
                 $('.caosdb-entity-panel').each(function(index) {
                     let entity = this;
+                    if ($(entity).is("[data-version-successor]")) {
+                        // not the latest version -> ignore
+                        return;
+                    }
                     if (typeof getEntityID(entity) == "undefined" || getEntityID(entity) == '') {
                         // no id -> insert
                         edit_mode.init_actions_panels(entity);
diff --git a/src/core/js/preview.js b/src/core/js/preview.js
index 9af9bb0366097f6d1017a46ff077874ab34bf773..f74fd13ffd2fe373543c025866e91cfe6b6fd9eb 100644
--- a/src/core/js/preview.js
+++ b/src/core/js/preview.js
@@ -30,9 +30,25 @@ var preview = new function() {
         return props;
     }
 
+    // import from global name space.
+    this.getEntityID = getEntityID;
+    this.getEntityVersion = getEntityVersion;
+    this.getEntityIdVersion = getEntityIdVersion;
+
+    /**
+     * Get the id and, if present, the version of an entity from a link or a
+     * displayed reference.
+     *
+     * @param {HTMLElement} a link to an entity.
+     * @return {string} <id>[@<version>]
+     */
+    this.getEntityRef = function (link) {
+        return link.getElementsByClassName("caosdb-id")[0].textContent;
+    }
+
     /**
      * Initialize the preview feature for all reference properties which belong to certain entity.
-     *  
+     *
      * @param {HTMLElement} entity
      * @return {HTMLElement[]} The initialized properties. 
      */
@@ -113,7 +129,7 @@ var preview = new function() {
         app.onEnterWaiting = function(e) {
             executeFailSave(function() {
                 preview.addWaitingNotification(ref_property_elem, preview.createWaitingNotification());
-                let entityIds = preview.getEntityIds(refLinksContainer);
+                let entityIds = preview.getAllEntityRefs(refLinksContainer);
                 preview.retrievePreviewEntities(entityIds).then(entities => {
                     app.receivePreview(entities);
                 }, err => {
@@ -391,9 +407,9 @@ var preview = new function() {
         let selectorButtons = preview.getSelectorButtons(nav);
         selectorButtons.each((index, button) => {
             let slide_id = button.getAttribute("data-slide-to");
-            let entity_id = getEntityId(button);
-            let entity = preview.getEntityById(entities, entity_id);
-            if (entity == null) throw new Error("Entity with ID " + entity_id + " could not be found!");
+            let entity_id_version = preview.getEntityRef(button);
+            let entity = preview.getEntityByIdVersion(entities, entity_id_version);
+            if (entity == null) throw new Error("Entity with ID " + entity_id_version + " could not be found!");
             inner.children[slide_id].appendChild(preview.preparePreviewEntity(entity));
         });
 
@@ -443,8 +459,8 @@ var preview = new function() {
      *
      */
     this.createSinglePreview = function(entities, refLinksContainer) {
-        let entityId = getEntityId(preview.getReferenceLinks(refLinksContainer)[0]);
-        let entity = preview.preparePreviewEntity(preview.getEntityById(entities, entityId));
+        const entityRef = preview.getEntityRef(preview.getReferenceLinks(refLinksContainer)[0]);
+        const entity = preview.preparePreviewEntity(preview.getEntityByIdVersion(entities, entityRef));
         return entity;
     }
 
@@ -456,15 +472,25 @@ var preview = new function() {
      * @return {HTMLElement} The prepared entity.
      */
     this.preparePreviewEntity = function(entity) {
+        // move version modal into body because otherwise it would be displayed
+        // inside the caroussel. That would make sense but there is simply not
+        // enough space.
+        $(entity).find(".caosdb-f-entity-version-info").appendTo(document.body);
+
+
+        // make backref button smaller
+        $(entity).find(".caosdb-backref-link > .hidden-xs").hide();
+
         var preparedEntity = entity.cloneNode(true);
 
         // header is clickable:
-        let href = connection.getBasePath() + transaction.generateEntitiesUri([getEntityId(entity)]);
+        let href = connection.getBasePath() + transaction.generateEntitiesUri([preview.getEntityRef(entity)]);
         let link = $('<a title="Load this entity in a new window." href="' + href + '" class="label caosdb-id caosdb-id-button" target="_blank"></a>');
         let entityIdElem = $(preparedEntity).find('.label.caosdb-id');
         link.insertAfter(entityIdElem);
         link.append(entityIdElem.text() + " ");
         link.append('<span class="glyphicon glyphicon-new-window"/>');
+        // TODO this link is not visible due to webcaosdb.css (caosdb-id)
         entityIdElem.remove();
 
         return preparedEntity;
@@ -529,22 +555,34 @@ var preview = new function() {
     }
 
     /**
-     * Get the entity with a certain ID from an array of entities. Returns null if no such entity
-     * is in the array.
+     * Get the entity with a certain ID and Version (if applicable) from an
+     * array of entities. Returns null if no such entity is in the array.
+     *
      * @param {HTMLElement[]} entities
-     * @param {Number} entity_id
-     * @return {HTMLElement} The entity with id=entity_id or null.
+     * @param {String} entity_id_version
+     * @return {HTMLElement} Matching entity or null.
      */
-    this.getEntityById = function(entities, entity_id) {
+    this.getEntityByIdVersion = function(entities, entity_id_version) {
         if (entities == null) {
             throw new Error("entities must not be null");
         }
-        if (entity_id == null || isNaN(entity_id)) {
-            throw new Error("entity_id is to be a number");
+        if (entity_id_version == null) {
+            throw new Error("entity_id_version must not be null");
+        }
+
+        // if the entity_id_version contains an "@" it is actually a reference
+        // to a versioned entity. Otherwise, it is just an id an thus only the
+        // id has to be matched.
+        const is_versioned = entity_id_version.indexOf("@") !== -1;
+        var matches;
+        if (is_versioned) {
+            matches = (e) => preview.getEntityIdVersion(e) === entity_id_version;
+        } else {
+            matches = (e) => preview.getEntityID(e) === entity_id_version;
         }
         for (let i = 0; i < entities.length; i++) {
-            let e = entities[i];
-            if (getEntityId(e) === entity_id) {
+            const e = entities[i];
+            if (matches(e)) {
                 return e;
             }
         }
@@ -694,26 +732,27 @@ var preview = new function() {
      * @param {HTMLElement} refLinksContainer
      * @return {String[]} An array of entity ids.
      */
-    this.getEntityIds = function(refLinksContainer) {
+    this.getAllEntityRefs = function(refLinksContainer) {
         if (refLinksContainer == null) {
             throw new Error("parameter refLinksContainer must not be null.");
         }
 
-        let entityIds = [];
-        preview.getReferenceLinks(refLinksContainer).each((index, link) => {
-            entityIds.push(getEntityId(link));
-        });
-        return entityIds;
+        let entityRefs = [];
+        for (let link of preview.getReferenceLinks(refLinksContainer)) {
+            entityRefs.push(preview.getEntityRef(link));
+        };
+        return entityRefs;
     }
 
     /**
      * Get an array of all reference links.
      * 
      * @param {HTMLElement} refLinksContainer - The original reference links.
-     * @return {jQuery} A collection of links.
+     * @return {HTMLElement[]} A collection of links.
      */
     this.getReferenceLinks = function(refLinksContainer) {
-        return $(refLinksContainer).find('a').addBack('a').has('.caosdb-id');
+        return $(refLinksContainer)
+            .find('a').addBack('a').has('.caosdb-id').toArray();
     }
 };
 
diff --git a/src/core/xsl/entity.xsl b/src/core/xsl/entity.xsl
index c43b2e8b7c891a6b7f7e17a74b5f42bcefcb4ed3..816d826c0a4d282f6b81f841aad7baa81cdebbbd 100644
--- a/src/core/xsl/entity.xsl
+++ b/src/core/xsl/entity.xsl
@@ -46,9 +46,9 @@
       <xsl:attribute name="href">
         <xsl:value-of select="concat($entitypath, '?P=0L10&amp;query=FIND+Entity+which+references+', current())"/>
       </xsl:attribute>
-      <span class="glyphicon glyphicon-share-alt flipped-horiz-icon"/> References
+      <span class="glyphicon glyphicon-share-alt flipped-horiz-icon"/>
+      <span class="hidden-xs"> References</span>
     </a>
-    <span class="spacer"/>
   </xsl:template>
   <!-- special entity properties like type, checksum, path... -->
   <xsl:template match="@datatype" mode="entity-heading-attributes-datatype">
@@ -87,11 +87,16 @@
     </p>
   </xsl:template>
   <xsl:template match="*" mode="entity-action-panel">
-    <div class="caosdb-entity-actions-panel text-right btn-group-xs"></div>
+    <div class="caosdb-entity-actions-panel text-right btn-group-xs">
+        <xsl:apply-templates select="Version/Successor" mode="entity-action-panel-version">
+          <xsl:with-param name="entityId" select="@id"/>
+        </xsl:apply-templates>
+    </div>
   </xsl:template>
   <!-- Main entry for ENTITIES -->
   <xsl:template match="Property|Record|RecordType|File" mode="entities">
     <div class="panel panel-default caosdb-entity-panel">
+      <xsl:apply-templates select="Version" mode="entity-version-marker"/>
       <xsl:attribute name="id">
         <xsl:value-of select="@id"/>
       </xsl:attribute>
@@ -138,17 +143,22 @@
             </h5>
           </div>
           <div class="col-sm-4 text-right">
-            <h5>
+            <h5 class="caosdb-v-entity-header-buttons-list">
               <!-- Button for expanding/collapsing the comments section-->
-              <span class="caosdb-clickable glyphicon glyphicon-comment" data-toggle="collapse" title="Comments" style="margin-right: 10px;">
+              <span class="caosdb-clickable glyphicon glyphicon-comment" data-toggle="collapse" title="Toggle the comments section at the bottom of this entity.">
                 <xsl:attribute name="data-target">
                   <xsl:value-of select="concat('#', 'comment_', $entityid)"/>
                 </xsl:attribute>
               </span>
+              <span>
               <xsl:apply-templates mode="backreference-link" select="@id"/>
+              </span>
               <span class="label caosdb-id caosdb-id-button hidden">
                 <xsl:value-of select="@id"/>
               </span>
+              <xsl:apply-templates mode="entity-heading-attributes-version" select="Version">
+                <xsl:with-param name="entityId" select="@id"/>
+              </xsl:apply-templates>
             </h5>
           </div>
         </div>
@@ -357,7 +367,7 @@
       <xsl:when test="contains(concat('&lt;',@datatype),'&lt;LIST&lt;')">
         <!-- list -->
         <xsl:choose>
-          <xsl:when test="translate(normalize-space(text()),'0123456789','')='' and not(contains('+LIST&lt;INTEGER>+LIST&lt;DOUBLE>+LIST&lt;TEXT>+LIST&lt;BOOLEAN>+LIST&lt;DATETIME>+',concat('+',@datatype,'+')))">
+          <xsl:when test="not(contains('+LIST&lt;INTEGER>+LIST&lt;DOUBLE>+LIST&lt;TEXT>+LIST&lt;BOOLEAN>+LIST&lt;DATETIME>+',concat('+',@datatype,'+')))">
             <xsl:apply-templates mode="property-reference-value-list" select="."/>
           </xsl:when>
           <xsl:otherwise>
@@ -372,7 +382,7 @@
             <xsl:value-of select="text()"/>
           </xsl:with-param>
           <xsl:with-param name="reference">
-            <xsl:value-of select="translate(normalize-space(text()),'0123456789','')='' and not(contains('+INTEGER+DOUBLE+TEXT+BOOLEAN+DATETIME+',concat('+',@datatype,'+')))"/>
+            <xsl:value-of select="not(contains('+INTEGER+DOUBLE+TEXT+BOOLEAN+DATETIME+',concat('+',@datatype,'+')))"/>
           </xsl:with-param>
           <xsl:with-param name="boolean">
             <xsl:value-of select="@datatype='BOOLEAN'"/>
@@ -458,4 +468,115 @@
       </li>
     </ul>
   </xsl:template>
+  <!--VERSIONING-->
+  <xsl:template match="Version" mode="entity-heading-attributes-version">
+    <xsl:param name="entityId"/>
+    <xsl:param name="versionModalId">version-modal-<xsl:value-of select="generate-id()"/></xsl:param>
+    <!-- the clock button which opens the window with the versioning info -->
+    <button title="Versioning Info" type="button" data-toggle="modal">
+      <xsl:attribute name="data-target">#<xsl:value-of select="$versionModalId"/></xsl:attribute>
+      <xsl:attribute name="class">
+        caosdb-f-entity-version-button caosdb-v-entity-version-button btn
+        <xsl:if test="Successor">
+          <!-- indicate old version by color and symbol -->
+          <xsl:value-of select="' text-danger'"/>
+        </xsl:if>
+      </xsl:attribute>
+      <span class="glyphicon glyphicon-time"/>
+    </button>
+    <!-- the following div.modal is the window that pops up when the user clicks on the clock button -->
+    <div class="caosdb-f-entity-version-info modal fade" tabindex="-1" role="dialog">
+      <xsl:attribute name="id"><xsl:value-of select="$versionModalId"/></xsl:attribute>
+      <div class="modal-dialog modal-lg" role="document">
+        <div class="modal-content text-left">
+          <div>
+            <xsl:attribute name="class">
+              modal-header
+              <xsl:if test="Successor">
+                <!-- indicate old version by color -->
+                <xsl:value-of select="' bg-danger'"/>
+              </xsl:if>
+            </xsl:attribute>
+            <button type="button" class="close" data-dismiss="modal" aria-label="Close" title="Close"><span aria-hidden="true">×</span></button>
+            <h4 class="modal-title">Version Info</h4>
+            <p class="caosdb-entity-heading-attr">
+              <em class="caosdb-entity-heading-attr-name">
+              This is
+              <xsl:if test="Successor"><b>not</b></xsl:if>
+              the latest version of this entity.
+              </em>
+            </p>
+          </div>
+          <div class="modal-body">
+            <xsl:apply-templates mode="entity-version-modal-head" select="Successor">
+              <xsl:with-param name="entityId" select="$entityId"/>
+            </xsl:apply-templates>
+            <xsl:apply-templates mode="entity-version-modal-successor" select="Successor">
+              <xsl:with-param name="entityId" select="$entityId"/>
+            </xsl:apply-templates>
+            <p class="caosdb-entity-heading-attr">
+              <em class="caosdb-entity-heading-attr-name">This version:</em>
+              <xsl:value-of select="@id"/> (<xsl:value-of select="@date"/>)
+            </p>
+            <xsl:apply-templates mode="entity-version-modal-predecessor" select="Predecessor">
+              <xsl:with-param name="entityId" select="$entityId"/>
+            </xsl:apply-templates>
+          </div>
+        </div>
+      </div>
+    </div>
+  </xsl:template>
+  <xsl:template match="Predecessor" mode="entity-version-modal-predecessor">
+    <!-- content of the versioning window -->
+    <xsl:param name="entityId"/>
+    <p class="caosdb-entity-heading-attr">
+      <em class="caosdb-entity-heading-attr-name">Previous version:</em>
+      <a><xsl:attribute name="href"><xsl:value-of select="$entityId"/>@<xsl:value-of select="@id"/></xsl:attribute>
+        <xsl:value-of select="@id"/> (<xsl:value-of select="@date"/>)
+      </a>
+    </p>
+  </xsl:template>
+  <xsl:template match="Successor" mode="entity-version-modal-head">
+    <!-- content of the versioning window -->
+    <xsl:param name="entityId"/>
+    <p class="caosdb-entity-heading-attr">
+      <em class="caosdb-entity-heading-attr-name">Newest version:</em>
+      <a><xsl:attribute name="href"><xsl:value-of select="$entityId"/>@HEAD</xsl:attribute>
+        <xsl:value-of select="$entityId"/>@HEAD
+      </a>
+    </p>
+  </xsl:template>
+  <xsl:template match="Successor" mode="entity-version-modal-successor">
+    <!-- content of the versioning window -->
+    <xsl:param name="entityId"/>
+    <p class="caosdb-entity-heading-attr">
+      <em class="caosdb-entity-heading-attr-name">Next version:</em>
+      <a><xsl:attribute name="href"><xsl:value-of select="$entityId"/>@<xsl:value-of select="@id"/></xsl:attribute>
+        <xsl:value-of select="@id"/> (<xsl:value-of select="@date"/>)
+      </a>
+    </p>
+  </xsl:template>
+  <xsl:template match="Version/Successor" mode="entity-action-panel-version">
+    <!-- clickable warning message in the entity actions panel when there exists a newer version -->
+    <xsl:param name="entityId"/>
+    <a class="caosdb-f-entity-version-old-warning alert-warning btn btn-link" title="Go to the latest version of this entity.">
+      <xsl:attribute name="href"><xsl:value-of select="$entityId"/>@HEAD</xsl:attribute>
+      <strong>Warning</strong> A newer version exists!
+    </a>
+  </xsl:template>
+  <xsl:template match="Version" mode="entity-version-marker">
+    <!-- content of the data-version-id attribute -->
+    <xsl:attribute name="data-version-id">
+        <xsl:value-of select="@id"/>
+    </xsl:attribute>
+    <xsl:apply-templates select="Successor" mode="entity-version-marker"/>
+  </xsl:template>
+  <xsl:template match="Successor" mode="entity-version-marker">
+    <!-- content of the data-version-successor attribute
+         This data-attribute marks entities which have a newer version.
+    -->
+    <xsl:attribute name="data-version-successor">
+      <xsl:value-of select="@id"/>
+    </xsl:attribute>
+  </xsl:template>
 </xsl:stylesheet>
diff --git a/test/core/js/modules/caosdb.js.js b/test/core/js/modules/caosdb.js.js
index 35a1834083d0b849a62b5f9552a3b7f1259c56c4..250d0f756fea34ad4c30e6377c61294c3d179513 100644
--- a/test/core/js/modules/caosdb.js.js
+++ b/test/core/js/modules/caosdb.js.js
@@ -167,6 +167,17 @@ QUnit.test("getProperties", function(assert) {
     assert.equal(ps[0].datatype, "TEXT");
 });
 
+QUnit.test("getEntityIdVersion", function(assert) {
+    // without version
+    var html = $('<div data-entity-id="1234"/>')[0];
+    assert.equal(getEntityIdVersion(html), "1234", "id extracted");
+
+    // with version
+    html = $('<div data-entity-id="1234" data-version-id="abcd"/>')[0];
+    assert.equal(getEntityIdVersion(html), "1234@abcd", "<id>@<version> extracted");
+
+});
+
 /**
   * @author Alexander Schlemmer
   * Test whether parents are retrieved correctly.
diff --git a/test/core/js/modules/entity.xsl.js b/test/core/js/modules/entity.xsl.js
index e9e50955b33e44a9f39ae014eba8aa7bde032067..c607ee28bf7888f94e3086abf8e033e1532d0d09 100644
--- a/test/core/js/modules/entity.xsl.js
+++ b/test/core/js/modules/entity.xsl.js
@@ -180,6 +180,90 @@ QUnit.test("single-value template with reference property.", function(assert) {
     assert.equal($(link).find('.caosdb-id').length, 1, 'has caosdb-id span');
 })
 
+QUnit.test("old version warning", function(assert) {
+    // with successor tag
+    var xmlstr = '<Record id="2345"><Version id="abcd1234"><Successor id="bcde2345"/></Version></Record>';
+    var xml = str2xml(xmlstr);
+    var html = applyTemplates(xml, this.entityXSL, "entities", "*");
+    assert.equal($(html).find(".caosdb-entity-panel .caosdb-f-entity-version-old-warning").length, 1, "warning present");
+
+    // with version tag, without successor
+    xmlstr = '<Record id="2345"><Version id="abcd1234"/></Record>';
+    xml = str2xml(xmlstr);
+    html = applyTemplates(xml, this.entityXSL, "entities", "*");
+    assert.equal($(html).find(".caosdb-f-entity-version-old-warning").length, 0, "warning not present");
+
+    // without version tag
+    xmlstr = '<Record id="2345"></Record>';
+    xml = str2xml(xmlstr);
+    html = applyTemplates(xml, this.entityXSL, "entities", "*");
+    assert.equal($(html).find(".caosdb-f-entity-version-old-warning").length, 0, "warning not present");
+});
+
+QUnit.test("version button", function(assert) {
+    // with version tag
+    var xmlstr = '<Record id="2345"><Version id="abcd1234"/></Record>';
+    var xml = str2xml(xmlstr);
+    var html = applyTemplates(xml, this.entityXSL, "entities", "*");
+
+    assert.equal($(html).find("div.caosdb-entity-panel button.caosdb-f-entity-version-button").length, 1, "button present");
+
+    // without version tag
+    xmlstr = '<Record id="2345"></Record>';
+    xml = str2xml(xmlstr);
+    html = applyTemplates(xml, this.entityXSL, "entities", "*");
+    assert.equal($(html).find(".caosdb-f-entity-version-button").length, 0, "button not present");
+});
+
+QUnit.test("version info modal", function(assert) {
+    // with version tag
+    var xmlstr = '<Record id="2345"><Version id="abcd1234"/></Record>';
+    var xml = str2xml(xmlstr);
+    var html = applyTemplates(xml, this.entityXSL, "entities", "*");
+
+    assert.equal($(html).find("div.caosdb-entity-panel div.caosdb-f-entity-version-info").length, 1, "info present");
+
+    // without version tag
+    xmlstr = '<Record id="2345"></Record>';
+    xml = str2xml(xmlstr);
+    html = applyTemplates(xml, this.entityXSL, "entities", "*");
+    assert.equal($(html).find(".caosdb-f-entity-version-info").length, 0, "info not present");
+});
+
+QUnit.test("data-version-id attribute", function(assert) {
+    // with version tag
+    var xmlstr = '<Record id="2345"><Version id="abcd1234"/></Record>';
+    var xml = str2xml(xmlstr);
+    var html = applyTemplates(xml, this.entityXSL, "entities", "*");
+    assert.equal($(html).find("div.caosdb-entity-panel[data-version-id='abcd1234']").length, 1, "data-version-id attribute present");
+
+    // without version tag
+    xmlstr = '<Record id="2345"></Record>';
+    xml = str2xml(xmlstr);
+    html = applyTemplates(xml, this.entityXSL, "entities", "*");
+    assert.equal($(html).find("div.caosdb-entity-panel[data-version-id]").length, 0, "data-version-id attribute not present");
+});
+
+QUnit.test("data-version-successor attribute", function(assert) {
+    // with successor tag
+    var xmlstr = '<Record id="2345"><Version id="abcd1234"><Successor id="bcde2345"/></Version></Record>';
+    var xml = str2xml(xmlstr);
+    var html = applyTemplates(xml, this.entityXSL, "entities", "*");
+    assert.equal($(html).find("div.caosdb-entity-panel[data-version-successor='bcde2345']").length, 1, "data-version-successor attribute present");
+
+    // with version tag, without successor
+    xmlstr = '<Record id="2345"><Version id="abcd1234"/></Record>';
+    xml = str2xml(xmlstr);
+    html = applyTemplates(xml, this.entityXSL, "entities", "*");
+    assert.equal($(html).find("div.caosdb-entity-panel[data-version-successor]").length, 0, "data-version-successor attribute not present");
+
+    // without version tag
+    xmlstr = '<Record id="2345"></Record>';
+    xml = str2xml(xmlstr);
+    html = applyTemplates(xml, this.entityXSL, "entities", "*");
+    assert.equal($(html).find("div.caosdb-entity-panel[data-version-successor]").length, 0, "data-version-successor attribute not present");
+});
+
 /* MISC FUNCTIONS */
 function applyTemplates(xml, xsl, mode, select = "*") {
     let entryRule = '<xsl:template priority="9" match="/"><xsl:apply-templates select="' + select + '" mode="' + mode + '"/></xsl:template>';
diff --git a/test/core/js/modules/webcaosdb.js.js b/test/core/js/modules/webcaosdb.js.js
index 3b615e11e4bd96c26ecc0d8e1cb9d8220a4cba8e..1f8db45c07fef49fbd1e90be803d48fc8c96c7d2 100644
--- a/test/core/js/modules/webcaosdb.js.js
+++ b/test/core/js/modules/webcaosdb.js.js
@@ -164,7 +164,6 @@ QUnit.test("get", function(assert) {
     });
 });
 
-
 /* MODULE transformation */
 QUnit.module("webcaosdb.js - transformation", {
     before: function(assert) {
@@ -782,34 +781,29 @@ QUnit.test("getActiveSlideItemIndex", function(assert) {
     assert.equal(2, preview.getActiveSlideItemIndex(okElem2));
 });
 
-QUnit.test("getEntityById", function(assert) {
-    assert.ok(preview.getEntityById, "function available");
+QUnit.test("getEntityByIdVersion", function(assert) {
+    assert.ok(preview.getEntityByIdVersion, "function available");
     let e1 = $('<div><div class="caosdb-id">1</div></div>')[0];
     let e2 = $('<div><div class="caosdb-id">2</div></div>')[0];
-    let e3 = $('<div><div class="caosdb-id">3</div><div><div class="caosdb-id">1</div></div></div>')[0];
 
-    let es = [e1, e2, e3];
+    let es = [e1, e2];
 
     assert.throws(() => {
-        preview.getEntityById()
+        preview.getEntityByIdVersion()
     }, "no param throws.");
     assert.throws(() => {
-        preview.getEntityById(null, 1)
+        preview.getEntityByIdVersion(null, 1)
     }, "null first param throws.");
     assert.throws(() => {
-        preview.getEntityById("asdf", 1)
+        preview.getEntityByIdVersion("asdf", 1)
     }, "string first param throws.");
     assert.throws(() => {
-        preview.getEntityById(es, null)
+        preview.getEntityByIdVersion(es, null)
     }, "null second param throws.");
-    assert.throws(() => {
-        preview.getEntityById(es, "asdf")
-    }, "string second param throws.");
 
-    assert.equal(e1, preview.getEntityById(es, 1), "find 1");
-    assert.equal(e2, preview.getEntityById(es, 2), "find 2");
-    assert.equal(e3, preview.getEntityById(es, 3), "find 3");
-    assert.equal(null, preview.getEntityById(es, 4), "find 4 -> null");
+    assert.equal(e1, preview.getEntityByIdVersion(es, "1"), "find 1");
+    assert.equal(e2, preview.getEntityByIdVersion(es, "2"), "find 2");
+    assert.equal(null, preview.getEntityByIdVersion(es, "3"), "find 3 -> null");
 });
 
 QUnit.test("createEmptyInner", function(assert) {
@@ -870,7 +864,7 @@ QUnit.test("createCarouselNav", function(assert) {
     let refLinks = $('<div class="caosdb-value-list"><a><span class="caosdb-id">1234</span></a><a><span class="caosdb-id">2345</span></a><a><span class="caosdb-id">3456</span></a><a><span class="caosdb-id">4567</span></a></div>')[0];
     let e1 = $('<div><div class="caosdb-id">1234</div></div>')[0];
     let e2 = $('<div><div class="caosdb-id">2345</div></div>')[0];
-    let e3 = $('<div><div class="caosdb-id">3456</div><div><div class="caosdb-id">1234</div></div></div>')[0];
+    let e3 = $('<div><div class="caosdb-id">3456</div></div>')[0];
     let e4 = $('<div><div class="caosdb-id">4567</div></div>')[0];
     let entities = [e1, e3, e4, e2];
     let carousel = preview.createPreviewCarousel(entities, refLinks);
@@ -1040,8 +1034,41 @@ QUnit.test("preparePreviewEntity", function(assert){
     assert.equal($(prepared).find('a.caosdb-id')[0].href, connection.getBasePath() + "Entity/1234", "link is correct.");
 });
 
-QUnit.test("getEntitiyIds", function(assert) {
-    assert.ok(preview.getEntityIds, 'function available');
+QUnit.test("getEntityRef", function(assert) {
+    assert.ok(preview.getEntityRef, 'function available');
+
+    var html = $('<div><div class="caosdb-id">sdfg</div></div>')[0];
+    assert.equal(preview.getEntityRef(html), "sdfg", "id extracted");
+
+    html = $('<div><div class="caosdb-id"></div></div>')[0];
+    assert.equal(preview.getEntityRef(html), "", "empty string extracted");
+
+    html = $('<div></div>')[0];
+    assert.throws(()=>{preview.getEntityRef(html);}, "missing .caosdb-id throws");
+});
+
+
+QUnit.test("getAllEntityRefs", function(assert) {
+    assert.ok(preview.getAllEntityRefs, 'function available');
+    assert.throws(preview.getAllEntityRefs, "null param throws");
+
+    // overwrite called methods
+    const oldGetReferenceLinks = preview.getReferenceLinks;
+    preview.getReferenceLinks = function(links) {
+        assert.propEqual(links, ["bla"], "array is passed to getReferenceLinks");
+        return links;
+    }
+    const oldGetEntityRef = preview.getEntityRef;
+    preview.getEntityRef = function(link) {
+        assert.equal(link, "bla", "array elements are passed to getEntityRef");
+        return "asdf";
+    }
+
+    assert.propEqual(preview.getAllEntityRefs(["bla"]), ["asdf"], "returns array with refs");
+
+
+    // cleanup
+    preview.getReferenceLinks = oldGetReferenceLinks;
 });
 
 QUnit.test("retrievePreviewEntities", function(assert) {