Merge pull request #3963 from dpalou/MOBILE-4526

Mobile 4526
main
Noel De Martin 2024-03-13 08:34:49 +01:00 committed by GitHub
commit 0b9ee2d29b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
64 changed files with 525 additions and 192 deletions

View File

@ -20,6 +20,7 @@ import { CoreCourseBlock } from '@features/course/services/course';
import { Params } from '@angular/router';
import { makeSingleton } from '@singletons';
import { AddonCalendarMainMenuHandlerService } from '@addons/calendar/services/handlers/mainmenu';
import { ContextLevel } from '@/core/constants';
/**
* Block handler.
@ -38,8 +39,8 @@ export class AddonBlockCalendarMonthHandlerService extends CoreBlockBaseHandler
* @param instanceId The instance ID associated with the context level.
* @returns Data or promise resolved with the data.
*/
getDisplayData(block: CoreCourseBlock, contextLevel: string, instanceId: number): CoreBlockHandlerData {
const linkParams: Params = contextLevel == 'course' ? { courseId: instanceId } : {};
getDisplayData(block: CoreCourseBlock, contextLevel: ContextLevel, instanceId: number): CoreBlockHandlerData {
const linkParams: Params = contextLevel === ContextLevel.COURSE ? { courseId: instanceId } : {};
return {
title: 'addon.block_calendarmonth.pluginname',

View File

@ -21,6 +21,7 @@ import { Params } from '@angular/router';
import { makeSingleton } from '@singletons';
import { AddonCalendarMainMenuHandlerService } from '@addons/calendar/services/handlers/mainmenu';
import { CoreSites } from '@services/sites';
import { ContextLevel } from '@/core/constants';
/**
* Block handler.
@ -39,10 +40,10 @@ export class AddonBlockCalendarUpcomingHandlerService extends CoreBlockBaseHandl
* @param instanceId The instance ID associated with the context level.
* @returns Data or promise resolved with the data.
*/
getDisplayData(block: CoreCourseBlock, contextLevel: string, instanceId: number): CoreBlockHandlerData {
getDisplayData(block: CoreCourseBlock, contextLevel: ContextLevel, instanceId: number): CoreBlockHandlerData {
const linkParams: Params = { upcoming: true };
if (contextLevel == 'course' && instanceId !== CoreSites.getCurrentSiteHomeId()) {
if (contextLevel === ContextLevel.COURSE && instanceId !== CoreSites.getCurrentSiteHomeId()) {
linkParams.courseId = instanceId;
}

View File

@ -19,6 +19,7 @@ import { CoreBlockBaseHandler } from '@features/block/classes/base-block-handler
import { CoreCourseBlock } from '@features/course/services/course';
import { makeSingleton } from '@singletons';
import { CoreComments } from '@features/comments/services/comments';
import { ContextLevel } from '@/core/constants';
/**
* Block handler.
@ -44,7 +45,7 @@ export class AddonBlockCommentsHandlerService extends CoreBlockBaseHandler {
* @param instanceId The instance ID associated with the context level.
* @returns Data or promise resolved with the data.
*/
getDisplayData(block: CoreCourseBlock, contextLevel: string, instanceId: number): CoreBlockHandlerData {
getDisplayData(block: CoreCourseBlock, contextLevel: ContextLevel, instanceId: number): CoreBlockHandlerData {
return {
title: 'addon.block_comments.pluginname',
class: 'addon-block-comments',

View File

@ -19,6 +19,7 @@ import { CoreBlockBaseHandler } from '@features/block/classes/base-block-handler
import { CoreCourseBlock } from '@features/course/services/course';
import { makeSingleton } from '@singletons';
import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion';
import { ContextLevel } from '@/core/constants';
/**
* Block handler.
@ -41,7 +42,7 @@ export class AddonBlockCompletionStatusHandlerService extends CoreBlockBaseHandl
*/
async getDisplayData(
block: CoreCourseBlock,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
): Promise<undefined | CoreBlockHandlerData> {
if (contextLevel !== 'course') {

View File

@ -20,6 +20,7 @@ import { makeSingleton } from '@singletons';
import { CoreCourseBlock } from '@features/course/services/course';
import { CORE_SEARCH_PAGE_NAME } from '@features/search/services/handlers/mainmenu';
import { CoreSearchGlobalSearch } from '@features/search/services/global-search';
import { ContextLevel } from '@/core/constants';
/**
* Block handler.
@ -40,8 +41,8 @@ export class AddonBlockGlobalSearchHandlerService extends CoreBlockBaseHandler {
/**
* @inheritdoc
*/
getDisplayData(block: CoreCourseBlock, contextLevel: string, instanceId: number): CoreBlockHandlerData | undefined {
const isCourseSearch = contextLevel === 'course';
getDisplayData(block: CoreCourseBlock, contextLevel: ContextLevel, instanceId: number): CoreBlockHandlerData | undefined {
const isCourseSearch = contextLevel === ContextLevel.COURSE;
return {
title: isCourseSearch ? 'core.search' : 'addon.block_globalsearch.pluginname',

View File

@ -20,6 +20,7 @@ import { makeSingleton } from '@singletons';
import { ADDON_MOD_FORUM_SEARCH_PAGE_NAME } from '@addons/mod/forum/constants';
import { CoreCourseBlock } from '@features/course/services/course';
import { CoreSearchGlobalSearch } from '@features/search/services/global-search';
import { ContextLevel } from '@/core/constants';
/**
* Block handler.
@ -42,10 +43,10 @@ export class AddonBlockSearchForumsHandlerService extends CoreBlockBaseHandler {
*/
async getDisplayData(
block: CoreCourseBlock,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
): Promise<undefined | CoreBlockHandlerData> {
if (contextLevel !== 'course') {
if (contextLevel !== ContextLevel.COURSE) {
return;
}

View File

@ -19,6 +19,7 @@ import { CoreBlockBaseHandler } from '@features/block/classes/base-block-handler
import { CoreCourseBlock } from '@features/course/services/course';
import { makeSingleton } from '@singletons';
import { AddonCourseCompletion } from '@addons/coursecompletion/services/coursecompletion';
import { ContextLevel } from '@/core/constants';
/**
* Block handler.
@ -41,10 +42,10 @@ export class AddonBlockSelfCompletionHandlerService extends CoreBlockBaseHandler
*/
async getDisplayData(
block: CoreCourseBlock,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
): Promise<undefined | CoreBlockHandlerData> {
if (contextLevel !== 'course') {
if (contextLevel !== ContextLevel.COURSE) {
return;
}

View File

@ -30,7 +30,7 @@
<div class="flex-row ion-justify-content-between ion-align-items-center">
<h2>
<core-format-text [text]="entry.subject" [contextLevel]="contextLevel"
[contextInstanceId]="contextInstanceId" />
[contextInstanceId]="contextInstanceId" [courseId]="entry.courseid" />
</h2>
<ion-note class="ion-text-end">
{{ 'addon.blog.' + entry.publishTranslated! | translate}}
@ -47,8 +47,8 @@
<ion-card-content>
<ion-item class="ion-text-wrap">
<ion-label>
<core-format-text [text]="entry.summary" [component]="this.component" [componentId]="entry.id"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" />
<core-format-text [text]="entry.summary" [component]="component" [componentId]="entry.id"
[contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId" [courseId]="entry.courseid" />
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap" *ngIf="tagsEnabled && entry.tags && entry.tags!.length > 0">
@ -57,10 +57,9 @@
<core-tag-list [tags]="entry.tags" />
</ion-label>
</ion-item>
<core-comments *ngIf="commentsEnabled" [component]="this.component" [itemId]="entry.id" area="format_blog"
[instanceId]="entry.userid" contextLevel="user" [showItem]="true" />
<core-file *ngFor="let file of entry.attachmentfiles" [file]="file" [component]="this.component"
[componentId]="entry.id" />
<core-comments *ngIf="commentsEnabled" [component]="component" [itemId]="entry.id" area="format_blog"
[instanceId]="entry.userid" contextLevel="user" [showItem]="true" [courseId]="entry.courseid" />
<core-file *ngFor="let file of entry.attachmentfiles" [file]="file" [component]="component" [componentId]="entry.id" />
<ion-item *ngIf="entry.uniquehash" [href]="entry.uniquehash" core-link [detail]="true">
<ion-label>{{ 'addon.blog.linktooriginalentry' | translate }}</ion-label>
</ion-item>

View File

@ -273,7 +273,7 @@ export class AddonBlogEntriesPage implements OnInit {
*/
refresh(refresher?: HTMLIonRefresherElement): void {
const promises = this.entries.map((entry) =>
CoreComments.invalidateCommentsData('user', entry.userid, this.component, entry.id, 'format_blog'));
CoreComments.invalidateCommentsData(ContextLevel.USER, entry.userid, this.component, entry.id, 'format_blog'));
promises.push(AddonBlog.invalidateEntries(this.filter));
@ -304,6 +304,6 @@ export class AddonBlogEntriesPage implements OnInit {
type AddonBlogPostFormatted = AddonBlogPost & {
publishTranslated?: string; // Calculated in the app. Key of the string to translate the publish state of the post.
user?: CoreUserProfile; // Calculated in the app. Data of the user that wrote the post.
contextLevel?: string; // Calculated in the app. The context level of the entry.
contextLevel?: ContextLevel; // Calculated in the app. The context level of the entry.
contextInstanceId?: number; // Calculated in the app. The context instance id.
};

View File

@ -46,6 +46,7 @@ import { CoreReminders, CoreRemindersService, CoreRemindersUnits } from '@featur
import { CoreRemindersSetReminderMenuComponent } from '@features/reminders/components/set-reminder-menu/set-reminder-menu';
import moment from 'moment-timezone';
import { ADDON_CALENDAR_COMPONENT } from '@addons/calendar/constants';
import { ContextLevel } from '@/core/constants';
/**
* Page that displays a form to create/edit an event.
@ -266,7 +267,7 @@ export class AddonCalendarEditEventPage implements OnInit, OnDestroy, CanLeave {
const courseFillFullname = async (course: CoreCourseSearchedData | CoreEnrolledCourseData): Promise<void> => {
try {
const result = await CoreFilterHelper.getFiltersAndFormatText(course.fullname, 'course', course.id);
const result = await CoreFilterHelper.getFiltersAndFormatText(course.fullname, ContextLevel.COURSE, course.id);
course.fullname = result.text;
} catch {
// Ignore errors.

View File

@ -48,7 +48,7 @@ export class AddonCompetencyCompetenciesPage implements AfterViewInit, OnDestroy
>;
title = '';
contextLevel?: string;
contextLevel?: ContextLevel;
contextInstanceId?: number;
protected logView: () => void;

View File

@ -58,7 +58,7 @@ export class AddonCompetencyCompetencyPage implements OnInit, OnDestroy {
user?: CoreUserSummary;
competency?: AddonCompetencyDataForUserCompetencySummaryWSResponse;
userCompetency?: AddonCompetencyUserCompetencyPlan | AddonCompetencyUserCompetency | AddonCompetencyUserCompetencyCourse;
contextLevel?: string;
contextLevel?: ContextLevel;
contextInstanceId?: number;
protected logView: () => void;

View File

@ -25,6 +25,7 @@ import { AddonModAssignFeedbackDelegate } from '@addons/mod/assign/services/feed
import { AddonModAssignOffline } from '@addons/mod/assign/services/assign-offline';
import { CoreUtils } from '@services/utils/utils';
import { AddonModAssignFeedbackPluginBaseComponent } from '@addons/mod/assign/classes/base-feedback-plugin-component';
import { ContextLevel } from '@/core/constants';
/**
* Component to render a comments feedback plugin.
*/
@ -69,7 +70,7 @@ export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedb
component: this.component,
componentId: this.assign.cmid,
filter: true,
contextLevel: 'module',
contextLevel: ContextLevel.MODULE,
instanceId: this.assign.cmid,
courseId: this.assign.course,
});

View File

@ -36,6 +36,7 @@ import { CoreFormFields } from '@singletons/form';
import { CoreFileHelper } from '@services/file-helper';
import { CoreIonicColorNames } from '@singletons/colors';
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
import { ContextLevel } from '@/core/constants';
const ROOT_CACHE_KEY = 'mmaModAssign:';
@ -757,7 +758,7 @@ export class AddonModAssignProvider {
promises.push(this.invalidateAssignmentUserMappingsData(assign.id, siteId));
promises.push(this.invalidateAssignmentGradesData(assign.id, siteId));
promises.push(this.invalidateListParticipantsData(assign.id, siteId));
promises.push(CoreComments.invalidateCommentsByInstance('module', assign.id, siteId));
promises.push(CoreComments.invalidateCommentsByInstance(ContextLevel.MODULE, assign.id, siteId));
promises.push(this.invalidateAssignmentData(courseId, siteId));
promises.push(CoreGrades.invalidateAllCourseGradesData(courseId));

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { AddonModAssignSubmissionPluginBaseComponent } from '@addons/mod/assign/classes/base-submission-plugin-component';
import { Component, ViewChild } from '@angular/core';
import { CoreCommentsCommentsComponent } from '@features/comments/components/comments/comments';
@ -43,7 +44,7 @@ export class AddonModAssignSubmissionCommentsComponent extends AddonModAssignSub
*/
invalidate(): Promise<void> {
return CoreComments.invalidateCommentsData(
'module',
ContextLevel.MODULE,
this.assign.cmid,
'assignsubmission_comments',
this.submission.id,

View File

@ -19,6 +19,7 @@ import { Injectable, Type } from '@angular/core';
import { CoreComments } from '@features/comments/services/comments';
import { makeSingleton } from '@singletons';
import { AddonModAssignSubmissionCommentsComponent } from '../component/comments';
import { ContextLevel } from '@/core/constants';
/**
* Handler for comments submission plugin.
@ -68,7 +69,7 @@ export class AddonModAssignSubmissionCommentsHandlerService implements AddonModA
siteId?: string,
): Promise<void> {
await CoreComments.getComments(
'module',
ContextLevel.MODULE,
assign.cmid,
'assignsubmission_comments',
submission.id,

View File

@ -21,6 +21,7 @@ import { CoreSites } from '@services/sites';
import { CoreTextUtils } from '@services/utils/text';
import { CoreUtils } from '@services/utils/utils';
import { AddonModAssignSubmissionOnlineTextPluginData } from '../services/handler';
import { ContextLevel } from '@/core/constants';
/**
* Component to render an onlinetext submission plugin.
@ -86,7 +87,7 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
component: this.component,
componentId: this.assign.cmid,
filter: true,
contextLevel: 'module',
contextLevel: ContextLevel.MODULE,
instanceId: this.assign.cmid,
courseId: this.assign.course,
});

View File

@ -26,6 +26,7 @@ import { CoreWSFile } from '@services/ws';
import { makeSingleton } from '@singletons';
import { AddonModDataProvider, AddonModDataEntry, AddonModData, AddonModDataData } from '../data';
import { AddonModDataSync, AddonModDataSyncResult } from '../data-sync';
import { ContextLevel } from '@/core/constants';
/**
* Handler to prefetch databases.
@ -249,7 +250,7 @@ export class AddonModDataPrefetchHandlerService extends CoreCourseActivityPrefet
if (commentsEnabled && database.comments) {
promises.push(CoreComments.getComments(
'module',
ContextLevel.MODULE,
database.coursemodule,
'mod_data',
entry.id,

View File

@ -170,15 +170,25 @@ export class AddonModGlossaryIndexComponent extends CoreCourseModuleMainActivity
// Listen for offline ratings saved and synced.
this.observers.push(CoreEvents.on(CoreRatingProvider.RATING_SAVED_EVENT, (data) => {
if (this.glossary && data.component == 'mod_glossary' && data.ratingArea == 'entry' && data.contextLevel == 'module'
&& data.instanceId == this.glossary.coursemodule) {
if (
this.glossary &&
data.component == 'mod_glossary' &&
data.ratingArea == 'entry' &&
data.contextLevel == ContextLevel.MODULE &&
data.instanceId == this.glossary.coursemodule
) {
this.hasOfflineRatings = true;
this.hasOffline = true;
}
}));
this.observers.push(CoreEvents.on(CoreRatingSyncProvider.SYNCED_EVENT, (data) => {
if (this.glossary && data.component == 'mod_glossary' && data.ratingArea == 'entry' && data.contextLevel == 'module'
&& data.instanceId == this.glossary.coursemodule) {
if (
this.glossary &&
data.component == 'mod_glossary' &&
data.ratingArea == 'entry' &&
data.contextLevel == ContextLevel.MODULE &&
data.instanceId == this.glossary.coursemodule
) {
this.hasOfflineRatings = false;
this.hasOffline = this.hasOfflineEntries;
}

View File

@ -25,6 +25,7 @@ import { CoreWSFile } from '@services/ws';
import { makeSingleton } from '@singletons';
import { AddonModGlossary, AddonModGlossaryEntry, AddonModGlossaryGlossary, AddonModGlossaryProvider } from '../glossary';
import { AddonModGlossarySync, AddonModGlossarySyncResult } from '../glossary-sync';
import { ContextLevel } from '@/core/constants';
/**
* Handler to prefetch forums.
@ -161,7 +162,7 @@ export class AddonModGlossaryPrefetchHandlerService extends CoreCourseActivityPr
// Don't fetch individual entries, it's too many WS calls.
if (glossary.allowcomments && commentsEnabled) {
promises.push(CoreComments.getComments(
'module',
ContextLevel.MODULE,
glossary.coursemodule,
'mod_glossary',
entry.id,

View File

@ -19,6 +19,7 @@ import { CoreMimetypeUtils } from '@services/utils/mimetype';
import { CoreWSExternalWarning } from '@services/ws';
import { CoreSite } from '@classes/sites/site';
import { makeSingleton } from '@singletons';
import { ContextLevel } from '@/core/constants';
const ROOT_CACHE_KEY = 'mmaFiles:';
@ -136,7 +137,7 @@ export class AddonPrivateFilesProvider {
contextid: -1,
component: 'user',
filearea: 'private',
contextlevel: 'user',
contextlevel: ContextLevel.USER,
instanceid: CoreSites.getCurrentSite()?.getUserId(),
itemid: 0,
filepath: '',
@ -428,7 +429,7 @@ export type AddonPrivateFilesGetFilesWSParams = {
filepath: string; // File path.
filename: string; // File name.
modified?: number; // Timestamp to return files changed after this time.
contextlevel?: string; // The context level for the file location.
contextlevel?: ContextLevel; // The context level for the file location.
instanceid?: number; // The instance id for where the file is located.
};

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { CoreQuestionBehaviourButton, CoreQuestionQuestion } from '@features/question/services/question-helper';
@ -30,7 +31,7 @@ export class AddonQbehaviourDeferredCBMComponent {
@Input() componentId?: number; // ID of the component the question belongs to.
@Input() attemptId?: number; // Attempt ID.
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the question belongs to (if any). It can be used to improve performance with filters.
@Input() review?: boolean; // Whether the user is in review mode.

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, Input, Output, EventEmitter } from '@angular/core';
import { CoreQuestionBehaviourButton, CoreQuestionQuestion } from '@features/question/services/question-helper';
@ -30,7 +31,7 @@ export class AddonQbehaviourInformationItemComponent {
@Input() componentId?: number; // ID of the component the question belongs to.
@Input() attemptId?: number; // Attempt ID.
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the question belongs to (if any). It can be used to improve performance with filters.
@Input() review?: boolean; // Whether the user is in review mode.

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, Input, OnDestroy, OnInit, ElementRef, OnChanges, ViewChild, SimpleChange } from '@angular/core';
import { CoreFilter } from '@features/filter/services/filter';
import { CoreFilterHelper } from '@features/filter/services/filter-helper';
@ -50,7 +51,7 @@ export class CoreChartComponent implements OnDestroy, OnInit, OnChanges {
@Input() legend?: ChartLegendOptions; // 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() contextLevel?: ContextLevel; // The context level of the text.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
@Input() wsNotFiltered?: boolean | string; // If true it means the WS didn't filter the labels for some reason.

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChange } from '@angular/core';
import { Translate } from '@singletons';
@ -36,7 +37,7 @@ export class CoreNavigationBarComponent implements OnChanges {
@Input() nextTranslate = 'core.next'; // Next translatable text, can admit $a variable.
@Input() component?: string; // Component the bar belongs to.
@Input() componentId?: number; // Component ID.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.

View File

@ -55,6 +55,7 @@ import { MediaElementController } from '@classes/element-controllers/MediaElemen
import { FrameElement, FrameElementController } from '@classes/element-controllers/FrameElementController';
import { CoreUrl } from '@singletons/url';
import { CoreIcons } from '@singletons/icons';
import { ContextLevel } from '../constants';
/**
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
@ -81,7 +82,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
@Input() singleLine?: boolean | string; // Whether new lines should be removed (all text in single line). Only if clean=true.
@Input() highlight?: string; // Text to highlight.
@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() contextLevel?: ContextLevel; // The context level of the text.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
@Input() wsNotFiltered?: boolean | string; // If true it means the WS didn't filter the text for some reason.
@ -397,11 +398,13 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
const siteId = site?.getId();
if (site && this.contextLevel == 'course' && this.contextInstanceId !== undefined && this.contextInstanceId <= 0) {
if (
site && this.contextLevel === ContextLevel.COURSE && this.contextInstanceId !== undefined && this.contextInstanceId <= 0
) {
this.contextInstanceId = site.getSiteHomeId();
}
if (this.contextLevel === 'course' && this.contextInstanceId === undefined && this.courseId !== undefined) {
if (this.contextLevel === ContextLevel.COURSE && this.contextInstanceId === undefined && this.courseId !== undefined) {
this.contextInstanceId = this.courseId;
}
@ -422,7 +425,7 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
if (filter) {
const filterResult = await CoreFilterHelper.getFiltersAndFormatText(
this.text || '',
this.contextLevel || '',
this.contextLevel || ContextLevel.SYSTEM,
this.contextInstanceId ?? -1,
options,
siteId,

View File

@ -26,6 +26,7 @@ import { CoreSites } from '@services/sites';
import { CoreUtils } from '@services/utils/utils';
import { mock, mockSingleton, RenderConfig, renderTemplate, renderWrapperComponent } from '@/testing/utils';
import { ContextLevel } from '@/core/constants';
describe('CoreFormatTextDirective', () => {
@ -120,7 +121,7 @@ describe('CoreFormatTextDirective', () => {
expect(CoreFilterHelper.getFiltersAndFormatText).toHaveBeenCalledTimes(1);
expect(CoreFilterHelper.getFiltersAndFormatText).toHaveBeenCalledWith(
'Lorem ipsum dolor',
'course',
ContextLevel.COURSE,
42,
expect.anything(),
undefined,

View File

@ -14,6 +14,7 @@
import { CoreCourseBlock } from '@features/course/services/course';
import { CoreBlockHandler, CoreBlockHandlerData } from '../services/block-delegate';
import { ContextLevel } from '@/core/constants';
/**
* Base handler for blocks.
@ -45,7 +46,7 @@ export class CoreBlockBaseHandler implements CoreBlockHandler {
*/
getDisplayData(
block: CoreCourseBlock, // eslint-disable-line @typescript-eslint/no-unused-vars
contextLevel: string, // eslint-disable-line @typescript-eslint/no-unused-vars
contextLevel: ContextLevel, // eslint-disable-line @typescript-eslint/no-unused-vars
instanceId: number, // eslint-disable-line @typescript-eslint/no-unused-vars
): undefined | CoreBlockHandlerData | Promise<undefined | CoreBlockHandlerData> {
// To be overridden.

View File

@ -18,6 +18,7 @@ import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-comp
import { Subscription } from 'rxjs';
import { CoreCourseBlock } from '@/core/features/course/services/course';
import type { ICoreBlockComponent } from '@features/block/classes/base-block-component';
import { ContextLevel } from '@/core/constants';
/**
* Component to render a block.
@ -32,7 +33,7 @@ export class CoreBlockComponent implements OnChanges, OnDestroy {
@ViewChild(CoreDynamicComponent) dynamicComponent?: CoreDynamicComponent<ICoreBlockComponent>;
@Input() block!: CoreCourseBlock; // The block to render.
@Input() contextLevel!: string; // The context where the block will be used.
@Input() contextLevel!: ContextLevel; // The context where the block will be used.
@Input() instanceId!: number; // The instance ID associated with the context level.
@Input() extraData!: Record<string, unknown>; // Any extra data to be passed to the block.
@Input() labelledBy?: string;

View File

@ -14,6 +14,7 @@
import { OnInit, Component, HostBinding } from '@angular/core';
import { CoreBlockBaseComponent } from '../../classes/base-block-component';
import { ContextLevel } from '@/core/constants';
/**
* Component to render blocks with pre-rendered HTML.
@ -36,13 +37,11 @@ export class CoreBlockPreRenderedComponent extends CoreBlockBaseComponent implem
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
await super.ngOnInit();
this.courseId = this.contextLevel == 'course' ? this.instanceId : undefined;
this.courseId = this.contextLevel === ContextLevel.COURSE ? this.instanceId : undefined;
this.fetchContentDefaultError = 'Error getting ' + this.block.contents?.title + ' data.';
this.id = `block-${this.block.instanceid}`;
await super.ngOnInit();
}
}

View File

@ -20,6 +20,7 @@ import { CoreDomUtils } from '@services/utils/dom';
import { CoreDom } from '@singletons/dom';
import { CoreBlockSideBlocksTourComponent } from '../side-blocks-tour/side-blocks-tour';
import { CoreBlockSideBlocksComponent } from '../side-blocks/side-blocks';
import { ContextLevel } from '@/core/constants';
/**
* Component that displays a button to open blocks.
@ -31,7 +32,7 @@ import { CoreBlockSideBlocksComponent } from '../side-blocks/side-blocks';
})
export class CoreBlockSideBlocksButtonComponent implements OnInit, OnDestroy {
@Input() contextLevel!: string;
@Input() contextLevel!: ContextLevel;
@Input() instanceId!: number;
@Input() myDashboardPage?: string;

View File

@ -22,6 +22,7 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreCoursesDashboard } from '@features/courses/services/dashboard';
import { CoreTextUtils } from '@services/utils/text';
import { CoreDom } from '@singletons/dom';
import { ContextLevel } from '@/core/constants';
/**
* Component that displays the list of side blocks.
@ -33,7 +34,7 @@ import { CoreDom } from '@singletons/dom';
})
export class CoreBlockSideBlocksComponent implements OnInit {
@Input() contextLevel!: string;
@Input() contextLevel!: ContextLevel;
@Input() instanceId!: number;
@Input() initialBlockInstanceId?: number;
@Input() myDashboardPage?: string;
@ -64,7 +65,7 @@ export class CoreBlockSideBlocksComponent implements OnInit {
async invalidateBlocks(): Promise<void> {
const promises: Promise<void>[] = [];
if (this.contextLevel === 'course') {
if (this.contextLevel === ContextLevel.COURSE) {
promises.push(CoreCourse.invalidateCourseBlocks(this.instanceId));
} else {
promises.push(CoreCoursesDashboard.invalidateDashboardBlocks());
@ -87,7 +88,7 @@ export class CoreBlockSideBlocksComponent implements OnInit {
*/
async loadContent(): Promise<void> {
try {
if (this.contextLevel === 'course') {
if (this.contextLevel === ContextLevel.COURSE) {
this.blocks = await CoreBlockHelper.getCourseBlocks(this.instanceId);
} else {
const blocks = await CoreCoursesDashboard.getDashboardBlocks(undefined, undefined, this.myDashboardPage);

View File

@ -23,6 +23,7 @@ import { makeSingleton } from '@singletons';
import { CoreBlockDefaultHandler } from './handlers/default-block';
import { CoreNavigationOptions } from '@services/navigator';
import type { ICoreBlockComponent } from '@features/block/classes/base-block-component';
import { ContextLevel } from '@/core/constants';
/**
* Interface that all blocks must implement.
@ -43,7 +44,7 @@ export interface CoreBlockHandler extends CoreDelegateHandler {
*/
getDisplayData?(
block: CoreCourseBlock,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
): undefined | CoreBlockHandlerData | Promise<undefined | CoreBlockHandlerData>;
}
@ -162,7 +163,7 @@ export class CoreBlockDelegateService extends CoreDelegate<CoreBlockHandler> {
*/
async getBlockDisplayData(
block: CoreCourseBlock,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
): Promise<CoreBlockHandlerData | undefined> {
return this.executeFunctionOnEnabled(

View File

@ -17,6 +17,7 @@ import { CoreSites } from '@services/sites';
import { CoreTimeUtils } from '@services/utils/time';
import { makeSingleton } from '@singletons';
import { COMMENTS_TABLE, COMMENTS_DELETED_TABLE, CoreCommentsDBRecord, CoreCommentsDeletedDBRecord } from './database/comments';
import { ContextLevel } from '@/core/constants';
/**
* Service to handle offline comments.
@ -52,7 +53,7 @@ export class CoreCommentsOfflineProvider {
* @returns Promise resolved with the comments.
*/
async getComment(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -86,7 +87,7 @@ export class CoreCommentsOfflineProvider {
* @returns Promise resolved with the comments.
*/
async getComments(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -131,7 +132,7 @@ export class CoreCommentsOfflineProvider {
* @returns Promise resolved with the comments.
*/
async getDeletedComments(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -165,7 +166,7 @@ export class CoreCommentsOfflineProvider {
* @returns Promise resolved if deleted, rejected if failure.
*/
async removeComment(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -195,7 +196,7 @@ export class CoreCommentsOfflineProvider {
* @returns Promise resolved if deleted, rejected if failure.
*/
async removeDeletedComments(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -227,7 +228,7 @@ export class CoreCommentsOfflineProvider {
*/
async saveComment(
content: string,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -265,7 +266,7 @@ export class CoreCommentsOfflineProvider {
*/
async deleteComment(
commentId: number,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,

View File

@ -24,6 +24,7 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreNetworkError } from '@classes/errors/network-error';
import { CoreCommentsDBRecord, CoreCommentsDeletedDBRecord } from './database/comments';
import { CoreSyncResult } from '@services/sync';
import { ContextLevel } from '@/core/constants';
/**
* Service to sync omments.
@ -121,7 +122,7 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider<CoreCommentsS
* @returns Promise resolved when the comments are synced or if they don't need to be synced.
*/
private async syncCommentsIfNeeded(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -149,7 +150,7 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider<CoreCommentsS
* @returns Promise resolved if sync is successful, rejected otherwise.
*/
syncComments(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -185,7 +186,7 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider<CoreCommentsS
* @returns Promise resolved if sync is successful, rejected otherwise.
*/
private async performSyncComments(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -312,7 +313,13 @@ export class CoreCommentsSyncProvider extends CoreSyncBaseProvider<CoreCommentsS
* @param area String comment area. Default empty.
* @returns Sync ID.
*/
protected getSyncId(contextLevel: string, instanceId: number, component: string, itemId: number, area: string = ''): string {
protected getSyncId(
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
area: string = '',
): string {
return contextLevel + '#' + instanceId + '#' + component + '#' + itemId + '#' + area;
}
@ -325,7 +332,7 @@ export type CoreCommentsSyncResult = CoreSyncResult;
* Data passed to AUTO_SYNCED event.
*/
export type CoreCommentsSyncAutoSyncData = {
contextLevel: string;
contextLevel: ContextLevel;
instanceId: number;
componentName: string;
itemId: number;

View File

@ -24,6 +24,7 @@ import { CoreEvents } from '@singletons/events';
import { CoreCommentsOffline } from './comments-offline';
import { CoreCommentsSyncAutoSyncData, CoreCommentsSyncProvider } from './comments-sync';
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
import { ContextLevel } from '@/core/constants';
const ROOT_CACHE_KEY = 'mmComments:';
@ -80,7 +81,7 @@ export class CoreCommentsProvider {
*/
async addComment(
content: string,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -128,7 +129,7 @@ export class CoreCommentsProvider {
*/
async addCommentOnline(
content: string,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -318,7 +319,7 @@ export class CoreCommentsProvider {
*/
async deleteCommentsOnline(
commentIds: number[],
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -367,7 +368,7 @@ export class CoreCommentsProvider {
* @returns Cache key.
*/
protected getCommentsCacheKey(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -383,7 +384,7 @@ export class CoreCommentsProvider {
* @param instanceId The Instance id of item associated with the context level.
* @returns Cache key.
*/
protected getCommentsPrefixCacheKey(contextLevel: string, instanceId: number): string {
protected getCommentsPrefixCacheKey(contextLevel: ContextLevel, instanceId: number): string {
return ROOT_CACHE_KEY + 'comments:' + contextLevel + ':' + instanceId;
}
@ -400,7 +401,7 @@ export class CoreCommentsProvider {
* @returns Promise resolved with the comments.
*/
async getComments(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -449,7 +450,7 @@ export class CoreCommentsProvider {
* @returns Comments count with plus sign if needed.
*/
async getCommentsCount(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -514,7 +515,7 @@ export class CoreCommentsProvider {
* @returns Promise resolved when the data is invalidated.
*/
async invalidateCommentsData(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
component: string,
itemId: number,
@ -545,7 +546,7 @@ export class CoreCommentsProvider {
* @param siteId Site ID. If not defined, current site.
* @returns Promise resolved when the data is invalidated.
*/
async invalidateCommentsByInstance(contextLevel: string, instanceId: number, siteId?: string): Promise<void> {
async invalidateCommentsByInstance(contextLevel: ContextLevel, instanceId: number, siteId?: string): Promise<void> {
const site = await CoreSites.getSite(siteId);
await site.invalidateWsCacheForKeyStartingWith(this.getCommentsPrefixCacheKey(contextLevel, instanceId));
@ -587,7 +588,7 @@ type CoreCommentsAddCommentsWSParams = {
export type CoreCommentsCommentBasicData = {
id?: number; // Comment ID.
contextlevel: string; // Contextlevel system, course, user...
contextlevel: ContextLevel; // Contextlevel system, course, user...
instanceid: number; // The id of item associated with the contextlevel.
component: string; // Component.
content: string; // Component.
@ -628,7 +629,7 @@ type CoreCommentsDeleteCommentsWSParams = {
* Params of core_comment_get_comments WS.
*/
type CoreCommentsGetCommentsWSParams = {
contextlevel: string; // Contextlevel system, course, user...
contextlevel: ContextLevel; // Contextlevel system, course, user...
instanceid: number; // The Instance id of item associated with the context level.
component: string; // Component.
itemid: number; // Associated id.
@ -652,7 +653,7 @@ export type CoreCommentsGetCommentsWSResponse = {
* Data sent by COMMENTS_COUNT_CHANGED_EVENT event.
*/
export type CoreCommentsCountChangedEventData = {
contextLevel: string;
contextLevel: ContextLevel;
instanceId: number;
component: string;
itemId: number;
@ -664,7 +665,7 @@ export type CoreCommentsCountChangedEventData = {
* Data sent by REFRESH_COMMENTS_EVENT event.
*/
export type CoreCommentsRefreshCommentsEventData = {
contextLevel?: string;
contextLevel?: ContextLevel;
instanceId?: number;
component?: string;
itemId?: number;

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { CoreSiteSchema } from '@services/sites';
/**
@ -95,7 +96,7 @@ export const COMMENTS_OFFLINE_SITE_SCHEMA: CoreSiteSchema = {
};
export type CoreCommentsDBRecord = {
contextlevel: string; // Primary key.
contextlevel: ContextLevel; // Primary key.
instanceid: number; // Primary key.
component: string; // Primary key.
itemid: number; // Primary key.
@ -106,7 +107,7 @@ export type CoreCommentsDBRecord = {
export type CoreCommentsDeletedDBRecord = {
commentid: number; // Primary key.
contextlevel: string;
contextlevel: ContextLevel;
instanceid: number;
component: string;
itemid: number;

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { DownloadStatus } from '@/core/constants';
import { DownloadStatus, ContextLevel } from '@/core/constants';
import { CoreNetworkError } from '@classes/errors/network-error';
import { CoreFilterHelper } from '@features/filter/services/filter-helper';
import { CoreNetwork } from '@services/network';
@ -118,7 +118,7 @@ export class CoreCourseActivityPrefetchHandlerBase extends CoreCourseModulePrefe
await Promise.all([
CoreCourse.getModuleBasicInfo(module.id, { siteId }),
CoreCourse.getModule(module.id, courseId, undefined, false, true, siteId),
CoreFilterHelper.getFilters('module', module.id, { courseId }),
CoreFilterHelper.getFilters(ContextLevel.MODULE, module.id, { courseId }),
]);
// Call the download function.

View File

@ -21,6 +21,7 @@ import { CoreWSFile } from '@services/ws';
import { CoreCourse, CoreCourseAnyModuleData } from '../services/course';
import { CoreCourseModuleData } from '../services/course-helper';
import { CoreCourseModulePrefetchHandlerBase } from './module-prefetch-handler';
import { ContextLevel } from '@/core/constants';
/**
* Base prefetch handler to be registered in CoreCourseModulePrefetchDelegate. It is useful to minimize the amount of
@ -131,7 +132,7 @@ export class CoreCourseResourcePrefetchHandlerBase extends CoreCourseModulePrefe
));
}
promises.push(CoreFilterHelper.getFilters('module', module.id, { courseId }));
promises.push(CoreFilterHelper.getFilters(ContextLevel.MODULE, module.id, { courseId }));
await Promise.all(promises);
}

View File

@ -52,6 +52,7 @@ import { CoreDom } from '@singletons/dom';
import { CoreUserTourDirectiveOptions } from '@directives/user-tour';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreBlockSideBlocksComponent } from '@features/block/components/side-blocks/side-blocks';
import { ContextLevel } from '@/core/constants';
/**
* Component to display course contents using a certain format. If the format isn't found, use default one.
@ -318,7 +319,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
CoreDomUtils.openSideModal({
component: CoreBlockSideBlocksComponent,
componentProps: {
contextLevel: 'course',
contextLevel: ContextLevel.COURSE,
instanceId: this.course.id,
initialBlockInstanceId: this.initialBlockInstanceId,
},

View File

@ -25,6 +25,7 @@ import { CoreCourseModuleCompletionBaseComponent } from '@features/course/classe
import { CoreCourseHelper } from '@features/course/services/course-helper';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { BehaviorSubject } from 'rxjs';
import { ContextLevel } from '@/core/constants';
/**
* Component to handle activity completion in sites previous to 3.11.
@ -112,7 +113,7 @@ export class CoreCourseModuleCompletionLegacyComponent extends CoreCourseModuleC
const result = await CoreFilterHelper.getFiltersAndFormatText(
moduleName,
'module',
ContextLevel.MODULE,
this.moduleId,
{ clean: true, singleLine: true, shortenLength: 50, courseId: this.completion.courseId },
);

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, HostBinding, Input } from '@angular/core';
/**
@ -43,7 +44,7 @@ export class CoreCourseModuleDescriptionComponent {
@Input() component?: string; // Component for format text directive.
@Input() componentId?: string | number; // Component ID to use in conjunction with the component.
@Input() showFull?: string | boolean; // Whether to always display the full description.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.

View File

@ -28,7 +28,7 @@ import {
CoreCourseModuleCompletionStatus,
CoreCourseGetContentsWSModule,
} from './course';
import { CoreConstants, DownloadStatus, TDownloadStatus } from '@/core/constants';
import { CoreConstants, DownloadStatus, TDownloadStatus, ContextLevel } from '@/core/constants';
import { CoreLogger } from '@singletons/logger';
import { ApplicationInit, makeSingleton, Translate } from '@singletons';
import { CoreFilepool } from '@services/filepool';
@ -1636,7 +1636,7 @@ export class CoreCourseHelperProvider {
promises.push(CoreCourse.getActivitiesCompletionStatus(course.id));
}
promises.push(CoreFilterHelper.getFilters('course', course.id));
promises.push(CoreFilterHelper.getFilters(ContextLevel.COURSE, course.id));
await CoreUtils.allPromises(promises);

View File

@ -25,7 +25,7 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreCourse, CoreCourseAnyModuleData, CoreCourseModuleContentFile } from './course';
import { CoreCache } from '@classes/cache';
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
import { DownloadStatus, TDownloadStatus, TDownloadedStatus } from '@/core/constants';
import { DownloadStatus, TDownloadStatus, TDownloadedStatus, ContextLevel } from '@/core/constants';
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
import { makeSingleton } from '@singletons';
import { CoreEvents, CoreEventSectionStatusChangedData } from '@singletons/events';
@ -132,7 +132,7 @@ export class CoreCourseModulePrefetchDelegateService extends CoreDelegate<CoreCo
if (canUse) {
// Can use check updates, add it to the tocheck list.
result.toCheck.push({
contextlevel: 'module',
contextlevel: ContextLevel.MODULE,
id: module.id,
since: data.downloadTime || 0,
});
@ -888,7 +888,7 @@ export class CoreCourseModulePrefetchDelegateService extends CoreDelegate<CoreCo
courseid: courseId,
tocheck: [
{
contextlevel: 'module',
contextlevel: ContextLevel.MODULE,
id: module.id,
since: data.downloadTime || 0,
},
@ -1611,7 +1611,7 @@ export type CoreCourseCheckUpdatesWSParams = {
* Data to send in tocheck parameter.
*/
type CheckUpdatesToCheckWSParam = {
contextlevel: string; // The context level for the file location. Only module supported right now.
contextlevel: ContextLevel.MODULE; // The context level for the file location. Only module supported right now.
id: number; // Context instance id.
since: number; // Check updates since this time stamp.
};

View File

@ -44,6 +44,7 @@ import { CoreDom } from '@singletons/dom';
import { CorePlatform } from '@services/platform';
import { Swiper } from 'swiper';
import { SwiperOptions } from 'swiper/types';
import { ContextLevel } from '@/core/constants';
/**
* Component to display a rich text editor if enabled.
@ -71,7 +72,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
@Input() component?: string; // The component to link the files to.
@Input() componentId?: number; // An ID to use in conjunction with the component.
@Input() autoSave?: boolean | string; // Whether to auto-save the contents in a draft. Defaults to true.
@Input() contextLevel?: string; // The context level of the text.
@Input() contextLevel?: ContextLevel; // The context level of the text.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() elementId?: string; // An ID to set to the element.
@Input() draftExtraParams?: Record<string, unknown>; // Extra params to identify the draft.
@ -267,7 +268,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
// Save a draft so the original content is saved.
this.lastDraft = newValue ?? '';
CoreEditorOffline.saveDraft(
this.contextLevel || '',
this.contextLevel || ContextLevel.SYSTEM,
this.contextInstanceId || 0,
this.elementId || '',
this.draftExtraParams || {},
@ -923,7 +924,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
protected async restoreDraft(): Promise<void> {
try {
const entry = await CoreEditorOffline.resumeDraft(
this.contextLevel || '',
this.contextLevel || ContextLevel.SYSTEM,
this.contextInstanceId || 0,
this.elementId || '',
this.draftExtraParams || {},
@ -983,7 +984,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
try {
await CoreEditorOffline.saveDraft(
this.contextLevel || '',
this.contextLevel || ContextLevel.SYSTEM,
this.contextInstanceId || 0,
this.elementId || '',
this.draftExtraParams || {},
@ -1011,7 +1012,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
if (data.form && form && data.form == form) {
try {
await CoreEditorOffline.deleteDraft(
this.contextLevel || '',
this.contextLevel || ContextLevel.SYSTEM,
this.contextInstanceId || 0,
this.elementId || '',
this.draftExtraParams || {},

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { CoreSiteSchema } from '@services/sites';
/**
@ -75,7 +76,7 @@ export const SITE_SCHEMA: CoreSiteSchema = {
* Primary data to identify a stored draft.
*/
export type CoreEditorDraftPrimaryData = {
contextlevel: string; // Context level.
contextlevel: ContextLevel; // Context level.
contextinstanceid: number; // The instance ID related to the context.
elementid: string; // Element ID.
extraparams: string; // Extra params stringified.

View File

@ -20,6 +20,7 @@ import { CoreUtils } from '@services/utils/utils';
import { makeSingleton } from '@singletons';
import { CoreLogger } from '@singletons/logger';
import { CoreEditorDraft, CoreEditorDraftPrimaryData, DRAFT_TABLE } from './database/editor';
import { ContextLevel } from '@/core/constants';
/**
* Service with features regarding rich text editor in offline.
@ -44,7 +45,7 @@ export class CoreEditorOfflineProvider {
* @returns Promise resolved when done.
*/
async deleteDraft(
contextLevel: string,
contextLevel: ContextLevel,
contextInstanceId: number,
elementId: string,
extraParams: Record<string, unknown>,
@ -71,7 +72,7 @@ export class CoreEditorOfflineProvider {
* @returns Object with the fixed primary data.
*/
protected fixDraftPrimaryData(
contextLevel: string,
contextLevel: ContextLevel,
contextInstanceId: number,
elementId: string,
extraParams: Record<string, unknown>,
@ -96,7 +97,7 @@ export class CoreEditorOfflineProvider {
* @returns Promise resolved with the draft data. Undefined if no draft stored.
*/
async getDraft(
contextLevel: string,
contextLevel: ContextLevel,
contextInstanceId: number,
elementId: string,
extraParams: Record<string, unknown>,
@ -123,7 +124,7 @@ export class CoreEditorOfflineProvider {
* @returns Promise resolved with the draft data. Undefined if no draft stored.
*/
async resumeDraft(
contextLevel: string,
contextLevel: ContextLevel,
contextInstanceId: number,
elementId: string,
extraParams: Record<string, unknown>,
@ -183,7 +184,7 @@ export class CoreEditorOfflineProvider {
* @returns Promise resolved when done.
*/
async saveDraft(
contextLevel: string,
contextLevel: ContextLevel,
contextInstanceId: number,
elementId: string,
extraParams: Record<string, unknown>,

View File

@ -15,11 +15,12 @@
import { Injectable, ViewContainerRef } from '@angular/core';
import { CoreSites } from '@services/sites';
import { CoreFilter, CoreFilterFilter, CoreFilterFormatTextOptions } from './filter';
import { CoreFilter, CoreFilterFilter, CoreFilterFormatTextOptions, CoreFilterStateValue } from './filter';
import { CoreFilterDefaultHandler } from './handlers/default-filter';
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
import { CoreSite } from '@classes/sites/site';
import { makeSingleton } from '@singletons';
import { ContextLevel } from '@/core/constants';
/**
* Interface that all filter handlers must implement.
@ -156,7 +157,7 @@ export class CoreFilterDelegateService extends CoreDelegate<CoreFilterHandler> {
* @param instanceId Instance ID.
* @returns Filters.
*/
getEnabledFilters(contextLevel: string, instanceId: number): CoreFilterFilter[] {
getEnabledFilters(contextLevel: ContextLevel, instanceId: number): CoreFilterFilter[] {
const filters: CoreFilterFilter[] = [];
for (const name in this.enabledHandlers) {
@ -168,7 +169,7 @@ export class CoreFilterDelegateService extends CoreDelegate<CoreFilterHandler> {
filter: handler.filterName,
inheritedstate: 1,
instanceid: instanceId,
localstate: 1,
localstate: CoreFilterStateValue.ON,
});
}
@ -244,7 +245,10 @@ export class CoreFilterDelegateService extends CoreDelegate<CoreFilterHandler> {
skipFilters?: string[],
): boolean {
if (filter.localstate == -1 || (filter.localstate == 0 && filter.inheritedstate == -1)) {
if (
filter.localstate === CoreFilterStateValue.OFF ||
(filter.localstate === CoreFilterStateValue.INHERIT && filter.inheritedstate === CoreFilterStateValue.OFF)
) {
// Filter is disabled, ignore it.
return false;
}
@ -254,7 +258,7 @@ export class CoreFilterDelegateService extends CoreDelegate<CoreFilterHandler> {
return false;
}
if (skipFilters && skipFilters.indexOf(filter.filter) != -1) {
if (skipFilters && skipFilters.indexOf(filter.filter) !== -1) {
// Skip this filter.
return false;
}

View File

@ -23,6 +23,8 @@ import {
CoreFilterFormatTextOptions,
CoreFilterClassifiedFilters,
CoreFiltersGetAvailableInContextWSParamContext,
CoreFilterStateValue,
CoreFilterAllStates,
} from './filter';
import { CoreCourse } from '@features/course/services/course';
import { CoreCourses } from '@features/courses/services/courses';
@ -32,7 +34,7 @@ import { CoreLogger } from '@singletons/logger';
import { CoreSite } from '@classes/sites/site';
import { CoreCourseHelper } from '@features/course/services/course-helper';
import { firstValueFrom } from 'rxjs';
import { CoreBlockHelper } from '@features/block/services/block-helper';
import { ContextLevel } from '@/core/constants';
/**
* Helper service to provide filter functionalities.
@ -69,32 +71,6 @@ export class CoreFilterHelperProvider {
});
}
/**
* Get the contexts of all blocks in a course.
*
* @param courseId Course ID.
* @param siteId Site ID. If not defined, current site.
* @returns Promise resolved with the contexts.
*/
async getBlocksContexts(courseId: number, siteId?: string): Promise<CoreFiltersGetAvailableInContextWSParamContext[]> {
// Use stale while revalidate, but always use the first value. If data is updated it will be stored in DB.
const blocks = await firstValueFrom(CoreCourse.getCourseBlocksObservable(courseId, {
readingStrategy: CoreSitesReadingStrategy.STALE_WHILE_REVALIDATE,
siteId,
}));
const contexts: CoreFiltersGetAvailableInContextWSParamContext[] = [];
blocks.forEach((block) => {
contexts.push({
contextlevel: 'block',
instanceid: block.instanceid,
});
});
return contexts;
}
/**
* Get some filters from memory cache. If not in cache, get them and store them in cache.
*
@ -106,7 +82,7 @@ export class CoreFilterHelperProvider {
* @returns Promise resolved with the filters.
*/
protected async getCacheableFilters(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
getFilters: () => Promise<CoreFiltersGetAvailableInContextWSParamContext[]>,
options: CoreFilterFormatTextOptions,
@ -144,7 +120,7 @@ export class CoreFilterHelperProvider {
courseIds.forEach((courseId) => {
contexts.push({
contextlevel: 'course',
contextlevel: ContextLevel.COURSE,
instanceid: courseId,
});
});
@ -174,7 +150,7 @@ export class CoreFilterHelperProvider {
section.modules.forEach((module) => {
if (CoreCourseHelper.canUserViewModule(module, section)) {
contexts.push({
contextlevel: 'module',
contextlevel: ContextLevel.MODULE,
instanceid: module.id,
});
}
@ -197,12 +173,16 @@ export class CoreFilterHelperProvider {
* @returns Promise resolved with the filters.
*/
async getFilters(
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
options?: CoreFilterFormatTextOptions,
options: CoreFilterFormatTextOptions = {},
siteId?: string,
): Promise<CoreFilterFilter[]> {
options = options || {};
// Check the right context to use.
const newContext = CoreFilter.getEffectiveContext(contextLevel, instanceId, { courseId: options.courseId });
contextLevel = newContext.contextLevel;
instanceId = newContext.instanceId;
options.contextLevel = contextLevel;
options.instanceId = instanceId;
options.filter = false;
@ -220,10 +200,18 @@ export class CoreFilterHelperProvider {
return await CoreFilterDelegate.getEnabledFilters(contextLevel, instanceId);
}
const filters = await this.getFiltersInContextUsingAllStates(contextLevel, instanceId, options, site);
if (filters) {
return filters;
}
const courseId = options.courseId;
let hasFilters = true;
if (contextLevel == 'system' || (contextLevel == 'course' && instanceId == site.getSiteHomeId())) {
if (
contextLevel === ContextLevel.SYSTEM ||
(contextLevel === ContextLevel.COURSE && instanceId == site.getSiteHomeId())
) {
// No need to check the site filters because we're requesting the same context, so we'd do the same twice.
} else {
// Check if site has any filter to treat.
@ -236,21 +224,16 @@ export class CoreFilterHelperProvider {
options.filter = true;
if (contextLevel == 'module' && courseId) {
if (contextLevel === ContextLevel.MODULE && courseId) {
// Get all the modules filters with a single call to decrease the number of WS calls.
const getFilters = () => this.getCourseModulesContexts(courseId, siteId);
return await this.getCacheableFilters(contextLevel, instanceId, getFilters, options, site);
} else if (contextLevel == 'course') {
} else if (contextLevel === ContextLevel.COURSE) {
// If enrolled, get all enrolled courses filters with a single call to decrease number of WS calls.
const getFilters = () => this.getCourseContexts(instanceId, siteId);
return await this.getCacheableFilters(contextLevel, instanceId, getFilters, options, site);
} else if (contextLevel == 'block' && courseId && CoreBlockHelper.canGetCourseBlocks(site)) {
// Get all the course blocks filters with a single call to decrease number of WS calls.
const getFilters = () => this.getBlocksContexts(courseId, siteId);
return await this.getCacheableFilters(contextLevel, instanceId, getFilters, options, site);
}
@ -262,6 +245,95 @@ export class CoreFilterHelperProvider {
}
}
/**
* Get filters in context using the all states data.
*
* @param contextLevel The context level.
* @param instanceId Instance ID related to the context.
* @param options Options.
* @param site Site.
* @returns Filters, undefined if all states cannot be used.
*/
protected async getFiltersInContextUsingAllStates(
contextLevel: ContextLevel,
instanceId: number,
options: CoreFilterFormatTextOptions = {},
site?: CoreSite,
): Promise<CoreFilterFilter[] | undefined> {
site = site || CoreSites.getCurrentSite();
if (!CoreFilter.canGetAllStatesInSite(site)) {
return;
}
const allStates = await CoreFilter.getAllStates({ siteId: site?.getId() });
if (
contextLevel !== ContextLevel.SYSTEM &&
contextLevel !== ContextLevel.COURSECAT &&
this.hasCategoryOverride(allStates)
) {
// A category has an override, we cannot calculate the right filters for child contexts.
return;
}
const contexts = CoreFilter.getContextsTreeList(contextLevel, instanceId, { courseId: options.courseId });
const contextId = Object.values(allStates[contextLevel]?.[instanceId] ?? {})[0]?.contextid;
const filters: Record<string, CoreFilterFilter> = {};
contexts.reverse().forEach((context) => {
const isParentContext = context.contextLevel !== contextLevel;
const filtersInContext = allStates[context.contextLevel]?.[context.instanceId];
if (!filtersInContext) {
return;
}
for (const name in filtersInContext) {
const filterInContext = filtersInContext[name];
if (filterInContext.localstate === CoreFilterStateValue.DISABLED) {
// Ignore disabled filters to make it consistent with available in context.
continue;
}
filters[name] = {
contextlevel: contextLevel,
instanceid: instanceId,
contextid: contextId,
filter: name,
localstate: isParentContext ? CoreFilterStateValue.INHERIT : filterInContext.localstate,
inheritedstate: isParentContext ?
filterInContext.localstate :
filters[name]?.inheritedstate ?? filterInContext.localstate,
};
}
});
return Object.values(filters);
}
/**
* Check if there is an override for a category in the states of all filters.
*
* @param states States to check.
* @returns True if has category override, false otherwise.
*/
protected hasCategoryOverride(states: CoreFilterAllStates): boolean {
if (!states[ContextLevel.COURSECAT]) {
return false;
}
for (const instanceId in states[ContextLevel.COURSECAT]) {
for (const name in states[ContextLevel.COURSECAT][instanceId]) {
if (
states[ContextLevel.COURSECAT][instanceId][name].localstate !== states[ContextLevel.SYSTEM][0][name].localstate
) {
return true;
}
}
}
return false;
}
/**
* Get filters and format text.
*
@ -274,7 +346,7 @@ export class CoreFilterHelperProvider {
*/
async getFiltersAndFormatText(
text: string,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
options?: CoreFilterFormatTextOptions,
siteId?: string,
@ -298,7 +370,7 @@ export class CoreFilterHelperProvider {
*/
protected getFromMemoryCache(
courseId: number,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
site: CoreSite,
): CoreFilterFilter[] | undefined {
@ -331,7 +403,7 @@ export class CoreFilterHelperProvider {
const site = await CoreSites.getSite(siteId);
// Get filters at site level.
const filters = await CoreFilter.getAvailableInContext('system', 0, site.getId());
const filters = await CoreFilter.getAvailableInContext(ContextLevel.SYSTEM, 0, site.getId());
return CoreFilterDelegate.shouldBeApplied(filters, options, site);
}
@ -346,7 +418,7 @@ export class CoreFilterHelperProvider {
*/
protected storeInMemoryCache(
courseId: number,
contextLevel: string,
contextLevel: ContextLevel,
contexts: CoreFilterClassifiedFilters,
siteId: string,
): void {

View File

@ -15,7 +15,7 @@
import { Injectable } from '@angular/core';
import { CoreNetwork } from '@services/network';
import { CoreSites, CoreSitesReadingStrategy } from '@services/sites';
import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites';
import { CoreSite } from '@classes/sites/site';
import { CoreWSExternalWarning } from '@services/ws';
import { CoreTextUtils } from '@services/utils/text';
@ -24,6 +24,7 @@ import { makeSingleton } from '@singletons';
import { CoreEvents, CoreEventSiteData } from '@singletons/events';
import { CoreLogger } from '@singletons/logger';
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
import { ContextLevel } from '@/core/constants';
/**
* Service to provide filter functionalities.
@ -61,11 +62,37 @@ export class CoreFilterProvider {
});
}
/**
* Check if getting all states is available in site.
*
* @param siteId Site ID. If not defined, current site.
* @returns Whether it's available.
* @since 4.4
*/
async canGetAllStates(siteId?: string): Promise<boolean> {
const site = await CoreSites.getSite(siteId);
return this.canGetAllStatesInSite(site);
}
/**
* Check if getting all states is available in site.
*
* @param site Site. If not defined, current site.
* @returns Whether it's available.
* @since 4.4
*/
canGetAllStatesInSite(site?: CoreSite): boolean {
site = site || CoreSites.getCurrentSite();
return !!(site?.wsAvailable('core_filters_get_all_states'));
}
/**
* Returns whether or not we can get the available filters: the WS is available and the feature isn't disabled.
*
* @param siteId Site ID. If not defined, current site.
* @returns Promise resolved with boolean: whethe can get filters.
* @returns Whether can get filters.
*/
async canGetFilters(siteId?: string): Promise<boolean> {
const disabled = await this.checkFiltersDisabled(siteId);
@ -77,7 +104,7 @@ export class CoreFilterProvider {
* Returns whether or not we can get the available filters: the WS is available and the feature isn't disabled.
*
* @param site Site. If not defined, current site.
* @returns Promise resolved with boolean: whethe can get filters.
* @returns Whether can get filters.
*/
canGetFiltersInSite(site?: CoreSite): boolean {
return !this.checkFiltersDisabledInSite(site);
@ -140,7 +167,7 @@ export class CoreFilterProvider {
}
filters.forEach((filter) => {
if (hadSystemContext && filter.contextlevel == 'course' && filter.instanceid == site.getSiteHomeId()) {
if (hadSystemContext && filter.contextlevel === ContextLevel.COURSE && filter.instanceid == site.getSiteHomeId()) {
if (hadSiteHomeContext) {
// We need to return both site home and system. Add site home first.
classified[filter.contextlevel][filter.instanceid].push(filter);
@ -150,9 +177,9 @@ export class CoreFilterProvider {
}
// Simulate the system context based on the inherited data.
filter.contextlevel = 'system';
filter.contextlevel = ContextLevel.SYSTEM;
filter.instanceid = 0;
filter.contextid = -1;
filter.contextid = undefined;
filter.localstate = filter.inheritedstate;
}
@ -162,6 +189,29 @@ export class CoreFilterProvider {
return classified;
}
/**
* Given a context level and instance ID, return the proper context to use.
*
* @param contextLevel The context level.
* @param instanceId Instance ID related to the context.
* @param options Options.
* @returns Context to use.
*/
getEffectiveContext(
contextLevel: ContextLevel,
instanceId: number,
options: {courseId?: number} = {},
): {contextLevel: ContextLevel; instanceId: number} {
if (contextLevel === ContextLevel.BLOCK || contextLevel === ContextLevel.USER) {
// Blocks and users cannot have specific filters, use the parent context instead.
return options.courseId ?
{ contextLevel: ContextLevel.COURSE, instanceId: options.courseId } :
{ contextLevel: ContextLevel.SYSTEM, instanceId: 0 };
}
return { contextLevel, instanceId };
}
/**
* Given some HTML code, this function returns the text as safe HTML.
*
@ -217,6 +267,53 @@ export class CoreFilterProvider {
return text;
}
/**
* Get cache key for get all states WS call.
*
* @returns Cache key.
*/
protected getAllStatesCacheKey(): string {
return this.ROOT_CACHE_KEY + 'allStates';
}
/**
* Get all the states for filters.
*
* @param options Options.
* @returns Promise resolved with the filters classified by context.
* @since 4.4
*/
async getAllStates(options: CoreSitesCommonWSOptions = {}): Promise<CoreFilterAllStates> {
const site = await CoreSites.getSite(options.siteId);
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getAllStatesCacheKey(),
updateFrequency: CoreSite.FREQUENCY_RARELY,
// Use stale while revalidate by default, but always use the first value. If data is updated it will be stored in DB.
...CoreSites.getReadingStrategyPreSets(options.readingStrategy ?? CoreSitesReadingStrategy.STALE_WHILE_REVALIDATE),
};
const result = await site.read<CoreFilterGetAllStatesWSResponse>('core_filters_get_all_states', {}, preSets);
const classified: CoreFilterAllStates = {};
result.filters.forEach((filter) => {
classified[filter.contextlevel] = classified[filter.contextlevel] || {};
classified[filter.contextlevel][filter.instanceid] = classified[filter.contextlevel][filter.instanceid] || {};
classified[filter.contextlevel][filter.instanceid][filter.filter] = {
contextlevel: filter.contextlevel,
instanceid: filter.instanceid,
contextid: filter.contextid,
filter: filter.filter,
localstate: filter.state,
inheritedstate: filter.state,
};
});
return classified;
}
/**
* Get cache key for available in contexts WS calls.
*
@ -297,7 +394,7 @@ export class CoreFilterProvider {
* @param siteId Site ID. If not defined, current site.
* @returns Promise resolved with the filters.
*/
async getAvailableInContext(contextLevel: string, instanceId: number, siteId?: string): Promise<CoreFilterFilter[]> {
async getAvailableInContext(contextLevel: ContextLevel, instanceId: number, siteId?: string): Promise<CoreFilterFilter[]> {
const result = await this.getAvailableInContexts([{ contextlevel: contextLevel, instanceid: instanceId }], siteId);
return result[contextLevel][instanceId] || [];
@ -346,6 +443,45 @@ export class CoreFilterProvider {
}
}
/**
* Given a context, return the list of contexts used in the filters inheritance tree, from bottom to top.
* E.g. when using module, it will return the module context, course context (if course ID is supplied), category context
* (if categoy ID is supplied) and system context.
*
* @param contextLevel Context level.
* @param instanceId Instance ID.
* @param options Options
* @returns List of contexts.
*/
getContextsTreeList(
contextLevel: ContextLevel,
instanceId: number,
options: {courseId?: number; categoryId?: number} = {},
): { contextLevel: ContextLevel; instanceId: number }[] {
// Make sure context has been converted.
const newContext = CoreFilter.getEffectiveContext(contextLevel, instanceId, options);
contextLevel = newContext.contextLevel;
instanceId = newContext.instanceId;
const contexts = [
{ contextLevel, instanceId },
];
if (contextLevel === ContextLevel.MODULE && options.courseId) {
contexts.push({ contextLevel: ContextLevel.COURSE, instanceId: options.courseId });
}
if ((contextLevel === ContextLevel.MODULE || contextLevel === ContextLevel.COURSE) && options.categoryId) {
contexts.push({ contextLevel: ContextLevel.COURSECAT, instanceId: options.categoryId });
}
if (contextLevel !== ContextLevel.SYSTEM) {
contexts.push({ contextLevel: ContextLevel.SYSTEM, instanceId: 0 });
}
return contexts;
}
/**
* Invalidates all available in context WS calls.
*
@ -358,6 +494,18 @@ export class CoreFilterProvider {
await site.invalidateWsCacheForKeyStartingWith(this.getAvailableInContextsPrefixCacheKey());
}
/**
* Invalidates get all states WS call.
*
* @param siteId Site ID (empty for current site).
* @returns Promise resolved when the data is invalidated.
*/
async invalidateAllStates(siteId?: string): Promise<void> {
const site = await CoreSites.getSite(siteId);
await site.invalidateWsCacheForKey(this.getAllStatesCacheKey());
}
/**
* Invalidates available in context WS call.
*
@ -382,7 +530,7 @@ export class CoreFilterProvider {
* @param siteId Site ID (empty for current site).
* @returns Promise resolved when the data is invalidated.
*/
async invalidateAvailableInContext(contextLevel: string, instanceId: number, siteId?: string): Promise<void> {
async invalidateAvailableInContext(contextLevel: ContextLevel, instanceId: number, siteId?: string): Promise<void> {
await this.invalidateAvailableInContexts([{ contextlevel: contextLevel, instanceid: instanceId }], siteId);
}
@ -407,7 +555,7 @@ export class CoreFilterProvider {
for (let i = 0; i < contexts.length; i++) {
const context = contexts[i];
if (context.contextlevel != 'system') {
if (context.contextlevel !== ContextLevel.SYSTEM) {
continue;
}
@ -415,7 +563,7 @@ export class CoreFilterProvider {
// Use course site home instead. Check if it's already in the list.
result.hadSiteHomeContext = contexts.some((context) =>
context.contextlevel == 'course' && context.instanceid == site.getSiteHomeId());
context.contextlevel === ContextLevel.COURSE && context.instanceid == site.getSiteHomeId());
if (result.hadSiteHomeContext) {
// Site home is already in list, remove this context from the list.
@ -423,7 +571,7 @@ export class CoreFilterProvider {
} else {
// Site home not in list, use it instead of system.
contexts[i] = {
contextlevel: 'course',
contextlevel: ContextLevel.COURSE,
instanceid: site.getSiteHomeId(),
};
}
@ -470,20 +618,20 @@ export type CoreFiltersGetAvailableInContextWSParams = {
* Data about a context sent to core_filters_get_available_in_context.
*/
export type CoreFiltersGetAvailableInContextWSParamContext = {
contextlevel: string; // The context level where the filters are: (coursecat, course, module).
contextlevel: ContextLevel; // The context level where the filters are: (coursecat, course, module).
instanceid: number; // The instance id of item associated with the context.
};
/**
* Filter object returned by core_filters_get_available_in_context.
* Filter data.
*/
export type CoreFilterFilter = {
contextlevel: string; // The context level where the filters are: (coursecat, course, module).
contextlevel: ContextLevel; // The context level where the filters are: (coursecat, course, module).
instanceid: number; // The instance id of item associated with the context.
contextid: number; // The context id.
contextid?: number; // The context id. It will be undefined in cases where it cannot be calculated in the app.
filter: string; // Filter plugin name.
localstate: number; // Filter state: 1 for on, -1 for off, 0 if inherit.
inheritedstate: number; // 1 or 0 to use when localstate is set to inherit.
localstate: CoreFilterStateValue; // Filter state.
inheritedstate: CoreFilterStateValue; // State to use when localstate is set to inherit.
};
/**
@ -494,11 +642,41 @@ export type CoreFilterGetAvailableInContextResult = {
warnings: CoreWSExternalWarning[]; // List of warnings.
};
/**
* Filter state returned by core_filters_get_all_states.
*/
export type CoreFilterState = {
contextlevel: ContextLevel; // The context level where the filters are: (coursecat, course, module).
instanceid: number; // The instance id of item associated with the context.
contextid: number; // The context id.
filter: string; // Filter plugin name.
state: CoreFilterStateValue; // Filter state.
sortorder: number; // Sort order.
};
/**
* Context levels enumeration.
*/
export const enum CoreFilterStateValue {
ON = 1,
INHERIT = 0,
OFF = -1,
DISABLED = -9999,
}
/**
* Result of core_filters_get_all_states.
*/
export type CoreFilterGetAllStatesWSResponse = {
filters: CoreFilterState[]; // Filter state.
warnings: CoreWSExternalWarning[]; // List of warnings.
};
/**
* Options that can be passed to format text.
*/
export type CoreFilterFormatTextOptions = {
contextLevel?: string; // The context level where the text is.
contextLevel?: ContextLevel; // The context level where the text is.
instanceId?: number; // The instance id related to the context.
clean?: boolean; // If true all HTML will be removed. Default false.
filter?: boolean; // If true the string will be run through applicable filters as well. Default true.
@ -517,3 +695,14 @@ export type CoreFilterClassifiedFilters = {
[instanceid: number]: CoreFilterFilter[];
};
};
/**
* All filter states classified by context, instance and filter name.
*/
export type CoreFilterAllStates = {
[contextlevel: string]: {
[instanceid: number]: {
[filtername: string]: CoreFilterFilter;
};
};
};

View File

@ -23,6 +23,7 @@ import { CoreWSFile } from '@services/ws';
import { CoreIonicColorNames } from '@singletons/colors';
import { CoreLogger } from '@singletons/logger';
import { CoreQuestionBehaviourButton, CoreQuestionHelper, CoreQuestionQuestion } from '../services/question-helper';
import { ContextLevel } from '@/core/constants';
/**
* Base class for components to render a question.
@ -37,7 +38,7 @@ export class CoreQuestionBaseComponent<T extends AddonModQuizQuestion = AddonMod
@Input() componentId?: number; // ID of the component the question belongs to.
@Input() attemptId?: number; // Attempt ID.
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // The course the question belongs to (if any).
@Input() review?: boolean; // Whether the user is in review mode.

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, Input, Output, OnInit, EventEmitter, ChangeDetectorRef, Type, ElementRef } from '@angular/core';
import { AsyncDirective } from '@classes/async-directive';
import { CorePromisedValue } from '@classes/promised-value';
@ -41,7 +42,7 @@ export class CoreQuestionComponent implements OnInit, AsyncDirective {
@Input() attemptId?: number; // Attempt ID.
@Input() usageId?: number; // Usage ID.
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the question belongs to (if any). It can be used to improve performance with filters.
@Input() review?: boolean; // Whether the user is in review mode.

View File

@ -28,6 +28,7 @@ import { CoreQuestion, CoreQuestionProvider, CoreQuestionQuestionParsed, CoreQue
import { CoreQuestionDelegate } from './question-delegate';
import { CoreIcons } from '@singletons/icons';
import { CoreUrlUtils } from '@services/utils/url';
import { ContextLevel } from '@/core/constants';
/**
* Service with some common functions to handle questions.
@ -868,7 +869,7 @@ export class CoreQuestionHelperProvider {
element: HTMLElement,
component?: string,
componentId?: number,
contextLevel?: string,
contextLevel?: ContextLevel,
contextInstanceId?: number,
courseId?: number,
): void {

View File

@ -31,6 +31,7 @@ import { CoreUtils } from '@services/utils/utils';
import { CoreTime } from '@singletons/time';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { CoreBlockSideBlocksComponent } from '@features/block/components/side-blocks/side-blocks';
import { ContextLevel } from '@/core/constants';
/**
* Page that displays site home index.
@ -233,7 +234,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
CoreDomUtils.openSideModal({
component: CoreBlockSideBlocksComponent,
componentProps: {
contextLevel: 'course',
contextLevel: ContextLevel.COURSE,
instanceId: this.siteHomeId,
initialBlockInstanceId: blockInstanceId,
},

View File

@ -23,6 +23,7 @@ import { CoreSitePluginsOnlyTitleBlockComponent } from '@features/siteplugins/co
import { CoreSitePluginsBlockHandlerData, CoreSitePluginsContent } from '@features/siteplugins/services/siteplugins';
import { CoreLogger } from '@singletons/logger';
import { CoreSitePluginsBaseHandler } from './base-handler';
import { ContextLevel } from '@/core/constants';
/**
* Handler to support a block using a site plugin.
@ -48,7 +49,7 @@ export class CoreSitePluginsBlockHandler extends CoreSitePluginsBaseHandler impl
*/
async getDisplayData(
block: CoreCourseBlock,
contextLevel: string,
contextLevel: ContextLevel,
instanceId: number,
): Promise<CoreBlockHandlerData> {
const className = this.handlerSchema.displaydata?.class || 'block_' + block.name;

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { CoreQuestionBehaviourDelegate } from '@features/question/services/behaviour-delegate';
@ -33,7 +34,7 @@ export class CoreSitePluginsQuestionBehaviourComponent extends CoreSitePluginsCo
@Input() componentId?: number; // ID of the component the question belongs to.
@Input() attemptId?: number; // Attempt ID.
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the question belongs to (if any). It can be used to improve performance with filters.
@Input() review?: boolean; // Whether the user is in review mode.

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { AddonModQuizQuestion } from '@features/question/classes/base-question-component';
@ -34,7 +35,7 @@ export class CoreSitePluginsQuestionComponent extends CoreSitePluginsCompileInit
@Input() componentId?: number; // ID of the component the question belongs to.
@Input() attemptId?: number; // Attempt ID.
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the question belongs to (if any). It can be used to improve performance with filters.
@Input() review?: boolean; // Whether the user is in review mode.

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, OnInit, Input } from '@angular/core';
import { FormGroup } from '@angular/forms';
@ -36,7 +37,7 @@ export class CoreSitePluginsUserProfileFieldComponent extends CoreSitePluginsCom
@Input() disabled = false; // True if disabled. Defaults to false.
@Input() form?: FormGroup; // Form where to add the form control. Required if edit=true or signup=true.
@Input() registerAuth?: string; // Register auth method. E.g. 'email'.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the field belongs to (if any). It can be used to improve performance with filters.

View File

@ -21,6 +21,7 @@ import { CoreSitePluginsModuleIndexComponent } from '../../components/module-ind
import { CoreSites } from '@services/sites';
import { CoreFilterFormatTextOptions } from '@features/filter/services/filter';
import { CoreFilterHelper } from '@features/filter/services/filter-helper';
import { ContextLevel } from '@/core/constants';
/**
* Page to render the index page of a module site plugin.
@ -57,7 +58,7 @@ export class CoreSitePluginsModuleIndexPage implements OnInit, CanLeave {
const filteredTitle = await CoreFilterHelper.getFiltersAndFormatText(
this.title.trim(),
'module',
ContextLevel.MODULE,
this.module?.id ?? -1,
options,
siteId,

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, Input, OnInit } from '@angular/core';
import { FormGroup, Validators, FormControl } from '@angular/forms';
@ -32,7 +33,7 @@ export abstract class CoreUserProfileFieldBaseComponent<T = string> implements O
@Input() disabled = false; // True if disabled. Defaults to false.
@Input() form?: FormGroup; // Form where to add the form control. Required if edit=true or signup=true.
@Input() registerAuth?: string; // Register auth method. E.g. 'email'.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the field belongs to (if any). It can be used to improve performance with filters.

View File

@ -19,6 +19,7 @@ import { AuthEmailSignupProfileField } from '@features/login/services/login-help
import { CoreUserProfileField } from '@features/user/services/user';
import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profile-field-delegate';
import { CoreUtils } from '@services/utils/utils';
import { ContextLevel } from '@/core/constants';
/**
* Directive to render user profile field.
@ -34,7 +35,7 @@ export class CoreUserProfileFieldComponent implements OnInit {
@Input() edit = false; // True if editing the field. Defaults to false.
@Input() form?: FormGroup; // Form where to add the form control. Required if edit=true or signup=true.
@Input() registerAuth?: string; // Register auth method. E.g. 'email'.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the field belongs to (if any). It can be used to improve performance with filters.
@ -78,7 +79,7 @@ export type CoreUserProfileFieldComponentData = {
disabled?: boolean;
form?: FormGroup;
registerAuth?: string;
contextLevel?: string;
contextLevel?: ContextLevel;
contextInstanceId?: number;
courseId?: number;
};

View File

@ -29,7 +29,8 @@
<ion-item class="ion-text-wrap" *ngIf="user.description" lines="full">
<ion-label>
<p>
<core-format-text [text]="user.description" contextLevel="user" [contextInstanceId]="user.id" />
<core-format-text [text]="user.description" contextLevel="user" [contextInstanceId]="user.id"
[courseId]="courseId" />
</p>
</ion-label>
</ion-item>

View File

@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { ContextLevel } from '@/core/constants';
import { Component, Input } from '@angular/core';
import { CoreFileEntry } from '@services/file-helper';
@ -34,7 +35,7 @@ export class CoreViewerTextComponent {
@Input() componentId?: string | number; // Component ID to use in format-text.
@Input() files?: CoreFileEntry[]; // List of files.
@Input() filter?: boolean; // Whether to filter the text.
@Input() contextLevel?: string; // The context level.
@Input() contextLevel?: ContextLevel; // The context level.
@Input() instanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
@Input() displayCopyButton?: boolean; // Whether to display a button to copy the contents.

View File

@ -27,6 +27,7 @@ import { CoreUrl } from '@singletons/url';
import { AlertButton } from '@ionic/angular';
import { CorePath } from '@singletons/path';
import { CorePlatform } from '@services/platform';
import { ContextLevel } from '@/core/constants';
/**
* Different type of errors the app can treat.
@ -1048,7 +1049,7 @@ export type CoreTextUtilsViewTextOptions = {
componentId?: string | number; // An ID to use in conjunction with the component.
files?: CoreWSFile[]; // List of files to display along with the text.
filter?: boolean; // Whether the text should be filtered.
contextLevel?: string; // The context level.
contextLevel?: ContextLevel; // The context level.
instanceId?: number; // The instance ID related to the context.
courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
displayCopyButton?: boolean; // Whether to display a button to copy the text.