diff --git a/entity/360Degree_entity/entityfields/personobjects/children/objecttype_param/valueProcess.js b/entity/360Degree_entity/entityfields/personobjects/children/objecttype_param/valueProcess.js
index 0764eff7f7bb307932d6c09366ff99f6431a1114..3d9c6d75f1d5026e6e31e985752e51b6a88a9262 100644
--- a/entity/360Degree_entity/entityfields/personobjects/children/objecttype_param/valueProcess.js
+++ b/entity/360Degree_entity/entityfields/personobjects/children/objecttype_param/valueProcess.js
@@ -4,11 +4,22 @@ import("system.result");
 var res = { 
     "Offer": {}, 
     "Order": {
-        "setGroupBy":"ORDERTYPE"
+        "setGroupBy":"ORDERTYPE",
+        "groupByKeyword":"OrderType"
     }, 
     "Contract": {}, 
-    "SupportTicket": {},
-    "Campaign": {}
-}
+    "SupportTicket": {        
+        "subContext":"TaskLink",
+        "childField":"TASK_ID",
+        "parentField":"TASK_TASKID",
+        "contactIdField":"OBJECT_ROWID"
+    },
+    "Campaign": {        
+        "subContext":"CampaignParticipant",
+        "childField":"CAMPAIGN_ID",
+        "parentField":"CAMPAIGNID",
+        "contactIdField":"CONTACT_ID"
+    }
+};
 
-result.string(JSON.stringify(res))
\ No newline at end of file
+result.string(JSON.stringify(res));
\ No newline at end of file
diff --git a/entity/CampaignCost_entity/CampaignCost_entity.aod b/entity/CampaignCost_entity/CampaignCost_entity.aod
index 3f3d974f90dfa7cca37d59ae84a4153b1e0d8d94..7652bce2911c9999a838ab39eefdafe3bdbdc17c 100644
--- a/entity/CampaignCost_entity/CampaignCost_entity.aod
+++ b/entity/CampaignCost_entity/CampaignCost_entity.aod
@@ -175,8 +175,8 @@
           <valueProcess>%aditoprj%/entity/CampaignCost_entity/entityfields/keywordcampaignmanagementcostcategory/children/containername_param/valueProcess.js</valueProcess>
         </entityParameter>
         <entityParameter>
-          <name>ExcludedKeyIdsSubquery_param</name>
-          <valueProcess>%aditoprj%/entity/CampaignCost_entity/entityfields/keywordcampaignmanagementcostcategory/children/excludedkeyidssubquery_param/valueProcess.js</valueProcess>
+          <name>BlacklistIds_param</name>
+          <valueProcess>%aditoprj%/entity/CampaignCost_entity/entityfields/keywordcampaignmanagementcostcategory/children/blacklistids_param/valueProcess.js</valueProcess>
         </entityParameter>
       </children>
       <dependency>
diff --git a/entity/CampaignCost_entity/entityfields/keywordcampaignmanagementcostcategory/children/excludedkeyidssubquery_param/valueProcess.js b/entity/CampaignCost_entity/entityfields/keywordcampaignmanagementcostcategory/children/blacklistids_param/valueProcess.js
similarity index 84%
rename from entity/CampaignCost_entity/entityfields/keywordcampaignmanagementcostcategory/children/excludedkeyidssubquery_param/valueProcess.js
rename to entity/CampaignCost_entity/entityfields/keywordcampaignmanagementcostcategory/children/blacklistids_param/valueProcess.js
index d918b95bfcb392e62eeee2bc4e77dacf8b9efd29..64277edeb0afa5bd2ed0c3a9bf86700a54343e6e 100644
--- a/entity/CampaignCost_entity/entityfields/keywordcampaignmanagementcostcategory/children/excludedkeyidssubquery_param/valueProcess.js
+++ b/entity/CampaignCost_entity/entityfields/keywordcampaignmanagementcostcategory/children/blacklistids_param/valueProcess.js
@@ -12,9 +12,9 @@ var cond = newSelect("CATEGORY")
 if (vars.get("$field.CAMPAIGNSTEP_ID"))
     cond.andIfSet("CAMPAIGNCOST.CAMPAIGNSTEP_ID", "$field.CAMPAIGNSTEP_ID");
 else
-    cond.and("CAMPAIGNCOST.CAMPAIGNSTEP_ID is null")
+    cond.and("CAMPAIGNCOST.CAMPAIGNSTEP_ID is null");
     
 if (vars.get("$sys.recordstate") != neon.OPERATINGSTATE_NEW)
     cond.andIfSet("CAMPAIGNCOST.CAMPAIGNCOSTID", "$field.CAMPAIGNCOSTID", SqlBuilder.NOT_EQUAL());
 
-result.string(cond.toString());
\ No newline at end of file
+result.string(JSON.stringify(cond.arrayColumn()));
\ No newline at end of file
diff --git a/entity/CampaignPlanning_entity/CampaignPlanning_entity.aod b/entity/CampaignPlanning_entity/CampaignPlanning_entity.aod
index 44826e7cfe2893a5f59628e874e9c59f0b843d84..62ae8a76e9ae487f97b651173b78bc4c8acbf3ff 100644
--- a/entity/CampaignPlanning_entity/CampaignPlanning_entity.aod
+++ b/entity/CampaignPlanning_entity/CampaignPlanning_entity.aod
@@ -99,58 +99,6 @@
     </entityProvider>
   </entityFields>
   <recordContainers>
-    <dbRecordContainer>
-      <name>recordContainer</name>
-      <alias>Data_alias</alias>
-      <fromClauseProcess>%aditoprj%/entity/CampaignPlanning_entity/recordcontainers/recordcontainer/fromClauseProcess.js</fromClauseProcess>
-      <orderClauseProcess>%aditoprj%/entity/CampaignPlanning_entity/recordcontainers/recordcontainer/orderClauseProcess.js</orderClauseProcess>
-      <linkInformation>
-        <linkInformation>
-          <name>091a323d-d1e8-4eec-8f33-0c91bc7f62af</name>
-          <tableName>CAMPAIGN</tableName>
-          <primaryKey>CAMPAIGNID</primaryKey>
-          <isUIDTable v="false" />
-          <readonly v="true" />
-        </linkInformation>
-        <linkInformation>
-          <name>2df8e267-6c25-4bae-bd0f-2b7b4399cc2b</name>
-          <tableName>CAMPAIGNSTEP</tableName>
-          <primaryKey>CAMPAIGNSTEPID</primaryKey>
-          <isUIDTable v="true" />
-          <readonly v="false" />
-        </linkInformation>
-      </linkInformation>
-      <recordFieldMappings>
-        <dbRecordFieldMapping>
-          <name>CAMPAIGN_ID.value</name>
-          <recordfield>CAMPAIGN.CAMPAIGNID</recordfield>
-        </dbRecordFieldMapping>
-        <dbRecordFieldMapping>
-          <name>DATE_END.value</name>
-          <recordfield>CAMPAIGNSTEP.DATE_END</recordfield>
-          <isFilterable v="true" />
-          <isLookupFilter v="true" />
-        </dbRecordFieldMapping>
-        <dbRecordFieldMapping>
-          <name>DATE_START.value</name>
-          <recordfield>CAMPAIGNSTEP.DATE_START</recordfield>
-          <isFilterable v="true" />
-          <isLookupFilter v="true" />
-        </dbRecordFieldMapping>
-        <dbRecordFieldMapping>
-          <name>PREDECESSOR_STEP_ID.value</name>
-          <recordfield>CAMPAIGNSTEP.PREDECESSORSTEP_ID</recordfield>
-        </dbRecordFieldMapping>
-        <dbRecordFieldMapping>
-          <name>STEP_DESCRIPTION.value</name>
-          <recordfield>CAMPAIGNSTEP.DESCRIPTION</recordfield>
-        </dbRecordFieldMapping>
-        <dbRecordFieldMapping>
-          <name>STEP_ID.value</name>
-          <recordfield>CAMPAIGNSTEP.CAMPAIGNSTEPID</recordfield>
-        </dbRecordFieldMapping>
-      </recordFieldMappings>
-    </dbRecordContainer>
     <jDitoRecordContainer>
       <name>jditoRecordContainer</name>
       <jDitoRecordAlias>Data_alias</jDitoRecordAlias>
diff --git a/entity/CampaignPlanning_entity/recordcontainers/jditorecordcontainer/contentProcess.js b/entity/CampaignPlanning_entity/recordcontainers/jditorecordcontainer/contentProcess.js
index 9f1541d2f9b4b072bcdb0a64bdf3c8c84baf1d7a..53985b5cc50a47402b9df28330a3f2623c768f39 100644
--- a/entity/CampaignPlanning_entity/recordcontainers/jditorecordcontainer/contentProcess.js
+++ b/entity/CampaignPlanning_entity/recordcontainers/jditorecordcontainer/contentProcess.js
@@ -1,3 +1,4 @@
+import("system.logging");
 import("JditoFilter_lib");
 import("system.util");
 import("system.db");
@@ -26,34 +27,47 @@ if (isMariaDB)
     columnName = "\`NAME\`";
 }
 
-var stepsRows = stepCondition.select("CAMPAIGNSTEPID, CAMPAIGNSTEP."+ columnName + ", CAMPAIGNSTEP.PREDECESSORSTEP_ID,"
+stepCondition.select("CAMPAIGNSTEPID, CAMPAIGNSTEP."+ columnName + ", CAMPAIGNSTEP.PREDECESSORSTEP_ID,"
                                    + " CAMPAIGNSTEP.DESCRIPTION, CAMPAIGNSTEP.DATE_START, CAMPAIGNSTEP.DATE_END, CAMPAIGNID, CAMPAIGN."+ columnName +", CAMPAIGN.STATUS")
                              .from("CAMPAIGN")
-                             .join("CAMPAIGNSTEP", "CAMPAIGN.CAMPAIGNID = CAMPAIGNSTEP.CAMPAIGN_ID")
-                             .orderBy("SORTING")
-                             .table();
+                             .join("CAMPAIGNSTEP", "CAMPAIGN.CAMPAIGNID = CAMPAIGNSTEP.CAMPAIGN_ID");
+                             
+if (vars.exists("$local.idvalues") && vars.get("$local.idvalues"))
+   stepCondition.and("CAMPAIGNSTEP.CAMPAIGNSTEPID", vars.get("$local.idvalues"), SqlBuilder.IN());
 
-stepsRows = stepsRows.map(function ([stepId, stepName, predecessorId, desc, stepStartDate, stepEndDate, campaignId, campaignName, campaignStatus])
+var stepsRows = stepCondition.orderBy("SORTING").table();
+
+//this can happen when not a CampaignStep, but a Campaign node was selected
+if(stepsRows.length == 0 && vars.get("$local.idvalues") && newSelect("count(*)").from("CAMPAIGN").where("CAMPAIGN.CAMPAIGNID", vars.get("$local.idvalues")).cell() > 0)
 {
-    if (!campaignRows[campaignId])
-    {
-        let startDate = CampaignUtils.getCampaignStartDate(campaignId);
-        let endDate = CampaignUtils.getCampaignEndDate(campaignId);
-        campaignRows[campaignId] = [campaignId, campaignName, null, null, startDate, endDate, campaignId, TARGET_CONTEXT_ROOT_ELEMENT, campaignStatus];
-    }
-    stepIds[stepId] = true;
-    
-    return [stepId, stepName, predecessorId || campaignId, desc, stepStartDate, stepEndDate, campaignId, TARGET_CONTEXT_ELEMENT, campaignStatus];
-});
+    stepsRows = [[vars.get("$local.idvalues"), "", "", "", "", "", vars.get("$local.idvalues"), "", ""]]
+}
 
-//replace all predecessor ids that are not in the tree with the campaign id
-stepsRows.forEach(function (row)
+if(!vars.get("$local.idvalues"))
 {
-    if (!(stepIds[row[2]]))
-        row[2] = row[6];
-});
+    stepsRows = stepsRows.map(function ([stepId, stepName, predecessorId, desc, stepStartDate, stepEndDate, campaignId, campaignName, campaignStatus])
+    {
+        if (!campaignRows[campaignId])
+        {
+            let startDate = CampaignUtils.getCampaignStartDate(campaignId);
+            let endDate = CampaignUtils.getCampaignEndDate(campaignId);
+            campaignRows[campaignId] = [campaignId, campaignName, null, null, startDate, endDate, campaignId, TARGET_CONTEXT_ROOT_ELEMENT, campaignStatus];
+        }
+        stepIds[stepId] = true;
+
+        return [stepId, stepName, predecessorId || campaignId, desc, stepStartDate, stepEndDate, campaignId, TARGET_CONTEXT_ELEMENT, campaignStatus];
+    });
 
-//convert the object to an array
-campaignRows = Object.keys(campaignRows).map(function (id) {return campaignRows[id];});
+    //replace all predecessor ids that are not in the tree with the campaign id
+    stepsRows.forEach(function (row)
+    {
+        if (!(stepIds[row[2]]))
+            row[2] = row[6];
+    });
 
-result.object(campaignRows.concat(stepsRows))
+    //convert the object to an array
+    campaignRows = Object.keys(campaignRows).map(function (id) {return campaignRows[id];});
+    
+    stepsRows = campaignRows.concat(stepsRows);
+}
+result.object(stepsRows);
diff --git a/entity/CampaignPlanning_entity/recordcontainers/recordcontainer/fromClauseProcess.js b/entity/CampaignPlanning_entity/recordcontainers/recordcontainer/fromClauseProcess.js
deleted file mode 100644
index b007b4d58a7376e99c1fb0b20cfe88459dc89a4f..0000000000000000000000000000000000000000
--- a/entity/CampaignPlanning_entity/recordcontainers/recordcontainer/fromClauseProcess.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import("system.result");
-
-result.string("CAMPAIGN join CAMPAIGNSTEP on CAMPAIGN.CAMPAIGNID = CAMPAIGNSTEP.CAMPAIGN_ID")
\ No newline at end of file
diff --git a/entity/CampaignPlanning_entity/recordcontainers/recordcontainer/orderClauseProcess.js b/entity/CampaignPlanning_entity/recordcontainers/recordcontainer/orderClauseProcess.js
deleted file mode 100644
index 8538a702d13027d9d9739d9b6652105b87725638..0000000000000000000000000000000000000000
--- a/entity/CampaignPlanning_entity/recordcontainers/recordcontainer/orderClauseProcess.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import("system.result");
-import("system.db");
-
-result.object({"CAMPAIGNSTEP.SORTING": db.ASCENDING});
\ No newline at end of file
diff --git a/entity/CommRestriction_Entity/CommRestriction_Entity.aod b/entity/CommRestriction_Entity/CommRestriction_Entity.aod
index 60d0a7e5c5161c167310bb868be2f7e3aa4fcdd8..407e45312848c4daf741f99d7df616f30754ca5b 100644
--- a/entity/CommRestriction_Entity/CommRestriction_Entity.aod
+++ b/entity/CommRestriction_Entity/CommRestriction_Entity.aod
@@ -62,8 +62,8 @@
           <expose v="false" />
         </entityParameter>
         <entityParameter>
-          <name>ExcludedKeyIdsSubquery_param</name>
-          <valueProcess>%aditoprj%/entity/CommRestriction_Entity/entityfields/commrestrictionmedium/children/excludedkeyidssubquery_param/valueProcess.js</valueProcess>
+          <name>BlacklistIds_param</name>
+          <valueProcess>%aditoprj%/entity/CommRestriction_Entity/entityfields/commrestrictionmedium/children/blacklistids_param/valueProcess.js</valueProcess>
         </entityParameter>
       </children>
       <dependency>
diff --git a/entity/CommRestriction_Entity/entityfields/commrestrictionmedium/children/blacklistids_param/valueProcess.js b/entity/CommRestriction_Entity/entityfields/commrestrictionmedium/children/blacklistids_param/valueProcess.js
new file mode 100644
index 0000000000000000000000000000000000000000..c1f39ed63d29e2c0be655f3a075b7cd45cc6010b
--- /dev/null
+++ b/entity/CommRestriction_Entity/entityfields/commrestrictionmedium/children/blacklistids_param/valueProcess.js
@@ -0,0 +1,11 @@
+import("system.vars");
+import("Sql_lib");
+import("system.result");
+
+var alreadyUsedMediums = new SqlBuilder()
+    .select("MEDIUM")
+    .from("COMMRESTRICTION")
+    .where("COMMRESTRICTION.CONTACT_ID", vars.get("$field.CONTACT_ID"))
+    .arrayColumn();
+
+result.string(JSON.stringify(alreadyUsedMediums));
\ No newline at end of file
diff --git a/entity/CommRestriction_Entity/entityfields/commrestrictionmedium/children/excludedkeyidssubquery_param/valueProcess.js b/entity/CommRestriction_Entity/entityfields/commrestrictionmedium/children/excludedkeyidssubquery_param/valueProcess.js
deleted file mode 100644
index 50167db735b55a82144eadc0e440f38b57ce6de7..0000000000000000000000000000000000000000
--- a/entity/CommRestriction_Entity/entityfields/commrestrictionmedium/children/excludedkeyidssubquery_param/valueProcess.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import("system.question");
-import("system.vars");
-import("Sql_lib");
-import("system.result");
-
-var contactId = vars.get("$field.CONTACT_ID");
-var alreadyUsedMediums = new SqlBuilder().select("MEDIUM").from("COMMRESTRICTION").where("COMMRESTRICTION.CONTACT_ID", contactId);
-
-result.string(alreadyUsedMediums);
\ No newline at end of file
diff --git a/entity/Favorite_entity/recordcontainers/jditorecordcontainer/contentProcess.js b/entity/Favorite_entity/recordcontainers/jditorecordcontainer/contentProcess.js
index eafabfcf64d2ef9eaea73303673e6b186e7ab184..58d538be7dfd130e9c953f05ff1d7f2145d3e1ac 100644
--- a/entity/Favorite_entity/recordcontainers/jditorecordcontainer/contentProcess.js
+++ b/entity/Favorite_entity/recordcontainers/jditorecordcontainer/contentProcess.js
@@ -10,14 +10,13 @@ import("system.entities");
 
 if(vars.get("$local.idvalues") && vars.get("$local.idvalues").length != 0)
 {
-    var selected = vars.get("$local.idvalues");
-    var selectedFavos = [];
+    var seekedByCore = vars.get("$local.idvalues");
+    var seekedByCoreFavos = [];
     
-    for(h = 0; h < selected.length; h++)
-        selectedFavos.push(buildFavoriteForDeletion(favorite.getFavorites(favorite.createGetFavoriteByIdConfig()
-                                                                                    .setId(selected[h]))[0]));
+    var config = favorite.createGetFavoriteByIdConfig().setIds(seekedByCore);
+    seekedByCoreFavos = buildFavoritesForDeletion(favorite.getFavorites(config));
 
-    result.object(selectedFavos);
+    result.object(seekedByCoreFavos);
 }
 else
 {
@@ -32,12 +31,16 @@ else
     var finishedFavoritesForDisplay = [];
     
     var allContexts = getAllFavoriteEnabledContexts();
+    var allFavorites = favorite.getFavorites(favorite.createGetFavoritesConfig()
+                                                            .setUserId(tools.getCurrentUser()[tools.NAME]));
     for(i = 0; i < allContexts.length; i++)
     {
-        var allFavorites = favorite.getFavorites(favorite.createGetFavoritesConfig()
-                                                            .setUserId(tools.getCurrentUser()[tools.NAME])
-                                                            .setObjectType(allContexts[i]));
-        var builtFavos = buildFavorites(allFavorites, allContexts[i]);
+        var contextFavorites = [];
+        for(x = 0; x < allFavorites.length; x++)
+            if(allFavorites[x]["objecttype"] === allContexts[i])
+                contextFavorites.push(allFavorites[x])
+            
+        var builtFavos = buildFavorites(contextFavorites, allContexts[i]);
     
         for(let x = 0; x < builtFavos.length; x++)
         {
@@ -193,29 +196,36 @@ function getAllFavoriteEnabledContexts()
     return permittedContexts;
 }
 
-function buildFavoriteForDeletion(favo)
-{        
-    var groupid = favo["group"]["groupid"];
-    var grouptitle = favo["group"]["grouptitle"];
-    var grouptype = favo["group"]["grouptype"];
-    var objecttype = favo["objecttype"];
-    var image = "";
-    var rowid = favo["rowid"];
-    var title = "";
-    var id = favo["id"];
-    var user = favo["group"]["groupuser"];
-    
-    return [
-        groupid,
-        grouptitle,
-        grouptype, 
-        objecttype, 
-        image,
-        rowid,
-        title,
-        id,
-        user
-    ];
+function buildFavoritesForDeletion(favos)
+{      
+    var results = [];
+    
+    for(i = 0; i < favos.length; i++)
+    {
+        var groupid = favos[i]["group"]["groupid"];
+        var grouptitle = favos[i]["group"]["grouptitle"];
+        var grouptype = favos[i]["group"]["grouptype"];
+        var objecttype = favos[i]["objecttype"];
+        var image = "";
+        var rowid = favos[i]["rowid"];
+        var title = "";
+        var id = favos[i]["id"];
+        var user = favos[i]["group"]["groupuser"];
+    
+        results.push([
+            groupid,
+            grouptitle,
+            grouptype, 
+            objecttype, 
+            image,
+            rowid,
+            title,
+            id,
+            user
+        ]);
+    }
+    
+    return results;
 }
 
 
diff --git a/entity/Favorite_entity/recordcontainers/jditorecordcontainer/onDelete.js b/entity/Favorite_entity/recordcontainers/jditorecordcontainer/onDelete.js
index 5c5ab2b521e59ad3ba0e1b71bb5c99b51044fdf3..70ac8f02ca1518b3ce375c2f4c5901d08d2c1d4f 100644
--- a/entity/Favorite_entity/recordcontainers/jditorecordcontainer/onDelete.js
+++ b/entity/Favorite_entity/recordcontainers/jditorecordcontainer/onDelete.js
@@ -1,4 +1,5 @@
 import("system.vars");
 import("system.favorite");
 
-favorite.remove(favorite.createRemoveByIdConfig().setFavoriteRecordId(vars.get("$local.uid")));
\ No newline at end of file
+var selected = vars.get("$local.uid");
+favorite.remove(favorite.createRemoveMultipleByIdConfig().setFavoriteRecordIds([selected]));
\ No newline at end of file
diff --git a/entity/KeywordEntry_entity/KeywordEntry_entity.aod b/entity/KeywordEntry_entity/KeywordEntry_entity.aod
index 3472852e89848b764ea1dc9a2525a2e6bc300530..d897ddd5ff1732ea13f318d6834412bf0993cd20 100644
--- a/entity/KeywordEntry_entity/KeywordEntry_entity.aod
+++ b/entity/KeywordEntry_entity/KeywordEntry_entity.aod
@@ -639,7 +639,7 @@
       <name>ExcludedKeyIdsSubquery_param</name>
       <expose v="true" />
       <documentation>%aditoprj%/entity/KeywordEntry_entity/entityfields/excludedkeyidssubquery_param/documentation.adoc</documentation>
-      <description>PARAMETER</description>
+      <description>DEPRECATED!!! DO NOT USE!!!</description>
     </entityParameter>
     <entityParameter>
       <name>WhitelistIds_param</name>
@@ -661,6 +661,10 @@
       <name>#PROVIDER_AGGREGATES</name>
       <useAggregates v="true" />
     </entityProvider>
+    <entityParameter>
+      <name>BlacklistIds_param</name>
+      <expose v="true" />
+    </entityParameter>
   </entityFields>
   <recordContainers>
     <dbRecordContainer>
diff --git a/entity/KeywordEntry_entity/entityfields/excludedkeyidssubquery_param/documentation.adoc b/entity/KeywordEntry_entity/entityfields/excludedkeyidssubquery_param/documentation.adoc
index 55fde66fa9fdb8f3dfebb3341ea9f95471189c32..5992cb1957a8d69753478d3e19384a511371d90f 100644
--- a/entity/KeywordEntry_entity/entityfields/excludedkeyidssubquery_param/documentation.adoc
+++ b/entity/KeywordEntry_entity/entityfields/excludedkeyidssubquery_param/documentation.adoc
@@ -1,2 +1,5 @@
+DEPRECATED! This parameter does not work with caching, because if the result of the subquery changes while the subquery itself doesn't, the cached
+result won't be updated
+
 Can be filled with a subquery-string, which delivers KeyIds to exclude.
 The subquery is used insade a "KEYID not in(...)" 
\ No newline at end of file
diff --git a/entity/KeywordEntry_entity/recordcontainers/db/cacheKeyProcess.js b/entity/KeywordEntry_entity/recordcontainers/db/cacheKeyProcess.js
index bd9e8c01354097c564fcb6fd65e3e5390fbb9f10..822b1e5a4479fd59faf95e148e48d34d917071e2 100644
--- a/entity/KeywordEntry_entity/recordcontainers/db/cacheKeyProcess.js
+++ b/entity/KeywordEntry_entity/recordcontainers/db/cacheKeyProcess.js
@@ -1,6 +1,6 @@
 import("CachedRecordContainer_lib");
 import("system.result")
 
-var res = CachedRecordContainerUtils.getCommonKey("$param.ContainerName_param", "$param.ExcludedKeyIdsSubquery_param", "$param.OnlyActives_param"
+var res = CachedRecordContainerUtils.getCommonKey("$param.ContainerName_param", "$param.BlacklistIds_param", "$param.OnlyActives_param"
     , "$param.WhitelistIds_param");
 result.string(res);
diff --git a/entity/KeywordEntry_entity/recordcontainers/db/conditionProcess.js b/entity/KeywordEntry_entity/recordcontainers/db/conditionProcess.js
index c2921e933fff759455fdd5adefee3f8731fe79d3..90f58744de34c09de3164a324889dc3d3bf03859 100644
--- a/entity/KeywordEntry_entity/recordcontainers/db/conditionProcess.js
+++ b/entity/KeywordEntry_entity/recordcontainers/db/conditionProcess.js
@@ -1,3 +1,4 @@
+import("Util_lib");
 import("system.vars");
 import("system.db");
 import("system.result");
@@ -13,14 +14,16 @@ if (vars.get("$param.OnlyActives_param") == "true")
 if (vars.exists("$param.ExcludedKeyIdsSubquery_param") && vars.get("$param.ExcludedKeyIdsSubquery_param"))
     cond.and("AB_KEYWORD_ENTRY.KEYID not in (" + vars.get("$param.ExcludedKeyIdsSubquery_param") + ")");
 
-if (vars.getString("$param.WhitelistIds_param"))
+var whitelist = Utils.parseJSON(vars.get("$param.WhitelistIds_param"));
+var blacklist = Utils.parseJSON(vars.get("$param.BlacklistIds_param"));
+if (whitelist)
 {
-    var whitelist = JSON.parse(vars.getString("$param.WhitelistIds_param"));
-   
     if (whitelist.length > 0)
-        cond.and("AB_KEYWORD_ENTRY.KEYID", JSON.parse(vars.getString("$param.WhitelistIds_param")), SqlBuilder.IN());
+        cond.and("AB_KEYWORD_ENTRY.KEYID", whitelist, SqlBuilder.IN());
     else
         cond.noResult(); // force empty result if whitelist is empty
 }
+if (blacklist && blacklist.length > 0)
+    cond.and("AB_KEYWORD_ENTRY.KEYID", blacklist, SqlBuilder.NOT_IN());
 
 result.string(cond.toString());
diff --git a/entity/Notification_entity/Notification_entity.aod b/entity/Notification_entity/Notification_entity.aod
index 2668a0419e9a0b0d0d74c0f6f25867fd05c13faf..07dd08d1d45a3f702685d1a844e52d28ff548ddf 100644
--- a/entity/Notification_entity/Notification_entity.aod
+++ b/entity/Notification_entity/Notification_entity.aod
@@ -173,6 +173,7 @@
     <entityField>
       <name>SUBCATEGORY</name>
       <title>Subcategory</title>
+      <groupable v="true" />
     </entityField>
   </entityFields>
   <recordContainers>
diff --git a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
index 9efeb9f1343cec8f101ffd7f4549154d2659e3f7..b1e9c45fd04c10da89d571eac12e23f973b3fe3b 100644
--- a/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
+++ b/language/_____LANGUAGE_de/_____LANGUAGE_de.aod
@@ -8381,6 +8381,7 @@ Bitte Datumseingabe prüfen</value>
     </entry>
     <entry>
       <key>Subcategory</key>
+      <value>Unterkategorie</value>
     </entry>
     <entry>
       <key>The radius has to be at least %0.</key>
@@ -9999,6 +10000,9 @@ Bitte Datumseingabe prüfen</value>
     <entry>
       <key>My campaigns</key>
     </entry>
+    <entry>
+      <key>Subcategory</key>
+    </entry>
     <entry>
       <key>Campaign costs</key>
     </entry>
diff --git a/process/Contact_lib/process.js b/process/Contact_lib/process.js
index 7bbafdc693adac2eb70bf75da3d35f24790c6560..4a59839543915bbe0c16edfb2c56985efe935f15 100644
--- a/process/Contact_lib/process.js
+++ b/process/Contact_lib/process.js
@@ -528,10 +528,8 @@ ContactUtils.hasCommRestriction = function(pContactId, pMedium, pStartDate)
  */
 ContactUtils.getActiveCommRestrictionsSubselect = function()
 {
-    var mediumList = KeywordUtils.getEntryNamesAndIdsByContainer($KeywordRegistry.communicationMediumCampaign());
-    var sqlMasking = new SqlMaskingUtils();
-
-    var parts = [];
+    var mediumList = KeywordUtils.getResolvedTitleSqlPart($KeywordRegistry.communicationMediumCampaign(), "COMMRESTRICTION.MEDIUM");
+    var mask = new SqlMaskingUtils()
 
     var orgContactSubselect = newSelect("orgContact.CONTACTID")
                                     .from("CONTACT anyContact")
@@ -539,20 +537,19 @@ ContactUtils.getActiveCommRestrictionsSubselect = function()
                                                                     .and("orgContact.PERSON_ID IS NULL"))
                                     .where("anyContact.CONTACTID = CONTACT.CONTACTID")
 
-    mediumList.forEach(function(pMedium) 
+    var group = mask.getGroupConcat("DISTINCT "+mediumList, "', '");
+    var subselect = "''";
+    if(group)
     {
-        var subquery = newSelect("COMMRESTRICTION.MEDIUM, COMMRESTRICTIONID")
-                            .from("COMMRESTRICTION")
-                            .where("COMMRESTRICTION.MEDIUM", pMedium[0])
-                            .and("COMMRESTRICTION.STARTDATE", vars.get("$sys.date"), SqlBuilder.LESS_OR_EQUAL())
-                            .and(newWhere()
-                                    .or("COMMRESTRICTION.CONTACT_ID = CONTACT.CONTACTID")
-                                    .or("COMMRESTRICTION.CONTACT_ID", orgContactSubselect));
-        //!SqlBuilder
-        parts.push("case when exists(" + subquery.toString() + ") then '" + pMedium[1] + "' else '' end");
-    })
-
-    return sqlMasking.concatWithSeparator(parts, " ", false);
+        var res = newSelect(group).from("COMMRESTRICTION")
+                    .where("COMMRESTRICTION.CONTACT_ID = CONTACT.CONTACTID")
+
+        var subres = newSelect(group).from("COMMRESTRICTION")
+                    .where("COMMRESTRICTION.CONTACT_ID in ( "+orgContactSubselect.toString()+")")
+                    
+        subselect = mask.concatWithSeparator(["("+res.toString()+")", "("+subres.toString()+")"], ", ", false);
+    }
+    return subselect;
 }
 
 /**
diff --git a/process/Context_lib/process.js b/process/Context_lib/process.js
index a938c5d286655fc03057e7d612d9727b01f34294..aaecfe01d9b8faa1aaaff16fc18c71e58864c180 100644
--- a/process/Context_lib/process.js
+++ b/process/Context_lib/process.js
@@ -290,28 +290,36 @@ ContextUtils.loadContent = function(pEntity, pUid, pFields, pExcludeIDs, pProvid
  */
 ContextUtils.getContexts = function(pBlacklist, pInvertBlacklist)
 {
-    if (pInvertBlacklist == undefined)
-        pInvertBlacklist = false;
-
-
-    var contexts = project.getDataModels(project.DATAMODEL_KIND_CONTEXT);
-    var contextsStandardList = ["Organisation", "Person", "PrivatePerson", "Activity", "Salesproject", "Contract", "Offer", "Order", "Product", "Task",
-    "Campaign", "CampaignStep", "SupportTicket", "Leadimport"];
-
-    contexts = contexts.filter(function(pContext) {
-        if (contextsStandardList.indexOf(pContext[0]) > -1)
-            return true;
-        return false;
-    });
-
-    if (pBlacklist)
+    var whitelist;
+    if (pInvertBlacklist && pBlacklist)
+        whitelist = new Set(pBlacklist);
+    else
     {
-        contexts = contexts.filter(function(pContext) {
-            if (pBlacklist && pBlacklist.indexOf(pContext[0]) > -1 != pInvertBlacklist)
-                return false;
-            return true;
-        });
+        whitelist = new Set()
+            .add("Organisation")
+            .add("Person")
+            .add("PrivatePerson")
+            .add("Activity")
+            .add("Salesproject")
+            .add("Contract")
+            .add("Offer")
+            .add("Order")
+            .add("Product")
+            .add("Task")
+            .add("Campaign")
+            .add("CampaignStep")
+            .add("SupportTicket")
+            .add("Leadimport");
+            
+        if (pBlacklist)
+            pBlacklist.forEach(whitelist["delete"], whitelist);
     }
+    
+    var contexts = project.getDataModels(project.DATAMODEL_KIND_CONTEXT);
+    contexts = contexts.filter(function(context) 
+    {
+        return whitelist.has(context[0]);
+    });
 
     return contexts.map(ContextUtils._contextDataMapping).sort(function(pContext1, pContext2)
     {
diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js
index eecd2a9c83628dd909a5c182d53c53d38963a53b..8c981ecd4db360cd95a00663a447f1ebf3f8be5c 100644
--- a/process/Sql_lib/process.js
+++ b/process/Sql_lib/process.js
@@ -3292,6 +3292,45 @@ SqlMaskingUtils.prototype.getConcatSymbol = function()
     }
 }
 
+
+/**
+*  Returns the group_concat function, which groups <br>
+*  multiple Row Values into one Cell. Note: This function <br>
+*  does not work on Derby<br>
+*
+* @param {String} pField                 <p>
+*                                       Expression that shall be grouped.<br>
+* @param {String} pSeperator             <p>
+*                                       Character that shall be used as Seperator<br>
+* @return {String}                      <p>
+*                                       Returns the field with groupConcat wrapped around<br>
+*/
+SqlMaskingUtils.prototype.getGroupConcat = function(pField, pSeperator) 
+{
+    var group;
+    if(pField == null || pSeperator == null || pField == null && pSeperator == null)
+        throw new Error(translate.withArguments("Field or Seperator were empty function: %0", ["SqlMaskingUtils.prototype.getGroupConcat"]));
+    
+    switch(this.dbType) 
+    {
+        case db.DBTYPE_MARIADB10:
+        case db.DBTYPE_MYSQL4:
+            group = " GROUP_CONCAT("+pField+" SEPARATOR "+pSeperator+")";
+            break;
+        case db.DBTYPE_ORACLE10_CLUSTER:
+        case db.DBTYPE_ORACLE10_THIN:
+        case db.DBTYPE_ORACLE10_OCI:
+        case db.DBTYPE_POSTGRESQL8:
+        case db.DBTYPE_SQLSERVER2000:
+            group = " STRING_AGG("+pField+", "+pSeperator+")";
+            break;
+        case db.DBTYPE_DERBY10:
+            logging.log(translate.withArguments("${SQL_LIB_UNSUPPORTED_DBTYPE} function: %0", ["SqlMaskingUtils.prototype.getGroupConcat"]), logging.ERROR);
+            break;
+    }
+    return group;
+}
+
 /**
 *  Returns the trim function, which removes the<br>
 *  leading and trailing spaces in a string, depending<br>
diff --git a/process/Util_lib/process.js b/process/Util_lib/process.js
index 00ddb82a71bc8976508282f740e58b7fc996d5a3..154076f048f07ef9c800784fb3e056ea741ee46c 100644
--- a/process/Util_lib/process.js
+++ b/process/Util_lib/process.js
@@ -411,7 +411,7 @@ Utils.objectValues = function (pObject)
  */
 Utils.buildFilterObj = function(pFilterObj, pFieldName, pOperator, pContentType, pValue, pKey, pParentOperator)
 {
-    
+    //TODO: maybe something like this should be inside the JditoFilter_lib, but more fluent
     if(pParentOperator && pParentOperator != "AND" && pParentOperator != "OR")
         throw new Error(translate.text("Illegal Parent Operator in buildFilterObj-Function: "+pParentOperator));