MOBILE-2491 filter: Support wsNotFiltered option and filter in chart

main
Dani Palou 2019-10-03 11:25:58 +02:00
parent 952ce4939b
commit 31caccc396
19 changed files with 332 additions and 123 deletions

View File

@ -15,6 +15,8 @@
import { Injectable } from '@angular/core';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreSite } from '@classes/site';
/**
* Handler to support the Activity names filter.
@ -29,4 +31,15 @@ export class AddonFilterActivityNamesHandler extends CoreFilterDefaultHandler {
// This filter is handled by Moodle, nothing to do in the app.
}
/**
* Check if the filter should be applied in a certain site based on some filter options.
*
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
return false;
}
}

View File

@ -15,6 +15,8 @@
import { Injectable } from '@angular/core';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreSite } from '@classes/site';
/**
* Handler to support the Word censorship filter.
@ -29,4 +31,15 @@ export class AddonFilterCensorHandler extends CoreFilterDefaultHandler {
// This filter is handled by Moodle, nothing to do in the app.
}
/**
* Check if the filter should be applied in a certain site based on some filter options.
*
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
return false;
}
}

View File

@ -15,6 +15,8 @@
import { Injectable } from '@angular/core';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreSite } from '@classes/site';
/**
* Handler to support the Database auto-link filter.
@ -29,4 +31,15 @@ export class AddonFilterDataHandler extends CoreFilterDefaultHandler {
// This filter is handled by Moodle, nothing to do in the app.
}
/**
* Check if the filter should be applied in a certain site based on some filter options.
*
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
return false;
}
}

View File

@ -15,6 +15,8 @@
import { Injectable } from '@angular/core';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreSite } from '@classes/site';
/**
* Handler to support the Email protection filter.
@ -29,4 +31,15 @@ export class AddonFilterEmailProtectHandler extends CoreFilterDefaultHandler {
// This filter is handled by Moodle, nothing to do in the app.
}
/**
* Check if the filter should be applied in a certain site based on some filter options.
*
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
return false;
}
}

View File

@ -15,6 +15,8 @@
import { Injectable } from '@angular/core';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreSite } from '@classes/site';
/**
* Handler to support the Emoticon filter.
@ -29,4 +31,15 @@ export class AddonFilterEmoticonHandler extends CoreFilterDefaultHandler {
// This filter is handled by Moodle, nothing to do in the app.
}
/**
* Check if the filter should be applied in a certain site based on some filter options.
*
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
return false;
}
}

View File

@ -15,6 +15,8 @@
import { Injectable } from '@angular/core';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreSite } from '@classes/site';
/**
* Handler to support the Glossary auto-link filter.
@ -29,4 +31,15 @@ export class AddonFilterGlossaryHandler extends CoreFilterDefaultHandler {
// This filter is handled by Moodle, nothing to do in the app.
}
/**
* Check if the filter should be applied in a certain site based on some filter options.
*
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
return false;
}
}

View File

@ -15,7 +15,7 @@
import { Injectable } from '@angular/core';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFilter } from '@core/filter/providers/filter';
import { CoreFilterFilter, CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreTextUtilsProvider } from '@providers/utils/text';
/**
@ -36,9 +36,11 @@ export class AddonFilterMediaPluginHandler extends CoreFilterDefaultHandler {
* @param text The text to filter.
* @param filter The filter.
* @param options Options passed to the filters.
* @param siteId Site ID. If not defined, current site.
* @return Filtered text (or promise resolved with the filtered text).
*/
filter(text: string, filter: CoreFilterFilter, options: any): string | Promise<string> {
filter(text: string, filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, siteId?: string)
: string | Promise<string> {
const div = document.createElement('div');
div.innerHTML = text;

View File

@ -16,8 +16,9 @@
import { Injectable } from '@angular/core';
import { CoreSitesProvider } from '@providers/sites';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFilter } from '@core/filter/providers/filter';
import { CoreFilterFilter, CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreLangProvider } from '@providers/lang';
import { CoreSite } from '@classes/site';
/**
* Handler to support the Multilang filter.
@ -32,50 +33,61 @@ export class AddonFilterMultilangHandler extends CoreFilterDefaultHandler {
super();
}
/**
* Whether or not the handler is enabled on a site level.
*
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
*/
isEnabled(): boolean | Promise<boolean> {
// In Moodle versions older than 3.7, some specific content can be received unfiltered. Filter it in the app.
const currentSite = this.sitesProvider.getCurrentSite();
return !currentSite.isVersionGreaterEqualThan('3.7');
}
/**
* Filter some text.
*
* @param text The text to filter.
* @param filter The filter.
* @param options Options passed to the filters.
* @param siteId Site ID. If not defined, current site.
* @return Filtered text (or promise resolved with the filtered text).
*/
filter(text: string, filter: CoreFilterFilter, options: any): string | Promise<string> {
filter(text: string, filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, siteId?: string)
: string | Promise<string> {
return this.langProvider.getCurrentLanguage().then((language) => {
// Match the current language.
const anyLangRegEx = /<(?:lang|span)[^>]+lang="[a-zA-Z0-9_-]+"[^>]*>(.*?)<\/(?:lang|span)>/g;
let currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>', 'g');
return this.sitesProvider.getSite(siteId).then((site) => {
if (!text.match(currentLangRegEx)) {
// Current lang not found. Try to find the first language.
const matches = text.match(anyLangRegEx);
if (matches && matches[0]) {
language = matches[0].match(/lang="([a-zA-Z0-9_-]+)"/)[1];
currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>', 'g');
} else {
// No multi-lang tag found, stop.
return text;
}
// Don't apply this filter if Moodle is 3.7 or higher and the WS already filtered the content.
if (!this.shouldBeApplied(options, site)) {
return text;
}
// Extract contents of current language.
text = text.replace(currentLangRegEx, '$1');
// Delete the rest of languages
text = text.replace(anyLangRegEx, '');
return text;
return this.langProvider.getCurrentLanguage().then((language) => {
// Match the current language.
const anyLangRegEx = /<(?:lang|span)[^>]+lang="[a-zA-Z0-9_-]+"[^>]*>(.*?)<\/(?:lang|span)>/g;
let currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>', 'g');
if (!text.match(currentLangRegEx)) {
// Current lang not found. Try to find the first language.
const matches = text.match(anyLangRegEx);
if (matches && matches[0]) {
language = matches[0].match(/lang="([a-zA-Z0-9_-]+)"/)[1];
currentLangRegEx = new RegExp('<(?:lang|span)[^>]+lang="' + language + '"[^>]*>(.*?)<\/(?:lang|span)>',
'g');
} else {
// No multi-lang tag found, stop.
return text;
}
}
// Extract contents of current language.
text = text.replace(currentLangRegEx, '$1');
// Delete the rest of languages
text = text.replace(anyLangRegEx, '');
return text;
});
});
}
/**
* Check if the filter should be applied in a certain site based on some filter options.
*
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
// The filter should be applied if site is older than 3.7 or the WS didn't filter the text.
return options.wsNotFiltered || !site.isVersionGreaterEqualThan('3.7');
}
}

View File

@ -15,6 +15,8 @@
import { Injectable } from '@angular/core';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreSite } from '@classes/site';
/**
* Handler to support the TeX notation filter.
@ -29,4 +31,15 @@ export class AddonFilterTexHandler extends CoreFilterDefaultHandler {
// This filter is handled by Moodle, nothing to do in the app.
}
/**
* Check if the filter should be applied in a certain site based on some filter options.
*
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
return false;
}
}

View File

@ -15,6 +15,8 @@
import { Injectable } from '@angular/core';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreSite } from '@classes/site';
/**
* Handler to support the HTML tidy filter.
@ -29,4 +31,15 @@ export class AddonFilterTidyHandler extends CoreFilterDefaultHandler {
// This filter is handled by Moodle, nothing to do in the app.
}
/**
* Check if the filter should be applied in a certain site based on some filter options.
*
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
return false;
}
}

View File

@ -15,6 +15,8 @@
import { Injectable } from '@angular/core';
import { CoreFilterDefaultHandler } from '@core/filter/providers/default-filter';
import { CoreFilterFormatTextOptions } from '@core/filter/providers/filter';
import { CoreSite } from '@classes/site';
/**
* Handler to support the URL to link and images filter.
@ -29,4 +31,15 @@ export class AddonFilterUrlToLinkHandler extends CoreFilterDefaultHandler {
// This filter is handled by Moodle, nothing to do in the app.
}
/**
* Check if the filter should be applied in a certain site based on some filter options.
*
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
return false;
}
}

View File

@ -73,7 +73,7 @@
<ion-icon item-start name="warning" color="warning"></ion-icon> {{ 'addon.mod_choice.resultsnotsynced' | translate }}
</ion-item>
<ion-item>
<core-chart type="pie" [data]="data" [labels]="labels" height="300"></core-chart>
<core-chart type="pie" [data]="data" [labels]="labels" height="300" contextLevel="module" [contextInstanceId]="module.id"></core-chart>
</ion-item>
</ion-col>
<ion-col *ngIf="choice.publish && results" col-12 col-lg-7>

View File

@ -162,7 +162,7 @@
</ul>
</ng-container>
<ng-container *ngSwitchCase="'chart'">
<core-chart [type]="item.chartType" [data]="item.chartData" [labels]="item.labels" height="300"></core-chart>
<core-chart [type]="item.chartType" [data]="item.chartData" [labels]="item.labels" height="300" contextLevel="module" [contextInstanceId]="module.id" [wsNotFiltered]="true"></core-chart>
<p *ngIf="item.average">{{ 'addon.mod_feedback.average' | translate }}: {{item.average | number : '1.2-2'}}</p>
</ng-container>
</ng-container>

View File

@ -23,7 +23,7 @@
<div item-content class="addon-mod_feedback-form-content" *ngIf="item.template">
<ng-container [ngSwitch]="item.template">
<ng-container *ngSwitchCase="'label'">
<p><core-format-text [component]="component" [componentId]="componentId" [text]="item.presentation" contextLevel="module" [contextInstanceId]="module.id"></core-format-text></p>
<p><core-format-text [component]="component" [componentId]="componentId" [text]="item.presentation" contextLevel="module" [contextInstanceId]="module.id" [wsNotFiltered]="true"></core-format-text></p>
</ng-container>
<ng-container *ngSwitchCase="'textfield'">
<ion-input type="text" [(ngModel)]="item.value" autocorrect="off" name="{{item.typ}}_{{item.id}}" maxlength="{{item.maxlength}}" [required]="item.required"></ion-input>
@ -38,20 +38,20 @@
<ng-container *ngSwitchCase="'multichoice-r'">
<ion-list radio-group [(ngModel)]="item.value" [required]="item.required" name="{{item.typ}}_{{item.id}}">
<ion-item *ngFor="let option of item.choices">
<ion-label><core-format-text [component]="component" [componentId]="componentId" [text]="option.label" contextLevel="module" [contextInstanceId]="module.id"></core-format-text></ion-label>
<ion-label><core-format-text [component]="component" [componentId]="componentId" [text]="option.label" contextLevel="module" [contextInstanceId]="module.id" [wsNotFiltered]="true"></core-format-text></ion-label>
<ion-radio [value]="option.value"></ion-radio>
</ion-item>
</ion-list>
</ng-container>
<ion-list *ngSwitchCase="'multichoice-c'">
<ion-item *ngFor="let option of item.choices">
<ion-label><core-format-text [component]="component" [componentId]="componentId" [text]="option.label" contextLevel="module" [contextInstanceId]="module.id"></core-format-text></ion-label>
<ion-label><core-format-text [component]="component" [componentId]="componentId" [text]="option.label" contextLevel="module" [contextInstanceId]="module.id" [wsNotFiltered]="true"></core-format-text></ion-label>
<ion-checkbox [required]="item.required" name="{{item.typ}}_{{item.id}}" [(ngModel)]="option.checked" value="option.value"></ion-checkbox>
</ion-item>
</ion-list>
<ng-container *ngSwitchCase="'multichoice-d'">
<ion-select [required]="item.required" name="{{item.typ}}_{{item.id}}" [(ngModel)]="item.value" interface="action-sheet">
<ion-option *ngFor="let option of item.choices" [value]="option.value"><core-format-text [component]="component" [componentId]="componentId" [text]="option.label" contextLevel="module" [contextInstanceId]="module.id"></core-format-text></ion-option>
<ion-option *ngFor="let option of item.choices" [value]="option.value"><core-format-text [component]="component" [componentId]="componentId" [text]="option.label" contextLevel="module" [contextInstanceId]="module.id" [wsNotFiltered]="true"></core-format-text></ion-option>
</ion-select>
</ng-container>
<ng-container *ngSwitchCase="'captcha'">

View File

@ -14,6 +14,8 @@
import { Component, Input, OnDestroy, OnInit, ElementRef, OnChanges, ViewChild } from '@angular/core';
import { Chart } from 'chart.js';
import { CoreFilterProvider } from '@core/filter/providers/filter';
import { CoreUtilsProvider } from '@providers/utils/utils';
/**
* This component shows a chart using chart.js.
@ -44,13 +46,15 @@ export class CoreChartComponent implements OnDestroy, OnInit, OnChanges {
@Input() type: string; // Type of chart.
@Input() legend: any; // Legend options.
@Input() height = 300; // Height of the chart element.
@Input() filter?: boolean | string; // Whether to filter labels. If not defined, true if contextLevel and instanceId are set.
@Input() contextLevel?: string; // The context level of the text.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() wsNotFiltered?: boolean | string; // If true it means the WS didn't filter the labels for some reason.
@ViewChild('canvas') canvas: ElementRef;
chart: any;
constructor() {
// Nothing to do.
}
constructor(protected filterProvider: CoreFilterProvider, private utils: CoreUtilsProvider) { }
/**
* Component being initialized.
@ -86,17 +90,21 @@ export class CoreChartComponent implements OnDestroy, OnInit, OnChanges {
this.type = 'horizontalBar';
}
const context = this.canvas.nativeElement.getContext('2d');
this.chart = new Chart(context, {
type: this.type,
data: {
labels: this.labels,
datasets: [{
data: this.data,
backgroundColor: this.getRandomColors(this.data.length)
}]
},
options: {legend: legend}
// Format labels if needed.
this.formatLabels().then(() => {
const context = this.canvas.nativeElement.getContext('2d');
this.chart = new Chart(context, {
type: this.type,
data: {
labels: this.labels,
datasets: [{
data: this.data,
backgroundColor: this.getRandomColors(this.data.length)
}]
},
options: {legend: legend}
});
});
}
@ -105,15 +113,49 @@ export class CoreChartComponent implements OnDestroy, OnInit, OnChanges {
*/
ngOnChanges(): void {
if (this.chart) {
this.chart.data.datasets[0] = {
data: this.data,
backgroundColor: this.getRandomColors(this.data.length)
};
this.chart.data.labels = this.labels;
this.chart.update();
// Format labels if needed.
this.formatLabels().then(() => {
this.chart.data.datasets[0] = {
data: this.data,
backgroundColor: this.getRandomColors(this.data.length)
};
this.chart.data.labels = this.labels;
this.chart.update();
});
}
}
/**
* Format labels if needed.
*
* @return Promise resolved when done.
*/
protected formatLabels(): Promise<any> {
this.filter = typeof this.filter == 'undefined' ? !!(this.contextLevel && this.contextInstanceId) : !!this.filter;
if (!this.filter) {
return Promise.resolve();
}
const options = {
clean: true,
singleLine: true,
wsNotFiltered: this.utils.isTrueOrOne(this.wsNotFiltered)
};
return this.filterProvider.getFilters(this.contextLevel, this.contextInstanceId, options).then((filters) => {
const promises = [];
this.labels.forEach((label, i) => {
promises.push(this.filterProvider.formatText(label, options, filters).then((text) => {
this.labels[i] = text;
}));
});
return Promise.all(promises);
});
}
/**
* Generate random colors if needed.
*

View File

@ -14,7 +14,8 @@
import { Injectable } from '@angular/core';
import { CoreFilterHandler } from './delegate';
import { CoreFilterFilter } from './filter';
import { CoreFilterFilter, CoreFilterFormatTextOptions } from './filter';
import { CoreSite } from '@classes/site';
/**
* Default handler used when the module doesn't have a specific implementation.
@ -34,9 +35,11 @@ export class CoreFilterDefaultHandler implements CoreFilterHandler {
* @param text The text to filter.
* @param filter The filter.
* @param options Options passed to the filters.
* @param siteId Site ID. If not defined, current site.
* @return Filtered text (or promise resolved with the filtered text).
*/
filter(text: string, filter: CoreFilterFilter, options: any): string | Promise<string> {
filter(text: string, filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, siteId?: string)
: string | Promise<string> {
return text;
}
@ -50,15 +53,13 @@ export class CoreFilterDefaultHandler implements CoreFilterHandler {
}
/**
* Setup the filter to be used.
* Check if the filter should be applied in a certain site based on some filter options.
*
* Please notice this method iwill be called for each piece of text being filtered, so it is responsible
* for controlling its own execution cardinality.
*
* @param filter The filter.
* @return Promise resolved when done, or nothing if it's synchronous.
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
setup(filter: CoreFilterFilter): void | Promise<any> {
// Nothing to do.
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean {
return true;
}
}

View File

@ -16,9 +16,10 @@ import { Injectable } from '@angular/core';
import { CoreEventsProvider } from '@providers/events';
import { CoreLoggerProvider } from '@providers/logger';
import { CoreSitesProvider } from '@providers/sites';
import { CoreFilterFilter } from './filter';
import { CoreFilterFilter, CoreFilterFormatTextOptions } from './filter';
import { CoreFilterDefaultHandler } from './default-filter';
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
import { CoreSite } from '@classes/site';
/**
* Interface that all filter handlers must implement.
@ -35,20 +36,19 @@ export interface CoreFilterHandler extends CoreDelegateHandler {
* @param text The text to filter.
* @param filter The filter.
* @param options Options passed to the filters.
* @param siteId Site ID. If not defined, current site.
* @return Filtered text (or promise resolved with the filtered text).
*/
filter(text: string, filter: CoreFilterFilter, options: any): string | Promise<string>;
filter(text: string, filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, siteId?: string): string | Promise<string>;
/**
* Setup the filter to be used.
* Check if the filter should be applied in a certain site based on some filter options.
*
* Please notice this method iwill be called for each piece of text being filtered, so it is responsible
* for controlling its own execution cardinality.
*
* @param filter The filter.
* @return Promise resolved when done, or nothing if it's synchronous.
* @param options Options.
* @param site Site.
* @return Whether filter should be applied.
*/
setup(filter: CoreFilterFilter): void | Promise<any>;
shouldBeApplied(options: CoreFilterFormatTextOptions, site?: CoreSite): boolean;
}
/**
@ -71,12 +71,10 @@ export class CoreFilterDelegate extends CoreDelegate {
* @param filters Filters to apply.
* @param options Options passed to the filters.
* @param skipFilters Names of filters that shouldn't be applied.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with the filtered text.
*/
filterText(text: string, filters: CoreFilterFilter[], options?: any, skipFilters?: string[]): Promise<string> {
if (!text || typeof text != 'string') {
return Promise.resolve('');
}
filterText(text: string, filters: CoreFilterFilter[], options?: any, skipFilters?: string[], siteId?: string): Promise<string> {
// Wait for filters to be initialized.
return this.handlersInitPromise.then(() => {
@ -98,7 +96,7 @@ export class CoreFilterDelegate extends CoreDelegate {
}
promise = promise.then((text) => {
return this.executeFunctionOnEnabled(filter.filter, 'filter', [text, filter, options]);
return this.executeFunctionOnEnabled(filter.filter, 'filter', [text, filter, options, siteId]);
});
});
@ -138,18 +136,48 @@ export class CoreFilterDelegate extends CoreDelegate {
}
/**
* Setup filters to be applied to some content.
* Check if at least 1 filter should be applied in a certain site and with certain options.
*
* @param filters Filters to apply.
* @return Promise resolved when done.
* @param filter Filter to check.
* @param options Options passed to the filters.
* @param site Site. If not defined, current site.
* @return {Promise<boolean>} Promise resolved with true: whether the filter should be applied.
*/
setupFilters(filters: CoreFilterFilter[]): Promise<any> {
const promises: Promise<any>[] = [];
shouldBeApplied(filters: CoreFilterFilter[], options: CoreFilterFormatTextOptions, site?: CoreSite): Promise<boolean> {
// Wait for filters to be initialized.
return this.handlersInitPromise.then(() => {
const promises = [];
let shouldBeApplied = false;
filters.forEach((filter) => {
promises.push(this.executeFunctionOnEnabled(filter.filter, 'setup', [filter]));
filters.forEach((filter) => {
promises.push(this.shouldFilterBeApplied(filter, options, site).then((applied) => {
if (applied) {
shouldBeApplied = applied;
}
}));
});
return Promise.all(promises).then(() => {
return shouldBeApplied;
});
});
}
return Promise.all(promises);
/**
* Check whether a filter should be applied in a certain site and with certain options.
*
* @param filter Filter to check.
* @param options Options passed to the filters.
* @param site Site. If not defined, current site.
* @return {Promise<boolean>} Promise resolved with true: whether the filter should be applied.
*/
protected shouldFilterBeApplied(filter: CoreFilterFilter, options: CoreFilterFormatTextOptions, site?: CoreSite)
: Promise<boolean> {
if (!this.hasHandler(filter.filter, true)) {
return Promise.resolve(false);
}
return Promise.resolve(this.executeFunctionOnEnabled(filter.filter, 'shouldBeApplied', [options, site]));
}
}

View File

@ -29,8 +29,6 @@ export class CoreFilterProvider {
protected ROOT_CACHE_KEY = 'mmFilter:';
protected logger;
protected FILTERS_NOT_TREATED = ['activitynames', 'censor', 'data', 'emailprotect', 'emoticon', 'glossary', 'tex', 'tidy',
'urltolink'];
constructor(logger: CoreLoggerProvider,
private sitesProvider: CoreSitesProvider,
@ -71,9 +69,11 @@ export class CoreFilterProvider {
* @param text The text to be formatted.
* @param options Formatting options.
* @param filters The filters to apply. Required if filter is set to true.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with the formatted text.
*/
formatText(text: string, options?: CoreFilterFormatTextOptions, filters?: CoreFilterFilter[]): Promise<string> {
formatText(text: string, options?: CoreFilterFormatTextOptions, filters?: CoreFilterFilter[], siteId?: string)
: Promise<string> {
if (!text || typeof text != 'string') {
// No need to do any filters and cleaning.
@ -95,12 +95,10 @@ export class CoreFilterProvider {
options.filter = false;
}
// @todo: Setup?
let promise: Promise<string>;
if (options.filter) {
promise = this.filterDelegate.filterText(text, filters, options);
promise = this.filterDelegate.filterText(text, filters, options, [], siteId);
} else {
promise = Promise.resolve(text);
}
@ -250,17 +248,18 @@ export class CoreFilterProvider {
}
/**
* Get filters and format text.
* Get the filters in a certain context, performing some checks like the site version.
* It's recommended to use this function instead of canGetAvailableInContext because this function will check if
* it's really needed to call the WS.
*
* @param text Text to filter.
* @param contextLevel The context level.
* @param instanceId Instance ID related to the context.
* @param options Options for format text.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with the formatted text.
* @return Promise resolved with the filters.
*/
getFiltersAndFormatText(text: string, contextLevel: string, instanceId: number, options?: CoreFilterFormatTextOptions,
siteId?: string): Promise<string> {
getFilters(contextLevel: string, instanceId: number, options?: CoreFilterFormatTextOptions, siteId?: string)
: Promise<CoreFilterFilter[]> {
options.contextLevel = contextLevel;
options.instanceId = instanceId;
@ -275,7 +274,7 @@ export class CoreFilterProvider {
}
// Check if site has any filter to treat.
return this.siteHasFiltersToTreat(siteId).then((hasFilters) => {
return this.siteHasFiltersToTreat(options, siteId).then((hasFilters) => {
if (hasFilters) {
options.filter = true;
@ -286,8 +285,24 @@ export class CoreFilterProvider {
}).catch(() => {
return [];
});
}).then((filters) => {
return this.formatText(text, options, filters);
});
}
/**
* Get filters and format text.
*
* @param text Text to filter.
* @param contextLevel The context level.
* @param instanceId Instance ID related to the context.
* @param options Options for format text.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with the formatted text.
*/
getFiltersAndFormatText(text: string, contextLevel: string, instanceId: number, options?: CoreFilterFormatTextOptions,
siteId?: string): Promise<string> {
return this.getFilters(contextLevel, instanceId, options, siteId).then((filters) => {
return this.formatText(text, options, filters, siteId);
});
}
@ -331,29 +346,19 @@ export class CoreFilterProvider {
/**
* Check if site has available any filter that should be treated by the app.
*
* @param options Options passed to the filters.
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with boolean: whether it has filters to treat.
*/
siteHasFiltersToTreat(siteId?: string): Promise<boolean> {
siteHasFiltersToTreat(options?: CoreFilterFormatTextOptions, siteId?: string): Promise<boolean> {
options = options || {};
return this.sitesProvider.getSite(siteId).then((site) => {
// Get filters at site level.
return this.getAvailableInContext('system', site.getSiteHomeId(), site.getId()).then((filters) => {
for (let i = 0; i < filters.length; i++) {
const filter = filters[i];
if (this.FILTERS_NOT_TREATED.indexOf(filter.filter) != -1) {
continue;
}
if (this.filterDelegate.hasHandler(filter.filter, true)) {
// There is a filter to treat and is enabled.
return true;
}
}
return false;
return this.filterDelegate.shouldBeApplied(filters, options, site);
});
});
}
@ -390,4 +395,5 @@ export type CoreFilterFormatTextOptions = {
singleLine?: boolean; // If true then new lines will be removed (all the text in a single line).
shortenLength?: number; // Number of characters to shorten the text.
highlight?: string; // Text to highlight.
wsNotFiltered?: boolean; // If true it means the WS didn't filter the text for some reason.
};

View File

@ -61,6 +61,7 @@ export class CoreFormatTextDirective implements OnChanges {
@Input() filter?: boolean | string; // Whether to filter the text. If not defined, true if contextLevel and instanceId are set.
@Input() contextLevel?: string; // The context level of the text.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() wsNotFiltered?: boolean | string; // If true it means the WS didn't filter the text for some reason.
@Output() afterRender?: EventEmitter<any>; // Called when the data is rendered.
protected element: HTMLElement;
@ -382,7 +383,7 @@ export class CoreFormatTextDirective implements OnChanges {
clean: this.utils.isTrueOrOne(this.clean),
singleLine: this.utils.isTrueOrOne(this.singleLine),
highlight: this.highlight,
filter: this.filter
wsNotFiltered: this.utils.isTrueOrOne(this.wsNotFiltered)
};
if (this.filter) {