diff --git a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html
index aadff087cfc38dcbd323e9067faa3f68484aba41..cf04bef1a22cb1d9be4f35206be3f1cc4a872aaa 100644
--- a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html
+++ b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.html
@@ -22,8 +22,6 @@
         </mat-label>
       </ars-row>
     </span>
-    <span *ngIf="keywords.length > 0"></span>
-
     <ars-row class="list-container">
       <div fxLayout="row" fxLayoutAlign="center center" fxFill>
         <mat-progress-spinner *ngIf="isLoading" mode="indeterminate"></mat-progress-spinner>
@@ -61,10 +59,7 @@
       </mat-list>
     </ars-row>
     <ars-row>
-      <span *ngIf="keywords.length <= 0 && !this.isLoading">
-        <p>{{ 'spacy-dialog.empty-nouns' | translate }}</p>
-      </span>
-      <span *ngIf="!langSupported">
+      <span *ngIf="!isLoading && (!langSupported || !hasKeywordsFromSpacy)">
         <p class="manual-input-title">{{ 'spacy-dialog.add-manually' | translate }}</p>
         <textarea class="manual-input" [(ngModel)]="manualKeywords" (input)="manualKeywordsToKeywords()"></textarea>
       </span>
diff --git a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts
index 45166229b358cbcc4c6a3c2c4765931d45931961..37f836ac17168aaee562b7bd97c3f8dfb40cbc83 100644
--- a/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts
+++ b/src/app/components/shared/_dialogs/spacy-dialog/spacy-dialog.component.ts
@@ -29,10 +29,11 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit {
   commentBodyChecked: string;
   keywords: Keyword[] = [];
   keywordsOriginal: Keyword[] = [];
+  hasKeywordsFromSpacy = false;
   isLoading = false;
   langSupported: boolean;
   manualKeywords = '';
-  _concurrentEdits = 0
+  _concurrentEdits = 0;
 
   constructor(
     protected langService: LanguagetoolService,
@@ -84,6 +85,7 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit {
       )
       .subscribe(words => {
         this.keywords = words;
+        this.hasKeywordsFromSpacy = this.keywords.length > 0;
         //deep copy
         this.keywordsOriginal = [...words];
         for (let i = 0; i < this.keywordsOriginal.length; i++) {
@@ -92,6 +94,7 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit {
       }, () => {
         this.keywords = [];
         this.keywordsOriginal = [];
+        this.hasKeywordsFromSpacy = false;
       }, () => {
         this.isLoading = false;
       });
@@ -150,6 +153,6 @@ export class SpacyDialogComponent implements OnInit, AfterContentInit {
 
   onEditChange(change: number) {
     this._concurrentEdits += change;
-    this.appDialogActionButtons.confirmButtonDisabled = (this._concurrentEdits > 0)
+    this.appDialogActionButtons.confirmButtonDisabled = (this._concurrentEdits > 0);
   }
 }
diff --git a/src/app/components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData.ts b/src/app/components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData.ts
index cbbc0d8ebbf31b4d022a62cf117a359b4aa1cf2c..1337a08d1e37606c118c08f6ee6244e999e15e97 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData.ts
+++ b/src/app/components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData.ts
@@ -10,6 +10,11 @@ export interface TopicCloudAdminData {
   profanityFilter: ProfanityFilter;
   blacklistIsActive: boolean;
   keywordORfulltext: KeywordOrFulltext;
+  minQuestions: number;
+  minQuestioners: number;
+  minUpvotes: number;
+  startDate: string;
+  endDate: string;
 }
 
 export enum KeywordOrFulltext {
diff --git a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.html b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.html
index 3f14fe3bc5a0a5cf50739694b81f137e2ae3e3c8..c282c94d642cebf163ce3dbe80f6999d87389856 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.html
+++ b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.html
@@ -28,9 +28,15 @@
           </mat-radio-group>
         </mat-card>
 
-        <mat-card style="background: none;">
-          <mat-slide-toggle [(ngModel)]="considerVotes">
-            {{'topic-cloud-dialog.consider-votes' | translate}}
+        <mat-slide-toggle [(ngModel)]="considerVotes">
+          {{'topic-cloud-dialog.consider-votes' | translate}}
+        </mat-slide-toggle>
+        <div *ngIf="isCreatorOrMod">
+          <mat-slide-toggle (change)="changeProfanityFilter()" [(ngModel)]="profanityFilter">
+            {{'topic-cloud-dialog.profanity' | translate}}
+          </mat-slide-toggle>
+          <mat-slide-toggle [(ngModel)]="blacklistIsActive">
+            {{'topic-cloud-dialog.hide-blacklist-words' | translate}}
           </mat-slide-toggle>
         </mat-card>
         <div *ngIf="isCreatorOrMod">
@@ -249,6 +255,48 @@
               </mat-tab>
             </mat-tab-group>
           </mat-expansion-panel>
+          <mat-expansion-panel class="color-background">
+            <mat-expansion-panel-header class="color-background">
+              <mat-panel-title>
+                {{'topic-cloud-dialog.topic-requirement-title' | translate}}
+              </mat-panel-title>
+            </mat-expansion-panel-header>
+            <mat-form-field class="themeRequirementInput" appearance="fill">
+              <mat-label>{{'topic-cloud-dialog.topic-requirement-questions' | translate}}</mat-label>
+              <input matInput type="number" min="1" [(ngModel)]="minQuestions">
+            </mat-form-field>
+            <mat-form-field class="themeRequirementInput" appearance="fill">
+              <mat-label>{{'topic-cloud-dialog.topic-requirement-questioners' | translate}}</mat-label>
+              <input matInput type="number" min="1" [(ngModel)]="minQuestioners">
+            </mat-form-field>
+            <mat-form-field class="themeRequirementInput" appearance="fill">
+              <mat-label>{{'topic-cloud-dialog.topic-requirement-upvotes' | translate}}</mat-label>
+              <input matInput type="number" min="0" [(ngModel)]="minUpvotes">
+            </mat-form-field>
+
+            <table class="themeRequirementInput">
+              <tr>
+                <td>
+                  <mat-form-field class="themeRequirementInput" appearance="fill">
+                    <mat-label>{{'topic-cloud-dialog.topic-requirement-begin-datetime' | translate}}</mat-label>
+                    <input matInput type="datetime-local" [(ngModel)]="startDate">
+                    <button *ngIf="startDate" matSuffix mat-icon-button aria-label="Clear" (click)="startDate=''">
+                      <mat-icon>close</mat-icon>
+                    </button>
+                  </mat-form-field>
+                </td>
+                <td>
+                  <mat-form-field class="themeRequirementInput" appearance="fill">
+                    <mat-label>{{'topic-cloud-dialog.topic-requirement-end-datetime' | translate}}</mat-label>
+                    <input matInput type="datetime-local" [(ngModel)]="endDate">
+                    <button *ngIf="endDate" matSuffix mat-icon-button aria-label="Clear" (click)="endDate=''">
+                      <mat-icon>close</mat-icon>
+                    </button>
+                  </mat-form-field>
+                </td>
+              </tr>
+            </table>
+          </mat-expansion-panel>
         </mat-accordion>
       </mat-expansion-panel>
     </mat-accordion>
@@ -301,8 +349,7 @@
       </button>
     </mat-menu>
 
-    <mat-card class="color-surface"
-      *ngIf="(keywords.length === 0 || (searchMode && filteredKeywords.length === 0))&&!isLoading">
+    <mat-card class="color-surface" *ngIf="(keywords.length === 0 || (searchMode && filteredKeywords.length === 0))&&!isLoading">
       <p class="color-on-surface" fxLayoutAlign="center">
         {{'topic-cloud-dialog.no-keywords-note' | translate}}
       </p>
diff --git a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.scss b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.scss
index 5c4d5977a5aa579947c8d4593d64e54a9115c942..f672fae74a1e3ad92cd954a468e61a3edd90a22f 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.scss
+++ b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.scss
@@ -130,6 +130,11 @@ mat-panel-title, mat-panel-description {
   margin-left: 8px;
 }
 
+.themeRequirementInput {
+  width: 100%;
+  margin-left: 0 !important;
+}
+
 .vertical-center {
   margin-top: auto;
   margin-bottom: auto;
diff --git a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.ts b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.ts
index 2cb85515a616ff350d26c6c81b2a328fdba1539e..9d1bed55d7c8b9f4e0c0dd033baddfdf73d4db60 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.ts
+++ b/src/app/components/shared/_dialogs/topic-cloud-administration/topic-cloud-administration.component.ts
@@ -53,7 +53,13 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
     en: string[];
   };
   spacyLabelsAllSelectedDE = true;
-  isLoading = true;
+  isLoading: boolean;
+  minQuestions: string;
+  minQuestioners: string;
+  minUpvotes: string;
+  startDate: string;
+  endDate: string;
+
   keywords: Keyword[] = [];
   private topicCloudAdminData: TopicCloudAdminData;
   private profanityFilter: boolean;
@@ -213,19 +219,36 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
         profFilter = this.censorLanguageSpecificCheck ? ProfanityFilter.languageSpecific : ProfanityFilter.none;
         profFilter = this.censorPartialWordsCheck ? ProfanityFilter.partialWords : profFilter;
       }
+      let minQuestionersVerified = +this.minQuestioners;
+      if (Number.isNaN(minQuestionersVerified) || minQuestionersVerified < 1) {
+        minQuestionersVerified = 1;
+      }
+      let minQuestionsVerified = +this.minQuestions;
+      if (Number.isNaN(minQuestionsVerified) || minQuestionsVerified < 1) {
+        minQuestionsVerified = 1;
+      }
+      let minUpvotesVerified = +this.minUpvotes;
+      if (Number.isNaN(minUpvotesVerified) || minUpvotesVerified < 0) {
+        minUpvotesVerified = 0;
+      }
+      this.topicCloudAdminData = {
+        blacklist: [],
+        wantedLabels: {
+          de: this.wantedLabels.de,
+          en: this.wantedLabels.en
+        },
+        considerVotes: this.considerVotes,
+        profanityFilter: profFilter,
+        blacklistIsActive: this.blacklistIsActive,
+        keywordORfulltext: KeywordOrFulltext[this.keywordORfulltext],
+        minQuestioners: minQuestionersVerified,
+        minQuestions: minQuestionsVerified,
+        minUpvotes: minUpvotesVerified,
+        startDate: this.startDate.length ? this.startDate : null,
+        endDate: this.endDate.length ? this.endDate : null
+      };
+      this.topicCloudAdminService.setAdminData(this.topicCloudAdminData);
     }
-    this.topicCloudAdminData = {
-      blacklist: [],
-      wantedLabels: {
-        de: this.wantedLabels.de,
-        en: this.wantedLabels.en
-      },
-      considerVotes: this.considerVotes,
-      profanityFilter: profFilter,
-      blacklistIsActive: this.blacklistIsActive,
-      keywordORfulltext: KeywordOrFulltext[this.keywordORfulltext]
-    };
-    this.topicCloudAdminService.setAdminData(this.topicCloudAdminData);
   }
 
   setDefaultAdminData() {
@@ -245,6 +268,11 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy {
         de: this.topicCloudAdminData.wantedLabels.de,
         en: this.topicCloudAdminData.wantedLabels.en
       };
+      this.minQuestioners = String(this.topicCloudAdminData.minQuestioners);
+      this.minQuestions = String(this.topicCloudAdminData.minQuestions);
+      this.minUpvotes = String(this.topicCloudAdminData.minUpvotes);
+      this.startDate = this.topicCloudAdminData.startDate || '';
+      this.endDate = this.topicCloudAdminData.endDate || '';
     }
   }
 
diff --git a/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.ts b/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.ts
index 2cce403e9e7d0e084aaff4bfb6af3ed7de2ab23f..f9c8931bc5e2d63157e9786125d5d87b1d270346 100644
--- a/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.ts
+++ b/src/app/components/shared/_dialogs/topic-cloud-filter/topic-cloud-filter.component.ts
@@ -11,7 +11,8 @@ import { RoomService } from '../../../../services/http/room.service';
 import { Comment } from '../../../../models/comment';
 import { CommentListData } from '../../comment-list/comment-list.component';
 import { TopicCloudAdminService } from '../../../../services/util/topic-cloud-admin.service';
-import { KeywordOrFulltext } from '../topic-cloud-administration/TopicCloudAdminData';
+import { TopicCloudAdminData } from '../topic-cloud-administration/TopicCloudAdminData';
+import { TagCloudDataService } from '../../../../services/util/tag-cloud-data.service';
 
 class CommentsCount {
   comments: number;
@@ -33,8 +34,7 @@ export class TopicCloudFilterComponent implements OnInit {
   allComments: CommentsCount;
   filteredComments: CommentsCount;
   disableCurrentFiltersOptions = false;
-  private readonly _filter: KeywordOrFulltext;
-  private readonly _blacklist: string[];
+  private readonly _adminData: TopicCloudAdminData;
 
   constructor(public dialogRef: MatDialogRef<RoomCreatorPageComponent>,
               public dialog: MatDialog,
@@ -46,9 +46,7 @@ export class TopicCloudFilterComponent implements OnInit {
               @Inject(MAT_DIALOG_DATA) public data: any,
               public eventService: EventService) {
     langService.langEmitter.subscribe(lang => translationService.use(lang));
-    const adminData = TopicCloudAdminService.getDefaultAdminData;
-    this._filter = adminData.keywordORfulltext;
-    this._blacklist = adminData.blacklist;
+    this._adminData = TopicCloudAdminService.getDefaultAdminData;
   }
 
   ngOnInit() {
@@ -75,40 +73,11 @@ export class TopicCloudFilterComponent implements OnInit {
   }
 
   getCommentCounts(comments: Comment[]): CommentsCount {
+    const [data, users] = TagCloudDataService.buildDataFromComments(this._adminData, comments);
     const counts = new CommentsCount();
-    const userSet = new Set<number>();
-    const keywordSet = new Set<string>();
-
-    comments.forEach(c => {
-      if (c.userNumber) {
-        userSet.add(c.userNumber);
-      }
-      let source = c.keywordsFromQuestioner;
-      if (this._filter === KeywordOrFulltext.both) {
-        source = !source || !source.length ? c.keywordsFromSpacy : source;
-      } else if (this._filter === KeywordOrFulltext.fulltext) {
-        source = c.keywordsFromSpacy;
-      }
-      if (source) {
-        source.forEach(k => {
-          let isProfanity = false;
-          const lowerCasedKeyword = k.toLowerCase();
-          for (const word of this._blacklist) {
-            if (lowerCasedKeyword.includes(word)) {
-              isProfanity = true;
-              break;
-            }
-          }
-          if (!isProfanity) {
-            keywordSet.add(k);
-          }
-        });
-      }
-    });
-
     counts.comments = comments.length;
-    counts.users = userSet.size;
-    counts.keywords = keywordSet.size;
+    counts.users = users.size;
+    counts.keywords = data.size;
     return counts;
   }
 
diff --git a/src/app/components/shared/comment-list/comment-list.component.ts b/src/app/components/shared/comment-list/comment-list.component.ts
index 967ff70fc74be02627932e551c1e63d8c00816db..7c595d4335d5f03358c304ba95f5ffbbf33009ca 100644
--- a/src/app/components/shared/comment-list/comment-list.component.ts
+++ b/src/app/components/shared/comment-list/comment-list.component.ts
@@ -372,6 +372,7 @@ export class CommentListComponent implements OnInit, OnDestroy {
       this.currentFilter = '';
       this.selectedTag = '';
       this.selectedKeyword = '';
+      this.userNumberSelection = 0;
       this.sortComments(this.currentSort);
       return;
     }
diff --git a/src/app/components/shared/tag-cloud/tag-cloud.component.html b/src/app/components/shared/tag-cloud/tag-cloud.component.html
index a706a462c875130cbac5257b2634e2bc87f9a7ba..88c7ae2ddc2536caff6068df814b0198f1521b6b 100644
--- a/src/app/components/shared/tag-cloud/tag-cloud.component.html
+++ b/src/app/components/shared/tag-cloud/tag-cloud.component.html
@@ -29,3 +29,12 @@
     </mat-drawer-content>
   </mat-drawer-container>
 </ars-screen>
+<button *ngIf="room && !room.closed"
+        mat-fab
+        mat-icon-button
+        aria-labelledby="add"
+        class="fab_add_comment"
+        (click)="createCommentWrapper.openCreateDialog(this.user)"
+        matTooltip="{{ 'comment-list.add-comment' | translate }}">
+  <mat-icon>add</mat-icon>
+</button>
diff --git a/src/app/components/shared/tag-cloud/tag-cloud.component.scss b/src/app/components/shared/tag-cloud/tag-cloud.component.scss
index 43a9e2415f6a7e747c6178f3d7d6afa0c16842cc..07159c41de2f1743edea3150c633dc5075804335 100644
--- a/src/app/components/shared/tag-cloud/tag-cloud.component.scss
+++ b/src/app/components/shared/tag-cloud/tag-cloud.component.scss
@@ -49,3 +49,14 @@ app-tag-cloud-pop-up {
 ::ng-deep .spacyTagCloud span:hover {
   cursor: pointer;
 }
+
+.fab_add_comment {
+  position: fixed;
+  right: 30px;
+  bottom: 30px;
+  background-color: var(--primary);
+  color: var(--on-primary);
+  transform: scale(1.4);
+  transition: all 0.1s ease-in-out;
+  z-index: 2;
+}
diff --git a/src/app/components/shared/tag-cloud/tag-cloud.component.ts b/src/app/components/shared/tag-cloud/tag-cloud.component.ts
index 37c401a931362c0a318d7b32b23fb187b34e1a0e..6dbb925b9cbf06410c2a17b8c0d67f021162f20e 100644
--- a/src/app/components/shared/tag-cloud/tag-cloud.component.ts
+++ b/src/app/components/shared/tag-cloud/tag-cloud.component.ts
@@ -61,6 +61,7 @@ class TagComment implements CloudData {
 
 const colorRegex = /rgba?\((\d+), (\d+), (\d+)(?:, (\d(?:\.\d+)?))?\)/;
 const transformationScaleKiller = /scale\([^)]*\)/;
+const transformationRotationKiller = /rotate\(([^)]*)\)/;
 type DefaultColors = [
   hover: string,
   w1: string,
@@ -176,8 +177,8 @@ export class TagCloudComponent implements OnInit, OnDestroy, AfterContentInit, A
   isLoading = true;
   headerInterface = null;
   themeSubscription = null;
+  createCommentWrapper: CreateCommentWrapper = null;
   private _currentSettings: CloudParameters;
-  private _createCommentWrapper: CreateCommentWrapper = null;
   private _subscriptionCommentlist = null;
   private _calcCanvas: HTMLCanvasElement = null;
   private _calcRenderContext: CanvasRenderingContext2D = null;
@@ -224,7 +225,7 @@ export class TagCloudComponent implements OnInit, OnDestroy, AfterContentInit, A
     this.updateGlobalStyles();
     this.headerInterface = this.eventService.on<string>('navigate').subscribe(e => {
       if (e === 'createQuestion') {
-        this._createCommentWrapper.openCreateDialog(this.user);
+        this.createCommentWrapper.openCreateDialog(this.user);
       } else if (e === 'topicCloudConfig') {
         this.configurationOpen = !this.configurationOpen;
       } else if (e === 'topicCloudAdministration') {
@@ -263,7 +264,7 @@ export class TagCloudComponent implements OnInit, OnDestroy, AfterContentInit, A
           this.room = room;
           this.roomId = room.id;
           this.directSend = this.room.directSend;
-          this._createCommentWrapper = new CreateCommentWrapper(this.translateService,
+          this.createCommentWrapper = new CreateCommentWrapper(this.translateService,
             this.notificationService, this.commentService, this.dialog, this.room);
           if (!this.authenticationService.hasAccess(this.shortId, UserRole.PARTICIPANT)) {
             this.roomService.addToHistory(this.room.id);
@@ -439,10 +440,14 @@ export class TagCloudComponent implements OnInit, OnDestroy, AfterContentInit, A
     this.child.cloudDataHtmlElements.forEach((elem, i) => {
       const dataElement = this.data[i];
       elem.addEventListener('mouseleave', () => {
-        elem.style.transform = elem.style.transform.replace(transformationScaleKiller, '').trim();
+        elem.style.transform = elem.style.transform.replace(transformationScaleKiller, '').trim() +
+          ' rotate(' + (elem.dataset['tempRotation'] || '0deg') + ')';
         this.popup.leave();
       });
       elem.addEventListener('mouseenter', () => {
+        const transformMatch = elem.style.transform.match(transformationRotationKiller);
+        elem.dataset['tempRotation'] = transformMatch ? transformMatch[1] : '0deg';
+        elem.style.transform = elem.style.transform.replace(transformationRotationKiller, '').trim();
         this.popup.enter(elem, dataElement.text, dataElement.tagData,
           (this._currentSettings.hoverTime + this._currentSettings.hoverDelay) * 1_000);
       });
diff --git a/src/app/services/util/tag-cloud-data.service.ts b/src/app/services/util/tag-cloud-data.service.ts
index 8509c510737cb4a2c2f5a462346bdc371a2b07e7..c3c3418f4fd06ecd44e7a9b2fa7e2feeb0ab67cc 100644
--- a/src/app/services/util/tag-cloud-data.service.ts
+++ b/src/app/services/util/tag-cloud-data.service.ts
@@ -1,6 +1,6 @@
 import { Injectable } from '@angular/core';
 import { TopicCloudAdminData } from '../../components/shared/_dialogs/topic-cloud-administration/TopicCloudAdminData';
-import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
 import { TopicCloudAdminService } from './topic-cloud-admin.service';
 import { CommentFilter } from '../../utils/filter-options';
 import { TranslateService } from '@ngx-translate/core';
@@ -16,6 +16,7 @@ export interface TagCloudDataTagEntry {
   cachedDownVotes: number;
   distinctUsers: Set<number>;
   firstTimeStamp: Date;
+  lastTimeStamp: Date;
   categories: Set<string>;
   comments: Comment[];
 }
@@ -47,12 +48,6 @@ export type TagCloudMetaDataCount = [
   number  // w10
 ];
 
-export enum TagCloudDataSupplyType {
-  keywords,
-  fullText,
-  keywordsAndFullText
-}
-
 export enum TagCloudCalcWeightType {
   byLength,
   byVotes,
@@ -70,7 +65,6 @@ export class TagCloudDataService {
   private _cachedData: TagCloudData;
   private _commentSubscription = null;
   private _roomId = null;
-  private _supplyType = TagCloudDataSupplyType.keywordsAndFullText;
   private _calcWeightType = TagCloudCalcWeightType.byLength;
   private _lastFetchedData: TagCloudData = null;
   private _lastFetchedComments: Comment[] = null;
@@ -102,10 +96,57 @@ export class TagCloudDataService {
     });
   }
 
+  static buildDataFromComments(adminData: TopicCloudAdminData, comments: Comment[]): [TagCloudData, Set<number>] {
+    const data: TagCloudData = new Map<string, TagCloudDataTagEntry>();
+    const users = new Set<number>();
+    for (const comment of comments) {
+      TopicCloudAdminService.approveKeywordsOfComment(comment, adminData, (keyword) => {
+        let current: TagCloudDataTagEntry = data.get(keyword);
+        const commentDate = new Date(comment.timestamp);
+        if (current === undefined) {
+          current = {
+            cachedVoteCount: 0,
+            cachedUpVotes: 0,
+            cachedDownVotes: 0,
+            comments: [],
+            weight: 0,
+            adjustedWeight: 0,
+            distinctUsers: new Set<number>(),
+            categories: new Set<string>(),
+            firstTimeStamp: commentDate,
+            lastTimeStamp: commentDate
+          };
+          data.set(keyword, current);
+        }
+        current.cachedVoteCount += comment.score;
+        current.cachedUpVotes += comment.upvotes;
+        current.cachedDownVotes += comment.downvotes;
+        current.distinctUsers.add(comment.userNumber);
+        if (comment.tag) {
+          current.categories.add(comment.tag);
+        }
+        // @ts-ignore
+        if (current.firstTimeStamp - commentDate > 0) {
+          current.firstTimeStamp = commentDate;
+        }
+        // @ts-ignore
+        if (current.lastTimeStamp - commentDate < 0) {
+          current.lastTimeStamp = commentDate;
+        }
+        current.comments.push(comment);
+      });
+      users.add(comment.userNumber);
+    }
+    return [
+      new Map<string, TagCloudDataTagEntry>([...data].filter(v => TopicCloudAdminService.isTopicAllowed(adminData,
+        v[1].comments.length, v[1].distinctUsers.size, v[1].cachedUpVotes, v[1].firstTimeStamp, v[1].lastTimeStamp))),
+      users
+    ];
+  }
+
   bindToRoom(roomId: string): void {
     this._currentFilter = CommentFilter.currentFilter;
     this._roomId = roomId;
-    this.onReceiveAdminData(TopicCloudAdminService.getDefaultAdminData);
     this._subscriptionAdminData = this._tagCloudAdmin.getAdminData.subscribe(adminData => {
       this.onReceiveAdminData(adminData, true);
     });
@@ -149,7 +190,8 @@ export class TagCloudDataService {
           adjustedWeight: i - 1,
           categories: new Set<string>(),
           distinctUsers: new Set<number>(),
-          firstTimeStamp: new Date()
+          firstTimeStamp: new Date(),
+          lastTimeStamp: new Date()
         });
       }
     });
@@ -163,17 +205,6 @@ export class TagCloudDataService {
     return this._cachedData;
   }
 
-  get dataSupplyType(): TagCloudDataSupplyType {
-    return this._supplyType;
-  }
-
-  set dataSupplyType(type: TagCloudDataSupplyType) {
-    if (this._supplyType !== type) {
-      this._supplyType = type;
-      this.rebuildTagData();
-    }
-  }
-
   set weightCalcType(type: TagCloudCalcWeightType) {
     if (type !== this._calcWeightType) {
       this._calcWeightType = type;
@@ -256,7 +287,6 @@ export class TagCloudDataService {
   private onReceiveAdminData(data: TopicCloudAdminData, update = false) {
     this._adminData = data;
     this._calcWeightType = this._adminData.considerVotes ? TagCloudCalcWeightType.byLengthAndVotes : TagCloudCalcWeightType.byLength;
-    this._supplyType = this._adminData.keywordORfulltext as unknown as TagCloudDataSupplyType;
     if (update) {
       this.rebuildTagData();
     }
@@ -292,64 +322,9 @@ export class TagCloudDataService {
       return;
     }
     const currentMeta = this._isDemoActive ? this._lastMetaData : this._currentMetaData;
-    const data: TagCloudData = new Map<string, TagCloudDataTagEntry>();
-    const users = new Set<number>();
     const filteredComments = this._lastFetchedComments.filter(comment => this._currentFilter.checkComment(comment));
     currentMeta.commentCount = filteredComments.length;
-    for (const comment of filteredComments) {
-      let keywords = comment.keywordsFromQuestioner;
-      if (this._supplyType === TagCloudDataSupplyType.keywordsAndFullText) {
-        if (!keywords || !keywords.length) {
-          keywords = comment.keywordsFromSpacy;
-        }
-      } else if (this._supplyType === TagCloudDataSupplyType.fullText) {
-        keywords = comment.keywordsFromSpacy;
-      }
-      if (!keywords) {
-        keywords = [];
-      }
-      for (const keyword of keywords) {
-        const lowerCaseKeyWord = keyword.toLowerCase();
-        let profanity = false;
-        for (const word of this._adminData.blacklist) {
-          if (lowerCaseKeyWord.includes(word)) {
-            profanity = true;
-            break;
-          }
-        }
-        if (profanity) {
-          continue;
-        }
-        let current = data.get(keyword);
-        if (current === undefined) {
-          current = {
-            cachedVoteCount: 0,
-            cachedUpVotes: 0,
-            cachedDownVotes: 0,
-            comments: [],
-            weight: 0,
-            adjustedWeight: 0,
-            distinctUsers: new Set<number>(),
-            categories: new Set<string>(),
-            firstTimeStamp: comment.timestamp
-          };
-          data.set(keyword, current);
-        }
-        current.cachedVoteCount += comment.score;
-        current.cachedUpVotes += comment.upvotes;
-        current.cachedDownVotes += comment.downvotes;
-        current.distinctUsers.add(comment.userNumber);
-        if (comment.tag) {
-          current.categories.add(comment.tag);
-        }
-        // @ts-ignore
-        if (current.firstTimeStamp - comment.timestamp > 0) {
-          current.firstTimeStamp = comment.timestamp;
-        }
-        current.comments.push(comment);
-      }
-      users.add(comment.userNumber);
-    }
+    const [data, users] = TagCloudDataService.buildDataFromComments(this._adminData, filteredComments);
     let minWeight = null;
     let maxWeight = null;
     for (const value of data.values()) {
diff --git a/src/app/services/util/topic-cloud-admin.service.ts b/src/app/services/util/topic-cloud-admin.service.ts
index 8b787e474077972eb676bde33272e5b8d33943a1..839a24b50aa5e848ae90557d6bb3455abe60c6ab 100644
--- a/src/app/services/util/topic-cloud-admin.service.ts
+++ b/src/app/services/util/topic-cloud-admin.service.ts
@@ -8,16 +8,17 @@ import { RoomService } from '../http/room.service';
 import { ProfanityFilter, Room } from '../../models/room';
 import { TranslateService } from '@ngx-translate/core';
 import { NotificationService } from './notification.service';
-import { Observable, Subject } from 'rxjs';
 import { WsRoomService } from '..//websockets/ws-room.service';
 import { ProfanityFilterService } from './profanity-filter.service';
+import { BehaviorSubject, Observable, Subject } from 'rxjs';
+import { Comment } from '../../models/comment';
 
 @Injectable({
   providedIn: 'root',
 })
 export class TopicCloudAdminService {
   private static readonly adminKey = 'Topic-Cloud-Admin-Data';
-  private adminData: Subject<TopicCloudAdminData>;
+  private adminData: BehaviorSubject<TopicCloudAdminData>;
   private blacklist: Subject<string[]>;
 
   constructor(private roomService: RoomService,
@@ -26,8 +27,6 @@ export class TopicCloudAdminService {
               private profanityFilterService: ProfanityFilterService,
               private notificationService: NotificationService) {
     this.blacklist = new Subject<string[]>();
-    this.adminData = new Subject<TopicCloudAdminData>();
-
     this.wsRoomService.getRoomStream(localStorage.getItem('roomId')).subscribe(msg => {
       const message = JSON.parse(msg.body);
       const room = message.payload.changes;
@@ -35,10 +34,45 @@ export class TopicCloudAdminService {
         this.blacklist.next(room.blacklist ? JSON.parse(room.blacklist) : []);
       }
     });
+    this.adminData = new BehaviorSubject<TopicCloudAdminData>(TopicCloudAdminService.getDefaultAdminData);
+  }
+
+  static approveKeywordsOfComment(comment: Comment, config: TopicCloudAdminData, keywordFunc: (string) => void) {
+    let source = comment.keywordsFromQuestioner;
+    if (config.keywordORfulltext === KeywordOrFulltext.both) {
+      source = !source || !source.length ? comment.keywordsFromSpacy : source;
+    } else if (config.keywordORfulltext === KeywordOrFulltext.fulltext) {
+      source = comment.keywordsFromSpacy;
+    }
+    if (!source) {
+      return;
+    }
+    for (const keyword of source) {
+      let isProfanity = false;
+      const lowerCasedKeyword = keyword.toLowerCase();
+      for (const word of config.blacklist) {
+        if (lowerCasedKeyword.includes(word)) {
+          isProfanity = true;
+          break;
+        }
+      }
+      if (!isProfanity) {
+        keywordFunc(keyword);
+      }
+    }
+  }
+
+  static isTopicAllowed(config: TopicCloudAdminData, comments: number, users: number,
+                        upvotes: number, firstTimeStamp: Date, lastTimeStamp: Date) {
+    return !((config.minQuestions > comments) ||
+      (config.minQuestioners > users) ||
+      (config.minUpvotes > upvotes) ||
+      (config.startDate && new Date(config.startDate) > firstTimeStamp) ||
+      (config.endDate && new Date(config.endDate) < lastTimeStamp));
   }
 
   static get getDefaultAdminData(): TopicCloudAdminData {
-    let data = JSON.parse(localStorage.getItem(this.adminKey));
+    let data: TopicCloudAdminData = JSON.parse(localStorage.getItem(this.adminKey));
     if (!data) {
       data = {
         blacklist: [],
@@ -49,7 +83,12 @@ export class TopicCloudAdminService {
         considerVotes: true,
         profanityFilter: ProfanityFilter.none,
         blacklistIsActive: true,
-        keywordORfulltext: KeywordOrFulltext.both
+        keywordORfulltext: KeywordOrFulltext.both,
+        minQuestioners: 1,
+        minQuestions: 1,
+        minUpvotes: 0,
+        startDate: null,
+        endDate: null
       };
     }
     return data;
diff --git a/src/app/utils/filter-options.ts b/src/app/utils/filter-options.ts
index 9268cbcf7cdb9cfab1ba03bc72c50e772f977386..d2791cb73b89eedd7b2f5e338f3870b100913957 100644
--- a/src/app/utils/filter-options.ts
+++ b/src/app/utils/filter-options.ts
@@ -32,7 +32,7 @@ export class CommentFilter {
   timeStampUntil = 0;
 
   periodSet: Period = Period.twoWeeks;
-  timeStampNow = 0; 
+  timeStampNow = 0;
 
   constructor(obj?: any) {
     if (obj) {
diff --git a/src/assets/i18n/creator/de.json b/src/assets/i18n/creator/de.json
index d139a60913cd19406ba00de93321936e2730b990..5c7697fc7c458cd8247fc99650b10b9ed03578b0 100644
--- a/src/assets/i18n/creator/de.json
+++ b/src/assets/i18n/creator/de.json
@@ -87,7 +87,6 @@
     "de": "Deutsch",
     "en": "Englisch",
     "fr": "Französisch",
-    "empty-nouns": "Keine Nomen enthalten",
     "select-all": "Alles auswählen",
     "lang-button-hint": "Ausgewählte Sprache für die Rechtschreibprüfung",
     "select-all-hint": "Alle Stichwörter auswählen",
@@ -95,7 +94,8 @@
     "edit-keyword-hint": "Stichwort editieren",
     "editing-done-hint": "Editierung abschliessen",
     "force-language-selection": "Automatische Spracherkennung unpräzise, bitte gewählte Sprache prüfen!",
-    "add-manually": "Geben Sie bitte die Stichwörter unten mit separatem Komma ein"
+    "add-manually": "Geben Sie bitte die Stichwörter unten mit separatem Komma ein",
+    "select-keywords": "Wählen Sie die Stichwörter für Ihre Frage aus"
   },
   "comment-page": {
     "a11y-comment_delete": "Löscht diese Frage",
@@ -417,7 +417,13 @@
   },
   "dialog-comment": {
     "read-more": "Mehr lesen",
-    "read-less": "Weniger lesen"
+    "read-less": "Weniger lesen",
+    "topic-requirement-title": "Themen Anforderung",
+    "topic-requirement-questions": "Minimale Anzahl an Fragen",
+    "topic-requirement-questioners": "Minimale Anzahl an Fragensteller*innen",
+    "topic-requirement-upvotes": "Minimale Anzahl an Upvotes",
+    "topic-requirement-begin-datetime": "Frühester Kommentar",
+    "topic-requirement-end-datetime": "Letzter Kommentar"
   },
   "tag-cloud": {
     "demo-data-topic": "Thema %d",
diff --git a/src/assets/i18n/creator/en.json b/src/assets/i18n/creator/en.json
index e6c7cc0aeef3ec4b54db5c988073489008ce7394..463c5fa9632a241d6a8a1066f5c16a50c68a70e6 100644
--- a/src/assets/i18n/creator/en.json
+++ b/src/assets/i18n/creator/en.json
@@ -88,7 +88,6 @@
     "de": "German",
     "en": "English",
     "fr": "French",
-    "empty-nouns": "No nouns included",
     "select-all": "Select all",
     "lang-button-hint": "Selected language for spell check",
     "select-all-hint": "Select all keywords",
@@ -96,7 +95,8 @@
     "edit-keyword-hint": "Edit keyword",
     "editing-done-hint": "Finish editing",
     "force-language-selection": "Language detection inaccurate, please check language settings!",
-    "add-manually": "You can manually enter the keywords separated with a comma "
+    "add-manually": "You can manually enter the keywords separated with a comma",
+    "select-keywords": "Choose the keywords for your question"
   },
   "comment-page": {
     "a11y-comment_delete": "Deletes this question",
@@ -425,7 +425,13 @@
   },
   "dialog-comment": {
     "read-more": "read more",
-    "read-less": "read less"
+    "read-less": "read less",
+    "topic-requirement-title": "Topic requirement",
+    "topic-requirement-questions": "Minimum number of questions",
+    "topic-requirement-questioners": "Minimum number of questioners",
+    "topic-requirement-upvotes": "Minimum number of upvotes",
+    "topic-requirement-begin-datetime": "Earliest comment",
+    "topic-requirement-end-datetime": "Last comment"
   },
   "tag-cloud-config":{
     "general":"General",
@@ -477,8 +483,8 @@
     "highestWeight-tooltip": "show x tags with the highest weight",
     "rotate-weight": "Rotate some entries of this weight class randomly by x degrees",
     "font":"Font",
-    "reset-btn": "Reset",  
-    "font-family-tooltip": "Select font",  
+    "reset-btn": "Reset",
+    "font-family-tooltip": "Select font",
     "bold-notation-tooltip": "Select font-thickness bold",
     "font-style-bold" : "Bold",
     "font-style-italic": "Italic",
diff --git a/src/assets/i18n/participant/de.json b/src/assets/i18n/participant/de.json
index 8db95744642ad14565f4bb52afdff6b7621bc5fd..fb3f73fcb18a89ae63fe49d4617b1b9f874456f6 100644
--- a/src/assets/i18n/participant/de.json
+++ b/src/assets/i18n/participant/de.json
@@ -92,7 +92,6 @@
     "de": "Deutsch",
     "en": "Englisch",
     "fr": "Französisch",
-    "empty-nouns": "Keine Nomen enthalten",
     "select-all": "Alles auswählen",
     "lang-button-hint": "Ausgewählte Sprache für die Rechtschreibprüfung",
     "select-all-hint": "Alle Stichwörter auswählen",
@@ -300,7 +299,13 @@
     "word-is-not-profanity": "Wort ist nicht profan",
     "words-in-profanity": "Profane Wörter",
     "language": "Sprache",
-    "preview": "Vorschau"
+    "preview": "Vorschau",
+    "topic-requirement-title": "Themen Anforderung",
+    "topic-requirement-questions": "Minimale Anzahl an Fragen",
+    "topic-requirement-questioners": "Minimale Anzahl an Fragensteller*innen",
+    "topic-requirement-upvotes": "Minimale Anzahl an Upvotes",
+    "topic-requirement-begin-datetime": "Frühester Kommentar",
+    "topic-requirement-end-datetime": "Letzter Kommentar"
   },
   "topic-cloud-confirm-dialog": {
     "cancel": "Abbrechen",
diff --git a/src/assets/i18n/participant/en.json b/src/assets/i18n/participant/en.json
index 79aca63883ac07fc63317c78853254e61edf9da0..17c661d8959354ebb34c7379c67c8d62d56d6cb9 100644
--- a/src/assets/i18n/participant/en.json
+++ b/src/assets/i18n/participant/en.json
@@ -102,7 +102,6 @@
     "de": "German",
     "en": "English",
     "fr": "French",
-    "empty-nouns": "No nouns included",
     "select-all": "Select all",
     "lang-button-hint": "Selected language for spell check",
     "select-all-hint": "Select all keywords",
@@ -306,7 +305,13 @@
     "word-is-not-profanity": "Word is not profane",
     "words-in-profanity": "Words in profanity filter",
     "language": "Language",
-    "preview": "Preview"
+    "preview": "Preview",
+    "topic-requirement-title": "Topic requirement",
+    "topic-requirement-questions": "Minimum number of questions",
+    "topic-requirement-questioners": "Minimum number of questioners",
+    "topic-requirement-upvotes": "Minimum number of upvotes",
+    "topic-requirement-begin-datetime": "Earliest comment",
+    "topic-requirement-end-datetime": "Last comment"
   },
   "topic-cloud-confirm-dialog":{
     "cancel": "Cancel",
@@ -372,7 +377,7 @@
     "rotate-weight": "Rotate some entries of this weight class randomly by x degrees",
     "font":"Font",
     "reset-btn": "Reset",
-    "font-family-tooltip": "Select font",  
+    "font-family-tooltip": "Select font",
     "bold-notation-tooltip": "Select font-thickness bold",
     "font-style-bold" : "Bold",
     "font-style-italic": "Italic",