diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index eecfe4cf5..8ea19a186 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -45,8 +45,17 @@ declare global { interface Number { modulo(n: number): number; } + + interface String { + decode(): string; + } } +/** + * May only be created once since this thing is fat + */ +const domParser = new DOMParser(); + /** * Angular's global App Component */ @@ -103,6 +112,7 @@ export class AppComponent { this.overloadSetFunctions(); this.overloadModulo(); this.loadCustomIcons(); + this.overloadDecodeString(); // Wait until the App reaches a stable state. // Required for the Service Worker. @@ -198,6 +208,23 @@ export class AppComponent { }); } + /** + * This is not the fastest solution but the most reliable one. + * Certain languages and TinyMCE do not follow the any predictable + * behaviour when it comes to encoding UTF8. + * decodeURI and decodeURIComponent were not able to successfully + * replace any ;&*uml with something meaningfull. + */ + private overloadDecodeString(): void { + Object.defineProperty(String.prototype, 'decode', { + enumerable: false, + value(): string { + const doc = domParser.parseFromString(this, 'text/html'); + return doc.body.textContent || ''; + } + }); + } + /** * Enhances the number object with a real modulo operation (not remainder). * TODO: Remove this, if the remainder operation is changed to modulo. diff --git a/client/src/app/site/motions/models/view-motion.ts b/client/src/app/site/motions/models/view-motion.ts index 783f9cf9c..ae9c3e04d 100644 --- a/client/src/app/site/motions/models/view-motion.ts +++ b/client/src/app/site/motions/models/view-motion.ts @@ -231,19 +231,25 @@ export class ViewMotion const properties: SearchProperty[] = []; properties.push({ key: 'Title', value: this.getTitle() }); properties.push({ key: 'Submitters', value: this.submittersAsUsers.map(user => user.full_name).join(', ') }); - properties.push({ key: 'Text', value: this.text, trusted: true }); - properties.push({ key: 'Reason', value: this.reason, trusted: true }); + properties.push({ key: 'Text', value: this.text.decode(), trusted: true }); + properties.push({ key: 'Reason', value: this.reason.decode(), trusted: true }); if (this.amendment_paragraphs) { properties.push({ key: 'Amendments', - value: this.amendment_paragraphs.filter(x => !!x).join('\n'), + value: this.amendment_paragraphs + .filter(x => !!x) + .join('\n') + .decode(), trusted: true }); } properties.push({ key: 'Tags', value: this.tags.map(tag => tag.getTitle()).join(', ') }); properties.push({ key: 'Comments', - value: this.motion.comments.map(comment => comment.comment).join('\n'), + value: this.motion.comments + .map(comment => comment.comment) + .join('\n') + .decode(), trusted: true }); properties.push({ key: 'Supporters', value: this.supporters.map(user => user.full_name).join(', ') }); diff --git a/client/src/app/site/topics/models/view-topic.ts b/client/src/app/site/topics/models/view-topic.ts index 26fb3ba6a..5f3116282 100644 --- a/client/src/app/site/topics/models/view-topic.ts +++ b/client/src/app/site/topics/models/view-topic.ts @@ -27,12 +27,14 @@ export class ViewTopic extends BaseViewModelWithAgendaItemAndListOfSpeakers property.value) }; }