diff --git a/build.properties.d/00_default.properties b/build.properties.d/00_default.properties
index 0304440ef627ee453f646bc8eec4809dc1553024..9b8f8095befc01f3f9fba610ee61efbaa8c24fc5 100644
--- a/build.properties.d/00_default.properties
+++ b/build.properties.d/00_default.properties
@@ -62,6 +62,7 @@ BUILD_MODULE_EXT_RESOLVE_REFERENCES=ENABLED
 BUILD_EXT_REFERENCES_CUSTOM_RESOLVER=caosdb_default_person_reference
 
 BUILD_MODULE_EXT_EDITMODE_WYSIWYG_TEXT=DISABLED
+BUILD_MODULE_EXT_PROPERTY_DISPLAY=DISABLED
 
 ##############################################################################
 # Navbar properties
@@ -203,4 +204,5 @@ MODULE_DEPENDENCIES=(
     ckeditor.js
     ext_editmode_wysiwyg_text.js
     reference_resolver/caosdb_default_person.js
+    ext_prop_display.js
 )
diff --git a/conf/core/json/ext_prop_display.json b/conf/core/json/ext_prop_display.json
new file mode 100644
index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93
--- /dev/null
+++ b/conf/core/json/ext_prop_display.json
@@ -0,0 +1 @@
+{}
diff --git a/src/core/css/webcaosdb.css b/src/core/css/webcaosdb.css
index 1c8f50be049d308efcac25e41d705b08d96d21e6..248f3444962a839193016a42dc4e9f730ba7d448 100644
--- a/src/core/css/webcaosdb.css
+++ b/src/core/css/webcaosdb.css
@@ -381,6 +381,14 @@ h5 {
     margin-right: 0px;
 }
 
+body[data-hidden-properties="true"] .caosdb-v-hidden-property {
+    display: None;
+}
+
+body[data-hidden-properties="true"] .caosdb-v-entity-being-edited .caosdb-v-hidden-property {
+    display: unset;
+}
+
 .caosdb-v-edit-drag {
     padding: 5px;
 }
diff --git a/src/core/js/edit_mode.js b/src/core/js/edit_mode.js
index 63b28b08a6c07d4c5b00795f5e9f87d1965e4031..ba478dcd90cc9f6899e678f543d1c928129e1df7 100644
--- a/src/core/js/edit_mode.js
+++ b/src/core/js/edit_mode.js
@@ -1359,6 +1359,7 @@ var edit_mode = new function () {
         doc.firstElementChild.appendChild(newrecord.firstElementChild);
         // TODO I dunno whats wrong here: xml -> str -> xml ???
         var x = await transformation.transformEntities(str2xml(xml2str(doc)));
+        $(x[0]).addClass("caosdb-v-entity-being-edited");
         return x[0];
     }
 
@@ -1618,6 +1619,8 @@ var edit_mode = new function () {
             app.entity = $(entity).clone(true)[0];
             // remove preview stuff
             $(app.entity).find(".caosdb-preview-container").remove();
+            // add class for styling the entity that's being edited
+            $(app.entity).addClass("caosdb-v-entity-being-edited");
             edit_mode.smooth_replace(app.old, app.entity);
 
             edit_mode.add_save_button(app.entity, () => app.update(app.entity));
@@ -1897,7 +1900,9 @@ var edit_mode = new function () {
 
     this.create_new_entity = async function (role) {
         var empty_entity = str2xml('<Response><' + role + '/></Response>');
-        return (await transformation.transformEntities(empty_entity))[0];
+        var ent_element = await transformation.transformEntities(empty_entity);
+        $(ent_element).addClass("caosdb-v-entity-being-edited");
+        return ent_element[0];
     }
 
     this.remove_cancel_button = function (entity) {
@@ -2097,4 +2102,4 @@ var edit_mode = new function () {
  */
 $(document).ready(function () {
     edit_mode.init();
-});
\ No newline at end of file
+});
diff --git a/src/core/js/ext_prop_display.js b/src/core/js/ext_prop_display.js
new file mode 100644
index 0000000000000000000000000000000000000000..802e49c97bb0cac017e8e0bab013001098b848c1
--- /dev/null
+++ b/src/core/js/ext_prop_display.js
@@ -0,0 +1,209 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2022 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';
+
+/**
+ * @requires jQuery (library)
+ * @requires log (singleton from loglevel library)
+ * @requires load_config (function from webcaosdb.js)
+ */
+var prop_display = new function ($, edit_mode, getEntityName, getEntityRole, getPropertyElements, getPropertyName, getUserName, getUserRoles, logger, load_config, query) {
+
+    /**
+     * Return the property-display config file; `ext_prop_display.json` by
+     * default.
+     *
+     * @param {string} resource - file name of the config file
+     */
+    this.load_config = async function (resource) {
+
+        var conf = {};
+        try {
+            resource = resource || "ext_prop_display.json";
+            conf = await load_config(resource);
+        } catch (err) {
+            logger.error(err);
+        }
+
+        return conf;
+    }
+
+    this.getEntitiesInView = function () {
+        // Use all entities, both in entity panel and in preview.
+        return $(".caosdb-entity-panel,.caosdb-entity-preview");
+    }
+
+    this.unhideProperties = function (entities, conf, allTypes, userName, userRoles) {
+
+        for (let ent of entities) {
+            let parents = getParents(ent).map(par => par.name);
+            let properties = getPropertyElements(ent);
+            // either the entity has matching parents OR it is the actual
+            // RecordType for which a rule is written.
+            if (parents.some(par => allTypes.allTypesOrChildren.includes(par)) ||
+                (getEntityRole(ent) == "RecordType" && allTypes.allTypesOrChildren.includes(getEntityName(ent)))) {
+                // we know that there is at least one rule for this type (it is
+                // in `allTypes.allTypesOrChildren`), but we don't know which tp
+                // apply yet.
+                for (let typeName of Object.keys(conf)) {
+                    let typeConf = conf[typeName];
+                    let allNames = allTypes.typesWithChildren[typeName];
+                    // only hide something if there is a match in at least one parent type
+                    if (parents.some(par => allNames.includes(par)) ||
+                        (getEntityRole(ent) == "RecordType" && allNames.includes(getEntityName(ent)))) {
+                        properties.forEach((prop, index) => {
+                            if (this._hide_property(getPropertyName(prop), userName, userRoles, typeConf)) {
+                                // Should be hidden by default but better safe than sorry
+                                $(prop).addClass("caosdb-v-hidden-property").removeClass("caosdb-v-show-property");
+                            } else {
+                                // show this property
+                                $(prop).addClass("caosdb-v-show-property").removeClass("caosdb-v-hidden-property");
+                            }
+                        });
+                    }
+                }
+            } else {
+                // no rules for this RecordType, so show all properties
+                properties.forEach((prop, index) => $(prop).addClass("caosdb-v-show-property").removeClass("caosdb-v-hidden-property"));
+            }
+        }
+    }
+
+    this._hide_property = function (propname, userName, userRoles, conf) {
+
+        // is this property only shown for certain users/groups?
+        if ((conf.show != undefined) && conf.show.length > 0) {
+            for (let def of conf.show) {
+                if (propname.toLowerCase() == def.name.toLowerCase()) {
+                    if (!(def.users.includes(userName)) && !(userRoles.some(role => def.roles.includes(role)))) {
+                        return true
+                    }
+                }
+            }
+        }
+
+        // is this property hidden for certain users/groups?
+        if ((conf.hide != undefined) && conf.hide.length > 0) {
+            for (let def of conf.hide) {
+                if (propname.toLowerCase() == def.name.toLowerCase()) {
+                    if (def.users.includes(userName) || userRoles.some(role => def.roles.includes(role))) {
+                        return true
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
+
+    this._getRecordTypes = async function (conf) {
+
+        const parentTypes = Object.keys(conf);
+
+        var typesWithChildren = {};
+        var allTypesOrChildren = [];
+
+        for (let parentName of parentTypes) {
+            const children = await query(`FIND RECORDTYPE "${parentName}"`);
+            const names = children.map(ent => getEntityName(ent));
+            typesWithChildren[parentName] = names;
+            allTypesOrChildren = allTypesOrChildren.concat(names);
+        }
+
+        return {
+            "typesWithChildren": typesWithChildren,
+            "allTypesOrChildren": allTypesOrChildren
+        };
+    }
+
+    this.unhideAllProperties = function () {
+        // Just show all initially hidden properties
+        $(".caosdb-v-hidden-property").removeClass("caosdb-v-hidden-property");
+    }
+
+    this._unhideAllPropertiesWrapper = function (original) {
+        // construct a function that wirst does the original work, then unhides
+        // all properties, then returns the original return values.
+        const result = function (entity) {
+            var original_return = undefined;
+            if (typeof original === "function") {
+                original_return = original(entity);
+            }
+            prop_display.unhideAllProperties();
+            return original_return;
+        }
+
+        return result;
+    }
+
+    this._unhidePropertiesWrapper = function (original, conf, allTypes) {
+        // Same as above, but for when there actually may be something to hide
+        // (i.e., build variable is set and config is non-empty).
+        const result = function (entitiy) {
+            var original_return = undefined;
+            if (typeof original === "function") {
+                original_return = original(entitiy);
+            }
+            var entities = prop_display.getEntitiesInView();
+            const userName = getUserName();
+            const userRoles = getUserRoles();
+            prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+            return original_return;
+        }
+
+        return result;
+    }
+
+    this.init = async function () {
+        const conf = await this.load_config();
+        if (Object.keys(conf).length > 0) {
+            const allTypes = await this._getRecordTypes(conf);
+            var entities = this.getEntitiesInView();
+            const userName = getUserName();
+            const userRoles = getUserRoles();
+            this.unhideProperties(entities, conf, allTypes, userName, userRoles);
+            // If we are in the edit mode, (un)hide properties after ending
+            // the editing of an entity
+            document.body.addEventListener(edit_mode.start_edit.type, (e) => {
+                edit_mode.app.onAfterShowResults = this._unhidePropertiesWrapper(edit_mode.app.onAfterShowResults, conf, allTypes);
+            }, true);
+
+        } else {
+            // There are no properties to be hidden, so make this clear in HTML body
+            $("body").attr("data-hidden-properties", "false")
+            this.unhideAllProperties();
+            document.body.addEventListener(edit_mode.start_edit.type, (e) => {
+                // also unhide properties when leaving the edit mode
+                // TODO(fspreck): We're lacking a proper state/event here in the
+                // edit mode, so do this on "init", since this is the state to which
+                // the state machine returns after either successfully saving an
+                // entity or canceling the edit.
+                edit_mode.app.onAfterShowResults = this._unhideAllPropertiesWrapper(edit_mode.app.onAfterShowResults);
+            }, true);
+        }
+    }
+}($, edit_mode, getEntityName, getEntityRole, getPropertyElements, getPropertyName, getUserName, getUserRoles, log.getLogger("ext_prop_display"), load_config, query);
+
+$(document).ready(() => {
+    if ("${BUILD_MODULE_EXT_PROPERTY_DISPLAY}" == "ENABLED") {
+        caosdb_modules.register(prop_display);
+    }
+});
diff --git a/src/core/webcaosdb.xsl b/src/core/webcaosdb.xsl
index 9a0d6769f1901e860c7a2318fce28957d6461496..38b6461e8793d9063f1d8f0047df9232c138da54 100644
--- a/src/core/webcaosdb.xsl
+++ b/src/core/webcaosdb.xsl
@@ -66,6 +66,14 @@
         <xsl:call-template name="caosdb-head-js" />
       </head>
       <body>
+        <xsl:choose>
+          <xsl:when test="'${BUILD_MODULE_EXT_PROPERTY_DISPLAY}'='ENABLED'">
+            <xsl:attribute name="data-hidden-properties">true</xsl:attribute>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:attribute name="data-hidden-properties">false</xsl:attribute>
+          </xsl:otherwise>
+        </xsl:choose>
         <xsl:attribute name="data-response-count">
           <xsl:value-of select="/Response/@count"/>
         </xsl:attribute>
diff --git a/src/core/xsl/entity.xsl b/src/core/xsl/entity.xsl
index 82de9e416c2e28f21cd3f386cc6e04419284dc5f..264136f09828dce4e2ba7d324ff09fc14db5842c 100644
--- a/src/core/xsl/entity.xsl
+++ b/src/core/xsl/entity.xsl
@@ -232,7 +232,15 @@
   </xsl:template>
   <!-- PROPERTIES -->
   <xsl:template match="Property" mode="entity-body">
-    <li class="list-group-item caosdb-v-property-row caosdb-f-entity-property">
+    <li>
+      <xsl:choose>
+        <xsl:when test="'${BUILD_MODULE_EXT_PROPERTY_DISPLAY}'='ENABLED'">
+          <xsl:attribute name="class">list-group-item caosdb-v-property-row caosdb-f-entity-property caosdb-v-hidden-property</xsl:attribute>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:attribute name="class">list-group-item caosdb-v-property-row caosdb-f-entity-property</xsl:attribute>
+        </xsl:otherwise>
+      </xsl:choose>
       <xsl:attribute name="id">
         <xsl:value-of select="generate-id()"/>
       </xsl:attribute>
diff --git a/src/doc/extension/display_of_properties.rst b/src/doc/extension/display_of_properties.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b5fdfe30198cf5042be6ca940fdcf67220d834d9
--- /dev/null
+++ b/src/doc/extension/display_of_properties.rst
@@ -0,0 +1,86 @@
+Tweaking the display of properties
+==================================
+
+Hide or show properties for specific roles and users
+****************************************************
+
+.. note::
+
+   This feature is part of CaosDB WebUI 0.10 and is not available for 0.9.X or
+   older.
+
+.. warning::
+
+   Hiding properties is purely cosmetics and should **never** be considered a
+   security feature. The hidden properties are still part of the server
+   response.
+
+Sometimes it is desirable to hide certain properties for specific users or
+roles, e.g., when they might be irrelevant or confusing. For example, an
+internal id might only be of interest to curators or administrators, whereas it
+is entirely meaningless to most other users.
+
+To configure the hiding of properties, you first need to enable the build
+variable ``BUILD_MODULE_EXT_PROPERTY_DISPLAY``. Then, the display of the
+properties is configured in ``conf/ext/json/ext_prop_display.json``. In there,
+properties of a specific RecordType can be hidden by specifying the name of the
+property, and the names of the roles and/or users from whom it should be hidden.
+
+.. code-block:: json
+
+   {
+     "RecordTypeName": {
+       "hide": [
+         {
+           "name": "property name",
+           "roles": ["list", "of", "roles"],
+           "users": ["list", "of", "users"]
+         },
+         ...
+       ]
+     },
+     ...
+   }
+
+In the same way but using the ``show`` keyword, properties can be hidden for
+everyone **but** the specified users/roles:
+
+.. code-block:: json
+
+   {
+     "RecordTypeName": {
+       "show": [
+         {
+           "name": "property name",
+           "roles": ["list", "of", "roles"],
+           "users": ["list", "of", "users"]
+         },
+         ...
+       ]
+     },
+     ...
+   }
+ 
+
+For example, using the data from demo.indiscale.com, the following would hide
+the ``price`` of all ``MusicalInstruments`` from ``anonymous`` and their
+``Manufacturer`` from the ``admin`` user.
+
+.. code-block:: json
+
+   {
+     "MusicalInstrument": {
+       "hide": [
+         {"name": "price", "roles": ["anonymous"], "users": []},
+         {"name": "Manufacturer", "roles": [], "users": ["admin"]}
+       ]
+     }
+   }
+
+Future
+******
+
+In the future, this feature will be extended to allow the RecordType-wise
+`sorting <https://gitlab.com/caosdb/caosdb-webui/-/issues/189>`__ of properties
+and to `toggle <https://gitlab.com/caosdb/caosdb-webui/-/issues/190>`__
+properties.
diff --git a/test/core/js/modules/ext_prop_display.js.js b/test/core/js/modules/ext_prop_display.js.js
new file mode 100644
index 0000000000000000000000000000000000000000..18d2c54056609bf0cc6aafca2d06705ad7a40b02
--- /dev/null
+++ b/test/core/js/modules/ext_prop_display.js.js
@@ -0,0 +1,263 @@
+/*
+ * This file is a part of the CaosDB Project.
+ *
+ * Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
+ * Copyright (C) 2022 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';
+
+QUnit.module("ext_prop_display.js", {
+    before: function (assert) {
+        // setup before module
+    },
+    beforeEach: function (assert) {
+        // setup before each test
+        // entity list, one entity with three properties (ids 1,2,3 for
+        // testing), all of them hidden by default.
+        $(document.body).append('<div class="caosdb-f-main-entities prop-display-test-entities"><div id=115 class="caosdb-entity-panel"><div class="caosdb-entity-panel-heading"><span class="caosdb-f-parent-list"><span class="caosdb-parent-item"><a class="caosdb-parent-name" href="https://demo.indiscale.com/Entity/110">Guitar</a></span></span></div><div class="caosdb-entity-panel-body"><ul class="list-group caosdb-properties"><li id=1 class="caosdb-v-property-row caosdb-f-entity-property caosdb-v-hidden-property"><div class="row"><div class="caosdb-v-property-left-col"><span class="caosdb-property-name">first prop</span></div><div class="caosdb-f-property-value"><span class="caosdb-f-property-single-raw-value caosdb-property-text-value caosdb-f-property-text-value caosdb-v-property-text-value">48.0</span><span class="caosdb-unit">€</span></div></div></li><li id=2 class="caosdb-v-property-row caosdb-f-entity-property caosdb-v-hidden-property"><div class="row"><div class="caosdb-v-property-left-col"><span class="caosdb-property-name">second prop</span></div><div class="caosdb-f-property-value"><span class="caosdb-f-property-single-raw-value caosdb-property-text-value caosdb-f-property-text-value caosdb-v-property-text-value">48.0</span><span class="caosdb-unit">€</span></div></div></li><li id=3 class="caosdb-v-property-row caosdb-f-entity-property caosdb-v-hidden-property"><div class="row"><div class="caosdb-v-property-left-col"><span class="caosdb-property-name">third prop</span></div><div class="caosdb-f-property-value"><span class="caosdb-f-property-single-raw-value caosdb-property-text-value caosdb-f-property-text-value caosdb-v-property-text-value">48.0</span><span class="caosdb-unit">€</span></div></div></li></ul></div></div></div>');
+    },
+    afterEach: function (assert) {
+        // teardown after each test
+        $(".prop-display-test-entities").remove();
+    },
+    after: function (assert) {
+        // teardown after module
+    }
+});
+
+QUnit.test("unhide all properties", function (assert) {
+    assert.ok(prop_display.unhideAllProperties, "unhideAllProperties available");
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all properties hidden initially");
+    prop_display.unhideAllProperties();
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 0, "no hidden properties after unhiding");
+});
+
+QUnit.test("hide properties garbage type", function (assert) {
+    assert.ok(prop_display.getEntitiesInView, "getEntitiesInView available");
+    assert.ok(prop_display.unhideProperties, "unhideProperties available");
+    const conf = {
+        "DoesntExist": {
+            "hide": [{
+                "name": "first prop",
+                "roles": ["some_role"],
+                "users": ["someone"]
+            }]
+        }
+    };
+    // only one garbage type
+    const allTypes = {
+        "typesWithChildren": {
+            "DoesntExist": ["DoesntExist"]
+        },
+        "allTypesOrChildren": ["DoesntExist"]
+    };
+    const userName = "someone";
+    const userRoles = ["some_role", "some_other_role"];
+    const entities = prop_display.getEntitiesInView();
+    assert.equal(entities.length, 1, "only one entity in test data");
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all properties hidden initially");
+    prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 0, "no garbage-type entity, so no hidden properties");
+    assert.equal($(document).find(".caosdb-v-show-property").length, 3, "all properties are being shown");
+});
+
+QUnit.test("hide properties garbage property", function (assert) {
+    assert.ok(prop_display.getEntitiesInView, "getEntitiesInView available");
+    assert.ok(prop_display.unhideProperties, "unhideProperties available");
+    const conf = {
+        "MusicalInstrument": {
+            "hide": [{
+                "name": "prop does not exist",
+                "roles": ["some_role"],
+                "users": ["someone"]
+            }]
+        }
+    };
+    const allTypes = {
+        "typesWithChildren": {
+            "MusicalInstrument": ["MusicalInstrument", "Guitar"]
+        },
+        "allTypesOrChildren": ["MusicalInstrument", "Guitar"]
+    };
+    const userName = "someone";
+    const userRoles = ["some_role", "some_other_role"];
+    const entities = prop_display.getEntitiesInView();
+    assert.equal(entities.length, 1, "only one entity in test data");
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all properties hidden initially");
+    prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 0, "no garbage property, so no hidden properties");
+    assert.equal($(document).find(".caosdb-v-show-property").length, 3, "all properties are being shown");
+});
+
+
+QUnit.test("hide properties", function (assert) {
+    assert.ok(prop_display.getEntitiesInView, "getEntitiesInView available");
+    assert.ok(prop_display.unhideProperties, "unhideProperties available");
+    const conf = {
+        "MusicalInstrument": {
+            "hide": [{
+                    "name": "first prop",
+                    "roles": ["some_role"],
+                    "users": ["someone"]
+                },
+                {
+                    "name": "second prop",
+                    "roles": [],
+                    "users": ["someone else"]
+                },
+                {
+                    "name": "third prop",
+                    "roles": ["some_other_role"],
+                    "users": ["someone else"]
+                }
+            ]
+        }
+    };
+    const allTypes = {
+        "typesWithChildren": {
+            "MusicalInstrument": ["MusicalInstrument", "Guitar"]
+        },
+        "allTypesOrChildren": ["MusicalInstrument", "Guitar"]
+    };
+    var userName = "someone";
+    var userRoles = ["some_role"];
+    const entities = prop_display.getEntitiesInView();
+    assert.equal(entities.length, 1, "only one entity in test data");
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all properties hidden initially");
+    prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 1, "exactly one hidden property");
+    assert.equal($(document).find(".caosdb-v-show-property").length, 2, "the remaining two are shown");
+    assert.equal($("#1").hasClass("caosdb-v-hidden-property"), true, "first prop hidden");
+    assert.equal($("#2").hasClass("caosdb-v-show-property"), true, "second prop shown");
+    assert.equal($("#3").hasClass("caosdb-v-show-property"), true, "third prop shown");
+
+    // reset
+    prop_display.unhideProperties(entities, conf, allTypes, "", []);
+    assert.equal($(document).find(".caosdb-v-show-property").length, 3, "all shown after reset");
+
+    userRoles = ["some_other_role"];
+    prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 2, "two hidden properties");
+    assert.equal($(document).find(".caosdb-v-show-property").length, 1, "the remaining one is shown");
+    assert.equal($("#1").hasClass("caosdb-v-hidden-property"), true, "first prop hidden");
+    assert.equal($("#2").hasClass("caosdb-v-show-property"), true, "second prop shown");
+    assert.equal($("#3").hasClass("caosdb-v-hidden-property"), true, "third prop hidden");
+
+    // reset
+    prop_display.unhideProperties(entities, conf, allTypes, "", []);
+    assert.equal($(document).find(".caosdb-v-show-property").length, 3, "all shown after reset");
+
+    userName = "someone else";
+    prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 2, "two hidden properties");
+    assert.equal($(document).find(".caosdb-v-show-property").length, 1, "the remaining one is shown");
+    assert.equal($("#1").hasClass("caosdb-v-show-property"), true, "first prop shown");
+    assert.equal($("#2").hasClass("caosdb-v-hidden-property"), true, "second prop hidden");
+    assert.equal($("#3").hasClass("caosdb-v-hidden-property"), true, "third prop hidden");
+
+    // reset
+    prop_display.unhideProperties(entities, conf, allTypes, "", []);
+    assert.equal($(document).find(".caosdb-v-show-property").length, 3, "all shown after reset");
+
+    userRoles = ["some_role", "some_other_role"]
+    prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "two hidden properties");
+    assert.equal($(document).find(".caosdb-v-show-property").length, 0, "None is shown");
+    assert.equal($("#1").hasClass("caosdb-v-hidden-property"), true, "first prop hidden");
+    assert.equal($("#2").hasClass("caosdb-v-hidden-property"), true, "second prop hidden");
+    assert.equal($("#3").hasClass("caosdb-v-hidden-property"), true, "third prop hidden");
+
+});
+
+QUnit.test("show properties", function (assert) {
+    assert.ok(prop_display.getEntitiesInView, "getEntitiesInView available");
+    assert.ok(prop_display.unhideProperties, "unhideProperties available");
+    const conf = {
+        "MusicalInstrument": {
+            "show": [{
+                    "name": "first prop",
+                    "roles": ["some_role"],
+                    "users": ["someone"]
+                },
+                {
+                    "name": "second prop",
+                    "roles": [],
+                    "users": ["someone else"]
+                },
+                {
+                    "name": "third prop",
+                    "roles": ["some_other_role"],
+                    "users": ["someone else"]
+                }
+            ]
+        }
+    };
+    const allTypes = {
+        "typesWithChildren": {
+            "MusicalInstrument": ["MusicalInstrument", "Guitar"]
+        },
+        "allTypesOrChildren": ["MusicalInstrument", "Guitar"]
+    };
+    var userName = "someone";
+    var userRoles = ["some_role"];
+    const entities = prop_display.getEntitiesInView();
+    assert.equal(entities.length, 1, "only one entity in test data");
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all properties hidden initially");
+    prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 2, "two hidden properties");
+    assert.equal($(document).find(".caosdb-v-show-property").length, 1, "the remaining one shown");
+    assert.equal($("#1").hasClass("caosdb-v-show-property"), true, "first prop shown");
+    assert.equal($("#2").hasClass("caosdb-v-hidden-property"), true, "second prop hidden");
+    assert.equal($("#3").hasClass("caosdb-v-hidden-property"), true, "third prop hidden");
+
+    // reset
+    prop_display.unhideProperties(entities, conf, allTypes, "", []);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all hidden after reset");
+
+    userRoles = ["some_other_role"];
+    prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 1, "one hidden properties");
+    assert.equal($(document).find(".caosdb-v-show-property").length, 2, "the remaining two are shown");
+    assert.equal($("#1").hasClass("caosdb-v-show-property"), true, "first prop shown");
+    assert.equal($("#2").hasClass("caosdb-v-hidden-property"), true, "second prop hidden");
+    assert.equal($("#3").hasClass("caosdb-v-show-property"), true, "third prop shown");
+
+    // reset
+    prop_display.unhideProperties(entities, conf, allTypes, "", []);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all hidden after reset");
+
+    userName = "someone else";
+    prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 1, "one hidden property");
+    assert.equal($(document).find(".caosdb-v-show-property").length, 2, "the remaining ones are shown");
+    assert.equal($("#1").hasClass("caosdb-v-hidden-property"), true, "first prop hidden");
+    assert.equal($("#2").hasClass("caosdb-v-show-property"), true, "second prop shown");
+    assert.equal($("#3").hasClass("caosdb-v-show-property"), true, "third prop shown");
+
+    // reset
+    prop_display.unhideProperties(entities, conf, allTypes, "", []);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 3, "all hidden after reset");
+
+    userRoles = ["some_role", "some_other_role"]
+    prop_display.unhideProperties(entities, conf, allTypes, userName, userRoles);
+    assert.equal($(document).find(".caosdb-v-hidden-property").length, 0, "no hidden properties");
+    assert.equal($(document).find(".caosdb-v-show-property").length, 3, "All are shown");
+    assert.equal($("#1").hasClass("caosdb-v-show-property"), true, "first prop shown");
+    assert.equal($("#2").hasClass("caosdb-v-show-property"), true, "second prop shown");
+    assert.equal($("#3").hasClass("caosdb-v-show-property"), true, "third prop shown");
+
+});