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 4744be85b9d6509aa19f3e435ddb72ceec9ec197..2822ca2f975633815ee0233780624da3b19de92a 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 @@ -359,7 +359,8 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy { } getKeywordWithoutProfanity(keyword: string, lang: string): string { - return this.profanityFilterService.filterProfanityWords(keyword, this.censorPartialWordsCheck, this.censorLanguageSpecificCheck, lang); + return this.profanityFilterService.filterProfanityWords(keyword, this.censorPartialWordsCheck, + this.censorLanguageSpecificCheck, lang)[0]; } sortQuestions(sortMode?: string) { @@ -605,7 +606,8 @@ export class TopicCloudAdministrationComponent implements OnInit, OnDestroy { getFilteredProfanity(): string { if (this.testProfanityWord) { // eslint-disable-next-line max-len - return this.profanityFilterService.filterProfanityWords(this.testProfanityWord, this.censorPartialWordsCheck, this.censorLanguageSpecificCheck, this.testProfanityLanguage); + return this.profanityFilterService.filterProfanityWords(this.testProfanityWord, this.censorPartialWordsCheck, + this.censorLanguageSpecificCheck, this.testProfanityLanguage)[0]; } else { return ''; } 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 bac19fa7a99d5c893ab1956a506364860e06b40f..5c7d2c91abcaf2781d770f9773724a9f1f4da004 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 @@ -145,8 +145,8 @@ export class TopicCloudFilterComponent implements OnInit, OnDestroy { } getCommentCounts(comments: Comment[]): CommentsCount { - const [data, users] = TagCloudDataService - .buildDataFromComments(this._room.ownerId, this._currentModerators, this._adminData, comments); + const [data, users] = TagCloudDataService.buildDataFromComments(this._room.ownerId, this._currentModerators, + this._adminData, this.roomDataService, comments); const counts = new CommentsCount(); counts.comments = comments.length; counts.users = users.size; diff --git a/src/app/components/shared/dialog/topic-dialog-comment/topic-dialog-comment.component.ts b/src/app/components/shared/dialog/topic-dialog-comment/topic-dialog-comment.component.ts index 438c5192aaaa1846215b6f97d4861d048a3df251..59cecafd13a75b0fb0121a0ac6009c6f9a03a03f 100644 --- a/src/app/components/shared/dialog/topic-dialog-comment/topic-dialog-comment.component.ts +++ b/src/app/components/shared/dialog/topic-dialog-comment/topic-dialog-comment.component.ts @@ -12,7 +12,7 @@ export class TopicDialogCommentComponent implements OnInit { @Input() question: string; @Input() language: Language; - @Input() keyword: string ; + @Input() keyword: string; @Input() maxShowedCharachters: number; @Input() profanityFilter: boolean; @Input() languageSpecific; @@ -28,21 +28,22 @@ export class TopicDialogCommentComponent implements OnInit { public partsShort: string[]; public partsWithoutProfanityShort: string[]; - constructor(private profanityFilterService: ProfanityFilterService) {} + constructor(private profanityFilterService: ProfanityFilterService) { + } get partsOfQuestion() { return this.profanityFilter ? this.partsWithoutProfanity : this.parts; } - get partsOfShortQuestion(){ + get partsOfShortQuestion() { return this.profanityFilter ? this.partsWithoutProfanityShort : this.partsShort; } - splitShortQuestion(question: string){ + splitShortQuestion(question: string) { return question.slice(0, this.maxShowedCharachters).split(this.keyword); } - splitQuestion(question: string){ + splitQuestion(question: string) { return question.split(this.keyword); } @@ -51,8 +52,8 @@ export class TopicDialogCommentComponent implements OnInit { return; } this.question = ViewCommentDataComponent.getTextFromData(this.question); - this.questionWithoutProfanity = this.profanityFilterService. - filterProfanityWords(this.question, this.partialWords, this.languageSpecific, this.language); + this.questionWithoutProfanity = this.profanityFilterService.filterProfanityWords(this.question, + this.partialWords, this.languageSpecific, this.language)[0]; this.partsWithoutProfanity = this.splitQuestion(this.questionWithoutProfanity); this.parts = this.splitQuestion(this.question); this.partsWithoutProfanityShort = this.splitShortQuestion(this.questionWithoutProfanity); diff --git a/src/app/services/util/profanity-filter.service.ts b/src/app/services/util/profanity-filter.service.ts index 73045faea036f26f3e2854f0ce53f4ffad7f5bc9..c77e5bd5eb45437f03204879db9bea16ea08ad82 100644 --- a/src/app/services/util/profanity-filter.service.ts +++ b/src/app/services/util/profanity-filter.service.ts @@ -17,7 +17,8 @@ export class ProfanityFilterService { badNL.splice(badNL.indexOf('nicht'), 1); const badDE = BadWords['de']; badDE.splice(badDE.indexOf('ische'), 1); - this.profanityWords = BadWords['en'] + const badEN = BadWords['en']; + this.profanityWords = badEN .concat(badDE) .concat(BadWords['fr']) .concat(BadWords['ar']) @@ -65,7 +66,10 @@ export class ProfanityFilterService { localStorage.removeItem(this.profanityKey); } - filterProfanityWords(str: string, censorPartialWordsCheck: boolean, censorLanguageSpecificCheck: boolean, lang?: string){ + filterProfanityWords(str: string, + censorPartialWordsCheck: boolean, + censorLanguageSpecificCheck: boolean, + lang?: string): [string, boolean] { let filteredString = str; let profWords = []; if (censorLanguageSpecificCheck) { @@ -75,12 +79,14 @@ export class ProfanityFilterService { } str = str.replace(new RegExp(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi), ''); const toCensoredString = censorPartialWordsCheck ? str.toLowerCase() : str.toLowerCase().split(/[\s,.]+/); + let censored = false; profWords.concat(this.getProfanityListFromStorage()).forEach(word => { if (toCensoredString.includes(word)) { filteredString = this.replaceString(filteredString, word, this.generateCensoredWord(word.length)); + censored = true; } }); - return filteredString; + return [filteredString, censored]; } private replaceString(str: string, search: string, replace: string) { diff --git a/src/app/services/util/room-data.service.ts b/src/app/services/util/room-data.service.ts index 54ccdc019c82af6bca997f7388879d691122af38..c1802e7b8659a3093af0cb70a69dcdeb0a16bac2 100644 --- a/src/app/services/util/room-data.service.ts +++ b/src/app/services/util/room-data.service.ts @@ -16,7 +16,7 @@ export interface UpdateInformation { subtype?: string; comment: Comment; finished?: boolean; - updates?: string[]; + updates?: (keyof Comment)[]; } class RoomDataUpdateSubscription { @@ -82,7 +82,14 @@ interface FastRoomAccessObject { [commentId: string]: Comment; } -type CommentFilterData = [body: string, genKeywords: SpacyKeyword[], userKeywords: SpacyKeyword[]]; +interface CommentFilterData { + body: string; + bodyCensored?: boolean; + genKeywords: SpacyKeyword[]; + genKeywordsCensored?: boolean[]; + userKeywords: SpacyKeyword[]; + userKeywordsCensored?: boolean[]; +} @Injectable({ providedIn: 'root' @@ -148,11 +155,15 @@ export class RoomDataService { public checkProfanity(comment: Comment) { const finish = new Subject<boolean>(); const subscription = finish.asObservable().subscribe(_ => { + let obj; if (this.room.profanityFilter !== ProfanityFilter.deactivated) { - [comment.body, comment.keywordsFromSpacy, comment.keywordsFromQuestioner] = this._savedCommentsAfterFilter.get(comment.id); + obj = this._savedCommentsAfterFilter.get(comment.id); } else { - [comment.body, comment.keywordsFromSpacy, comment.keywordsFromQuestioner] = this._savedCommentsBeforeFilter.get(comment.id); + obj = this._savedCommentsBeforeFilter.get(comment.id); } + comment.body = obj.body; + comment.keywordsFromSpacy = obj.genKeywords; + comment.keywordsFromQuestioner = obj.userKeywords; subscription.unsubscribe(); }); @@ -180,16 +191,27 @@ export class RoomDataService { return this._savedCommentsAfterFilter.get(id)[0]; } + getCensoredInformation(comment: Comment): CommentFilterData { + return this._savedCommentsAfterFilter.get(comment.id); + } + private setCommentBody(comment: Comment) { const genKeywords = RoomDataService.cloneKeywords(comment.keywordsFromSpacy); const userKeywords = RoomDataService.cloneKeywords(comment.keywordsFromQuestioner); - this._savedCommentsBeforeFilter.set(comment.id, [comment.body, genKeywords, userKeywords]); + this._savedCommentsBeforeFilter.set(comment.id, { + body: comment.body, + genKeywords, + userKeywords + }); this._savedCommentsAfterFilter.set(comment.id, this.filterCommentOfProfanity(this.room, comment)); } private filterAllCommentsBodies() { this._currentComments.forEach(comment => { - [comment.body, comment.keywordsFromSpacy, comment.keywordsFromQuestioner] = this._savedCommentsBeforeFilter.get(comment.id); + const obj = this._savedCommentsBeforeFilter.get(comment.id); + comment.body = obj.body; + comment.keywordsFromSpacy = obj.genKeywords; + comment.keywordsFromQuestioner = obj.userKeywords; this.setCommentBody(comment); this.checkProfanity(comment); }); @@ -198,11 +220,20 @@ export class RoomDataService { private filterCommentOfProfanity(room: Room, comment: Comment): CommentFilterData { const partialWords = room.profanityFilter === ProfanityFilter.all || room.profanityFilter === ProfanityFilter.partialWords; const languageSpecific = room.profanityFilter === ProfanityFilter.all || room.profanityFilter === ProfanityFilter.languageSpecific; - return [ - this.profanityFilterService.filterProfanityWords(comment.body, partialWords, languageSpecific, comment.language), - this.checkKeywords(comment.keywordsFromSpacy, partialWords, languageSpecific, comment.language), - this.checkKeywords(comment.keywordsFromQuestioner, partialWords, languageSpecific, comment.language) - ]; + const [body, bodyCensored] = this.profanityFilterService + .filterProfanityWords(comment.body, partialWords, languageSpecific, comment.language); + const [genKeywords, genKeywordsCensored] = this + .checkKeywords(comment.keywordsFromSpacy, partialWords, languageSpecific, comment.language); + const [userKeywords, userKeywordsCensored] = this + .checkKeywords(comment.keywordsFromQuestioner, partialWords, languageSpecific, comment.language); + return { + body, + bodyCensored, + genKeywords, + genKeywordsCensored, + userKeywords, + userKeywordsCensored + }; } private removeCommentBodies(key: string) { @@ -440,14 +471,21 @@ export class RoomDataService { this._fastCommentAccess[id] = undefined; } - private checkKeywords(keywords: SpacyKeyword[], partialWords: boolean, languageSpecific: boolean, lang: string): SpacyKeyword[] { + private checkKeywords(keywords: SpacyKeyword[], + partialWords: boolean, + languageSpecific: boolean, + lang: string): [SpacyKeyword[], boolean[]] { const newKeywords = [...keywords]; + const censored: boolean[] = new Array(keywords.length); for (let i = 0; i < newKeywords.length; i++) { + const [text, textCensored] = this.profanityFilterService + .filterProfanityWords(newKeywords[i].text, partialWords, languageSpecific, lang); + censored[i] = textCensored; newKeywords[i] = { - text: this.profanityFilterService.filterProfanityWords(newKeywords[i].text, partialWords, languageSpecific, lang), + text, dep: newKeywords[i].dep }; } - return newKeywords; + return [newKeywords, censored]; } } diff --git a/src/app/services/util/tag-cloud-data.service.ts b/src/app/services/util/tag-cloud-data.service.ts index f4ddb4012a1a4232e7c5f1f2cb47b88d1dfa8aaa..df5377a709ee8b2858c64a24a360136589a5519d 100644 --- a/src/app/services/util/tag-cloud-data.service.ts +++ b/src/app/services/util/tag-cloud-data.service.ts @@ -106,11 +106,12 @@ export class TagCloudDataService { static buildDataFromComments(roomOwner: string, moderators: string[], adminData: TopicCloudAdminData, + roomDataService: RoomDataService, 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, + TopicCloudAdminService.approveKeywordsOfComment(comment, roomDataService, adminData, (keyword: SpacyKeyword, isFromQuestioner: boolean) => { let current: TagCloudDataTagEntry = data.get(keyword.text); const commentDate = new Date(comment.timestamp); @@ -360,8 +361,8 @@ export class TagCloudDataService { const currentMeta = this._isDemoActive ? this._lastMetaData : this._currentMetaData; const filteredComments = this._lastFetchedComments.filter(comment => this._currentFilter.checkComment(comment)); currentMeta.commentCount = filteredComments.length; - const [data, users] = TagCloudDataService - .buildDataFromComments(this._currentOwner, this._currentModerators, this._adminData, filteredComments); + const [data, users] = TagCloudDataService.buildDataFromComments(this._currentOwner, this._currentModerators, + this._adminData, this._roomDataService, 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 9875cef1ac7c0c23f1525b932f98f0cc7f7c7bbd..bcd43931b9af8300960728f5a55e255a05d755e3 100644 --- a/src/app/services/util/topic-cloud-admin.service.ts +++ b/src/app/services/util/topic-cloud-admin.service.ts @@ -15,6 +15,7 @@ import { BehaviorSubject, Observable, Subject, Subscription } from 'rxjs'; import { Comment } from '../../models/comment'; import { UserRole } from '../../models/user-roles.enum'; import { CloudParameters } from '../../utils/cloud-parameters'; +import { RoomDataService } from './room-data.service'; @Injectable({ providedIn: 'root', @@ -54,31 +55,43 @@ export class TopicCloudAdminService { room.tagCloudSettings = JSON.stringify(settings); } - static approveKeywordsOfComment(comment: Comment, config: TopicCloudAdminData, keywordFunc: (SpacyKeyword, boolean) => void) { + static approveKeywordsOfComment(comment: Comment, + roomDataService: RoomDataService, + config: TopicCloudAdminData, + keywordFunc: (SpacyKeyword, boolean) => void) { + const regexMaskKeyword = /\b(frage|antwort|aufgabe|hallo|test|bzw|muss|more to come)\b/gmi; let source = comment.keywordsFromQuestioner; + let censored = roomDataService.getCensoredInformation(comment).userKeywordsCensored; let isFromQuestioner = true; if (config.keywordORfulltext === KeywordOrFulltext.both) { if (!source || !source.length) { - source = comment.keywordsFromSpacy; isFromQuestioner = false; + source = comment.keywordsFromSpacy; + censored = roomDataService.getCensoredInformation(comment).genKeywordsCensored; } } else if (config.keywordORfulltext === KeywordOrFulltext.fulltext) { isFromQuestioner = false; source = comment.keywordsFromSpacy; + censored = roomDataService.getCensoredInformation(comment).genKeywordsCensored; } if (!source) { return; } const wantedLabels = config.wantedLabels[comment.language.toLowerCase()]; - for (const keyword of source) { - if (wantedLabels && (!keyword.dep || !keyword.dep.some(e => wantedLabels.includes(e)))) { + for (let i = 0; i < source.length; i++) { + const keyword = source[i]; + keyword.text = keyword.text.replace(regexMaskKeyword, '').replace(/ +/, ' '); + if (keyword.text.trim().length < 1) { continue; } - let isProfanity = !!keyword.text.match(/\*/); - if (isProfanity) { + if (censored[i]) { + continue; + } + if (wantedLabels && (!keyword.dep || !keyword.dep.some(e => wantedLabels.includes(e)))) { continue; } const lowerCasedKeyword = keyword.text.toLowerCase(); + let isProfanity = false; for (const word of config.blacklist) { if (lowerCasedKeyword.includes(word)) { isProfanity = true;