MOBILE-3966 user: Use context+contextId in user delegate

main
Dani Palou 2022-01-21 12:58:11 +01:00
parent a51e722e74
commit 101faf20aa
14 changed files with 284 additions and 178 deletions

View File

@ -14,7 +14,12 @@
import { Injectable } from '@angular/core';
import { CoreCourseUserAdminOrNavOptionIndexed } from '@features/courses/services/courses';
import { CoreUserDelegateService, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
import {
CoreUserDelegateContext,
CoreUserDelegateService,
CoreUserProfileHandler,
CoreUserProfileHandlerData,
} from '@features/user/services/user-delegate';
import { CoreNavigator } from '@services/navigator';
import { makeSingleton } from '@singletons';
import { AddonBadges } from '../badges';
@ -30,22 +35,17 @@ export class AddonBadgesUserHandlerService implements CoreUserProfileHandler {
type = CoreUserDelegateService.TYPE_NEW_PAGE;
/**
* Check if handler is enabled.
*
* @return Always enabled.
* @inheritdoc
*/
isEnabled(): Promise<boolean> {
return AddonBadges.isPluginEnabled();
}
/**
* Check if handler is enabled for this user in this context.
*
* @param courseId Course ID.
* @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
* @return True if enabled, false otherwise.
* @inheritdoc
*/
async isEnabledForCourse(
async isEnabledForContext(
context: CoreUserDelegateContext,
courseId: number,
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
): Promise<boolean> {
@ -58,19 +58,17 @@ export class AddonBadgesUserHandlerService implements CoreUserProfileHandler {
}
/**
* Returns the data needed to render the handler.
*
* @return Data needed to render the handler.
* @inheritdoc
*/
getDisplayData(): CoreUserProfileHandlerData {
return {
icon: 'fas-trophy',
title: 'addon.badges.badges',
action: (event, user, courseId): void => {
action: (event, user, context, contextId): void => {
event.preventDefault();
event.stopPropagation();
CoreNavigator.navigateToSitePath('/badges', {
params: { courseId, userId: user.id },
params: { courseId: contextId, userId: user.id },
});
},
};

View File

@ -43,11 +43,11 @@ export class AddonBlogUserHandlerService implements CoreUserProfileHandler {
icon: 'far-newspaper',
title: 'addon.blog.blogentries',
class: 'addon-blog-handler',
action: (event, user, courseId): void => {
action: (event, user, context, contextId): void => {
event.preventDefault();
event.stopPropagation();
CoreNavigator.navigateToSitePath('/blog', {
params: { courseId, userId: user.id },
params: { courseId: contextId, userId: user.id },
});
},
};

View File

@ -16,7 +16,12 @@ import { ADDON_COMPETENCY_COMPETENCIES_PAGE, ADDON_COMPETENCY_LEARNING_PLANS_PAG
import { Injectable } from '@angular/core';
import { COURSE_PAGE_NAME } from '@features/course/course.module';
import { CoreUserProfile } from '@features/user/services/user';
import { CoreUserProfileHandler, CoreUserDelegateService, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
import {
CoreUserProfileHandler,
CoreUserDelegateService,
CoreUserProfileHandlerData,
CoreUserDelegateContext,
} from '@features/user/services/user-delegate';
import { PARTICIPANTS_PAGE_NAME } from '@features/user/user.module';
import { CoreNavigator } from '@services/navigator';
import { makeSingleton } from '@singletons';
@ -43,10 +48,10 @@ export class AddonCompetencyUserHandlerService implements CoreUserProfileHandler
/**
* @inheritdoc
*/
async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise<boolean> {
async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): Promise<boolean> {
try {
if (courseId) {
return AddonCompetency.canViewUserCompetenciesInCourse(courseId, user.id);
if (context === CoreUserDelegateContext.COURSE) {
return await AddonCompetency.canViewUserCompetenciesInCourse(contextId, user.id);
} else {
const plans = await AddonCompetency.getLearningPlans(user.id);
@ -61,21 +66,8 @@ export class AddonCompetencyUserHandlerService implements CoreUserProfileHandler
/**
* @inheritdoc
*/
getDisplayData(user: CoreUserProfile, courseId: number): CoreUserProfileHandlerData {
if (courseId) {
return {
icon: 'fas-award',
title: 'addon.competency.competencies',
class: 'addon-competency-handler',
action: (event, user, courseId): void => {
event.preventDefault();
event.stopPropagation();
CoreNavigator.navigateToSitePath(
[COURSE_PAGE_NAME, courseId, PARTICIPANTS_PAGE_NAME, user.id, ADDON_COMPETENCY_COMPETENCIES_PAGE].join('/'),
);
},
};
} else {
getDisplayData(user: CoreUserProfile, context: CoreUserDelegateContext): CoreUserProfileHandlerData {
if (context !== CoreUserDelegateContext.COURSE) {
return {
icon: 'fas-route',
title: 'addon.competency.learningplans',
@ -89,6 +81,19 @@ export class AddonCompetencyUserHandlerService implements CoreUserProfileHandler
},
};
}
return {
icon: 'fas-award',
title: 'addon.competency.competencies',
class: 'addon-competency-handler',
action: (event, user, context, contextId): void => {
event.preventDefault();
event.stopPropagation();
CoreNavigator.navigateToSitePath(
[COURSE_PAGE_NAME, contextId, PARTICIPANTS_PAGE_NAME, user.id, ADDON_COMPETENCY_COMPETENCIES_PAGE].join('/'),
);
},
};
}
}

View File

@ -14,7 +14,12 @@
import { Injectable } from '@angular/core';
import { CoreUserProfile } from '@features/user/services/user';
import { CoreUserProfileHandler, CoreUserDelegateService, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
import {
CoreUserProfileHandler,
CoreUserDelegateService,
CoreUserProfileHandlerData,
CoreUserDelegateContext,
} from '@features/user/services/user-delegate';
import { CoreNavigator } from '@services/navigator';
import { makeSingleton } from '@singletons';
import { AddonCourseCompletion } from '../coursecompletion';
@ -40,15 +45,19 @@ export class AddonCourseCompletionUserHandlerService implements CoreUserProfileH
/**
* @inheritdoc
*/
async isEnabledForCourse(courseId?: number): Promise<boolean> {
async isEnabledForContext(context: CoreUserDelegateContext, courseId: number): Promise<boolean> {
if (context !== CoreUserDelegateContext.COURSE) {
return false;
}
return AddonCourseCompletion.isPluginViewEnabledForCourse(courseId);
}
/**
* @inheritdoc
*/
async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise<boolean> {
return await AddonCourseCompletion.isPluginViewEnabledForUser(courseId!, user.id);
async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): Promise<boolean> {
return await AddonCourseCompletion.isPluginViewEnabledForUser(contextId, user.id);
}
/**
@ -59,11 +68,11 @@ export class AddonCourseCompletionUserHandlerService implements CoreUserProfileH
icon: 'fas-tasks',
title: 'addon.coursecompletion.coursecompletion',
class: 'addon-coursecompletion-handler',
action: (event, user, courseId): void => {
action: (event, user, context, contextId): void => {
event.preventDefault();
event.stopPropagation();
CoreNavigator.navigateToSitePath('/coursecompletion', {
params: { courseId, userId: user.id },
params: { courseId: contextId, userId: user.id },
});
},
};

View File

@ -32,9 +32,7 @@ export class AddonMessagesSendMessageUserHandlerService implements CoreUserProfi
type = CoreUserDelegateService.TYPE_COMMUNICATION;
/**
* Check if handler is enabled.
*
* @return Promise resolved with true if enabled, rejected or resolved with false otherwise.
* @inheritdoc
*/
isEnabled(): Promise<boolean> {
return AddonMessages.isPluginEnabled();
@ -43,15 +41,12 @@ export class AddonMessagesSendMessageUserHandlerService implements CoreUserProfi
/**
* @inheritdoc
*/
async isEnabledForCourse(): Promise<boolean> {
async isEnabledForContext(): Promise<boolean> {
return !!CoreSites.getCurrentSite();
}
/**
* Check if handler is enabled for this user in this context.
*
* @param user User to check.
* @return Promise resolved with true if enabled, resolved with false otherwise.
* @inheritdoc
*/
async isEnabledForUser(user: CoreUserProfile): Promise<boolean> {
const currentSite = CoreSites.getRequiredCurrentSite();

View File

@ -14,7 +14,12 @@
import { Injectable } from '@angular/core';
import { CoreUserProfile } from '@features/user/services/user';
import { CoreUserProfileHandler, CoreUserDelegateService, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
import {
CoreUserProfileHandler,
CoreUserDelegateService,
CoreUserProfileHandlerData,
CoreUserDelegateContext,
} from '@features/user/services/user-delegate';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { makeSingleton } from '@singletons';
@ -41,14 +46,14 @@ export class AddonNotesUserHandlerService implements CoreUserProfileHandler {
/**
* @inheritdoc
*/
async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise<boolean> {
async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): Promise<boolean> {
// Active course required.
if (!courseId || user.id == CoreSites.getCurrentSiteUserId()) {
if (context !== CoreUserDelegateContext.COURSE || !contextId || user.id == CoreSites.getCurrentSiteUserId()) {
return false;
}
// We are not using isEnabledForCourse because we need to cache the call.
return AddonNotes.isPluginViewNotesEnabledForCourse(courseId);
return AddonNotes.isPluginViewNotesEnabledForCourse(contextId);
}
/**
@ -59,11 +64,11 @@ export class AddonNotesUserHandlerService implements CoreUserProfileHandler {
icon: 'fas-receipt',
title: 'addon.notes.notes',
class: 'addon-notes-handler',
action: (event, user, courseId): void => {
action: (event, user, context, contextId): void => {
event.preventDefault();
event.stopPropagation();
CoreNavigator.navigateToSitePath('/notes', {
params: { courseId, userId: user.id },
params: { courseId: contextId, userId: user.id },
});
},
};

View File

@ -16,7 +16,12 @@ import { Injectable } from '@angular/core';
import { AddonPrivateFiles } from '@/addons/privatefiles/services/privatefiles';
import { makeSingleton } from '@singletons';
import { CoreUserDelegateService, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
import {
CoreUserDelegateContext,
CoreUserDelegateService,
CoreUserProfileHandler,
CoreUserProfileHandlerData,
} from '@features/user/services/user-delegate';
import { CoreUserProfile } from '@features/user/services/user';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
@ -44,9 +49,9 @@ export class AddonPrivateFilesUserHandlerService implements CoreUserProfileHandl
/**
* @inheritdoc
*/
async isEnabledForUser(user: CoreUserProfile): Promise<boolean> {
// Private files only available for the current user.
return user.id == CoreSites.getCurrentSiteUserId();
async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext): Promise<boolean> {
// Private files only available for the current user in user menu.
return user.id == CoreSites.getCurrentSiteUserId() && context === CoreUserDelegateContext.USER_MENU;
}
/**

View File

@ -19,6 +19,7 @@ import { GRADES_PAGE_NAME } from '@features/grades/grades.module';
import { CoreGrades } from '@features/grades/services/grades';
import { CoreUserProfile } from '@features/user/services/user';
import {
CoreUserDelegateContext,
CoreUserDelegateService ,
CoreUserProfileHandler,
CoreUserProfileHandlerData,
@ -50,8 +51,8 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler {
/**
* @inheritdoc
*/
async isEnabledForCourse(courseId?: number): Promise<boolean> {
if (courseId) {
async isEnabledForContext(context: CoreUserDelegateContext, courseId: number): Promise<boolean> {
if (context === CoreUserDelegateContext.COURSE) {
return CoreUtils.ignoreErrors(CoreGrades.isPluginEnabledForCourse(courseId), false);
} else {
return CoreGrades.isCourseGradesEnabled();
@ -61,9 +62,9 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler {
/**
* @inheritdoc
*/
async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise<boolean> {
if (courseId) {
return CoreUtils.promiseWorks(CoreGrades.getCourseGradesTable(courseId, user.id));
async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): Promise<boolean> {
if (context === CoreUserDelegateContext.COURSE) {
return CoreUtils.promiseWorks(CoreGrades.getCourseGradesTable(contextId, user.id));
}
// All course grades only available for the current user.
@ -73,17 +74,17 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler {
/**
* @inheritdoc
*/
getDisplayData(user: CoreUserProfile, courseId?: number): CoreUserProfileHandlerData {
if (courseId) {
getDisplayData(user: CoreUserProfile, context: CoreUserDelegateContext): CoreUserProfileHandlerData {
if (context === CoreUserDelegateContext.COURSE) {
return {
icon: 'fas-chart-bar',
title: 'core.grades.grades',
class: 'core-grades-user-handler',
action: (event, user, courseId): void => {
action: (event, user, context, contextId): void => {
event.preventDefault();
event.stopPropagation();
CoreNavigator.navigateToSitePath(
[COURSE_PAGE_NAME, courseId, PARTICIPANTS_PAGE_NAME, user.id, GRADES_PAGE_NAME].join('/'),
[COURSE_PAGE_NAME, contextId, PARTICIPANTS_PAGE_NAME, user.id, GRADES_PAGE_NAME].join('/'),
);
},
};

View File

@ -18,7 +18,12 @@ import { CoreSite, CoreSiteInfo } from '@classes/site';
import { CoreLoginSitesComponent } from '@features/login/components/sites/sites';
import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreUser, CoreUserProfile } from '@features/user/services/user';
import { CoreUserProfileHandlerData, CoreUserDelegate, CoreUserDelegateService } from '@features/user/services/user-delegate';
import {
CoreUserProfileHandlerData,
CoreUserDelegate,
CoreUserDelegateService,
CoreUserDelegateContext,
} from '@features/user/services/user-delegate';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
@ -64,20 +69,21 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy {
if (this.siteInfo) {
this.user = await CoreUser.getProfile(this.siteInfo.userid);
this.subscription = CoreUserDelegate.getProfileHandlersFor(this.user).subscribe((handlers) => {
if (!handlers || !this.user) {
return;
}
this.handlers = [];
handlers.forEach((handler) => {
if (handler.type == CoreUserDelegateService.TYPE_NEW_PAGE) {
this.handlers.push(handler.data);
this.subscription = CoreUserDelegate.getProfileHandlersFor(this.user, CoreUserDelegateContext.USER_MENU)
.subscribe((handlers) => {
if (!handlers || !this.user) {
return;
}
});
this.handlersLoaded = CoreUserDelegate.areHandlersLoaded(this.user.id);
});
this.handlers = [];
handlers.forEach((handler) => {
if (handler.type == CoreUserDelegateService.TYPE_NEW_PAGE) {
this.handlers.push(handler.data);
}
});
this.handlersLoaded = CoreUserDelegate.areHandlersLoaded(this.user.id, CoreUserDelegateContext.USER_MENU);
});
}
}
@ -150,7 +156,7 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy {
await this.close(event);
handler.action(event, this.user);
handler.action(event, this.user, CoreUserDelegateContext.USER_MENU);
}
/**

View File

@ -19,9 +19,13 @@ import {
CoreSitePluginsUserHandlerData,
} from '@features/siteplugins/services/siteplugins';
import { CoreUserProfile } from '@features/user/services/user';
import { CoreUserDelegateService, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
import {
CoreUserDelegateContext,
CoreUserDelegateService,
CoreUserProfileHandler,
CoreUserProfileHandlerData,
} from '@features/user/services/user-delegate';
import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreUtils, PromiseDefer } from '@services/utils/utils';
import { Md5 } from 'ts-md5';
import { CoreSitePluginsBaseHandler } from './base-handler';
@ -55,11 +59,7 @@ export class CoreSitePluginsUserProfileHandler extends CoreSitePluginsBaseHandle
/**
* @inheritdoc
*/
async isEnabledForCourse(
courseId?: number,
): Promise<boolean> {
courseId = courseId || CoreSites.getCurrentSiteHomeId();
async isEnabledForContext(context: CoreUserDelegateContext, courseId: number): Promise<boolean> {
// Check if it's enabled for the course.
return CoreSitePlugins.isHandlerEnabledForCourse(
courseId,
@ -89,12 +89,12 @@ export class CoreSitePluginsUserProfileHandler extends CoreSitePluginsBaseHandle
title: this.title,
icon: this.handlerSchema.displaydata?.icon,
class: this.handlerSchema.displaydata?.class,
action: (event: Event, user: CoreUserProfile, courseId?: number): void => {
action: (event, user, context, contextId): void => {
event.preventDefault();
event.stopPropagation();
const args = {
courseid: courseId,
courseid: contextId,
userid: user.id,
};
const hash = <string> Md5.hashAsciiStr(JSON.stringify(args));

View File

@ -23,7 +23,12 @@ import { CoreDomUtils } from '@services/utils/dom';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreUser, CoreUserProfile, CoreUserProvider } from '@features/user/services/user';
import { CoreUserHelper } from '@features/user/services/user-helper';
import { CoreUserDelegate, CoreUserDelegateService, CoreUserProfileHandlerData } from '@features/user/services/user-delegate';
import {
CoreUserDelegate,
CoreUserDelegateContext,
CoreUserDelegateService,
CoreUserProfileHandlerData,
} from '@features/user/services/user-delegate';
import { CoreUtils } from '@services/utils/utils';
import { CoreNavigator } from '@services/navigator';
import { CoreCourses } from '@features/courses/services/courses';
@ -131,7 +136,9 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
// If there's already a subscription, unsubscribe because we'll get a new one.
this.subscription?.unsubscribe();
this.subscription = CoreUserDelegate.getProfileHandlersFor(user, this.courseId).subscribe((handlers) => {
const context = this.courseId ? CoreUserDelegateContext.COURSE : CoreUserDelegateContext.SITE;
this.subscription = CoreUserDelegate.getProfileHandlersFor(user, context, this.courseId).subscribe((handlers) => {
this.actionHandlers = [];
this.newPageHandlers = [];
this.communicationHandlers = [];
@ -150,7 +157,7 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
}
});
this.isLoadingHandlers = !CoreUserDelegate.areHandlersLoaded(user.id);
this.isLoadingHandlers = !CoreUserDelegate.areHandlersLoaded(user.id, context, this.courseId);
});
} catch (error) {
@ -208,7 +215,8 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
return;
}
handler.action(event, this.user, this.courseId);
const context = this.courseId ? CoreUserDelegateContext.COURSE : CoreUserDelegateContext.SITE;
handler.action(event, this.user, context, this.courseId);
}
/**

View File

@ -52,7 +52,7 @@ export class CoreUserProfileMailHandlerService implements CoreUserProfileHandler
icon: 'mail',
title: 'core.user.sendemail',
class: 'core-user-profile-mail',
action: (event: Event, user: CoreUserProfile): void => {
action: (event, user): void => {
event.preventDefault();
event.stopPropagation();

View File

@ -47,15 +47,17 @@ export interface CoreUserProfileHandler extends CoreDelegateHandler {
cacheEnabled?: boolean;
/**
* Whether or not the handler is enabled for a course.
* Whether or not the handler is enabled for a context.
*
* @param courseId Course ID where to show.
* @param context Context.
* @param contextId Context ID.
* @param navOptions Navigation options for the course.
* @param admOptions Admin options for the course.
* @return Whether or not the handler is enabled for a user.
*/
isEnabledForCourse?(
courseId?: number,
isEnabledForContext?(
context: CoreUserDelegateContext,
contextId: number,
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
admOptions?: CoreCourseUserAdminOrNavOptionIndexed,
): Promise<boolean>;
@ -64,22 +66,21 @@ export interface CoreUserProfileHandler extends CoreDelegateHandler {
* Whether or not the handler is enabled for a user.
*
* @param user User object.
* @param courseId Course ID where to show.
* @param context Context.
* @param contextId Context ID.
* @return Whether or not the handler is enabled for a user.
*/
isEnabledForUser?(
user: CoreUserProfile,
courseId?: number,
): Promise<boolean>;
isEnabledForUser?(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): Promise<boolean>;
/**
* Returns the data needed to render the handler.
*
* @param user User object.
* @param courseId Course ID where to show.
* @param context Context.
* @param contextId Context ID.
* @return Data to be shown.
*/
getDisplayData(user: CoreUserProfile, courseId?: number): CoreUserProfileHandlerData;
getDisplayData(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): CoreUserProfileHandlerData;
}
/**
@ -136,9 +137,10 @@ export interface CoreUserProfileHandlerData {
*
* @param event Click event.
* @param user User object.
* @param courseId Course ID being viewed. If not defined, site context.
* @param context Context.
* @param contextId Context ID.
*/
action(event: Event, user: CoreUserProfile, courseId?: number): void;
action(event: Event, user: CoreUserProfile, context: CoreUserDelegateContext, contextId?: number): void;
}
/**
@ -199,24 +201,16 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
protected featurePrefix = 'CoreUserDelegate_';
// Hold the handlers and the observable to notify them for each user.
protected userHandlers: {
[userId: number]: {
loaded: boolean; // Whether the handlers are loaded.
handlers: CoreUserProfileHandlerToDisplay[]; // List of handlers.
observable: Subject<CoreUserProfileHandlerToDisplay[]>; // Observale to notify the handlers.
};
} = {};
protected userHandlers: Record<number, Record<string, CoreUserDelegateHandlersData>> = {};
constructor() {
super('CoreUserDelegate', true);
CoreEvents.on(CoreUserDelegateService.UPDATE_HANDLER_EVENT, (data) => {
if (!data || !data.handler || !this.userHandlers[data.userId]) {
return;
}
const handlersData = this.getHandlersData(data.userId, data.context, data.contextId);
// Search the handler.
const handler = this.userHandlers[data.userId].handlers.find((userHandler) => userHandler.name == data.handler);
const handler = handlersData.handlers.find((userHandler) => userHandler.name == data.handler);
if (!handler) {
return;
@ -224,7 +218,7 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
// Update the data and notify.
Object.assign(handler.data, data.data);
this.userHandlers[data.userId].observable.next(this.userHandlers[data.userId].handlers);
handlersData.observable.next(handlersData.handlers);
});
CoreEvents.on(CoreEvents.LOGOUT, () => {
@ -232,31 +226,41 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
});
CoreEvents.on(CoreUserProvider.PROFILE_REFRESHED, (data) => {
this.clearHandlerCache(data.courseId, data.userId);
const context = data.courseId ? CoreUserDelegateContext.COURSE : CoreUserDelegateContext.SITE;
this.clearHandlerCache(data.userId, context, data.courseId);
});
}
/**
* Check if handlers are loaded.
* Check if handlers are loaded for a certain user and context.
*
* @param userId User ID.
* @param context Context.
* @param contextId Context ID.
* @return True if handlers are loaded, false otherwise.
*/
areHandlersLoaded(userId: number): boolean {
return this.userHandlers[userId]?.loaded;
areHandlersLoaded(userId: number, context: CoreUserDelegateContext, contextId?: number): boolean {
return this.getHandlersData(userId, context, contextId).loaded;
}
/**
* Clear current user handlers.
*
* @param userId The user to clear.
* @param userId The user to clear. Undefined for all users.
* @param context Context.
* @param contextId Context ID.
*/
clearUserHandlers(userId: number): void {
const userData = this.userHandlers[userId];
clearUserHandlers(userId?: number, context?: CoreUserDelegateContext, contextId?: number): void {
if (!userId) {
this.userHandlers = {};
} else if (!context) {
delete this.userHandlers[userId];
} else {
const handlersData = this.getHandlersData(userId, context, contextId);
if (userData) {
userData.handlers = [];
userData.observable.next([]);
userData.loaded = false;
handlersData.handlers = [];
handlersData.observable.next([]);
handlersData.loaded = false;
}
}
@ -264,58 +268,65 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
* Get the profile handlers for a user.
*
* @param user The user object.
* @param courseId The course ID.
* @param context Context.
* @param contextId Context ID.
* @return Resolved with the handlers.
*/
getProfileHandlersFor(user: CoreUserProfile, courseId?: number): Subject<CoreUserProfileHandlerToDisplay[]> {
// Initialize the user handlers if it isn't initialized already.
if (!this.userHandlers[user.id]) {
this.userHandlers[user.id] = {
loaded: false,
handlers: [],
observable: new BehaviorSubject<CoreUserProfileHandlerToDisplay[]>([]),
};
}
getProfileHandlersFor(
user: CoreUserProfile,
context: CoreUserDelegateContext,
contextId?: number,
): Subject<CoreUserProfileHandlerToDisplay[]> {
this.calculateUserHandlers(user, context, contextId);
this.calculateUserHandlers(user, courseId);
return this.userHandlers[user.id].observable;
return this.getHandlersData(user.id, context, contextId).observable;
}
/**
* Get the profile handlers for a user.
*
* @param user The user object.
* @param courseId The course ID.
* @param context Context.
* @param contextId Context ID.
* @return Promise resolved when done.
*/
protected async calculateUserHandlers(user: CoreUserProfile, courseId?: number): Promise<void> {
protected async calculateUserHandlers(
user: CoreUserProfile,
context: CoreUserDelegateContext,
contextId?: number,
): Promise<void> {
// Get course options.
const courses = await CoreCourses.getUserCourses(true);
const courseIds = courses.map((course) => course.id);
const options = await CoreCourses.getCoursesAdminAndNavOptions(courseIds);
// For backwards compatibility we don't modify the courseId.
const courseIdForOptions = courseId || CoreSites.getCurrentSiteHomeId();
const courseId = context === CoreUserDelegateContext.COURSE && contextId ? contextId : CoreSites.getCurrentSiteHomeId();
const navOptions = options.navOptions[courseIdForOptions];
const admOptions = options.admOptions[courseIdForOptions];
const navOptions = options.navOptions[courseId];
const admOptions = options.admOptions[courseId];
const userData = this.userHandlers[user.id];
userData.handlers = [];
const handlersData = this.getHandlersData(user.id, context, contextId);
handlersData.handlers = [];
await CoreUtils.allPromises(Object.keys(this.enabledHandlers).map(async (name) => {
// Checks if the handler is enabled for the user.
const handler = this.handlers[name];
try {
const enabled = await this.getAndCacheEnabledForUserFromHandler(handler, user, courseId, navOptions, admOptions);
const enabled = await this.getAndCacheEnabledForUserFromHandler(
handler,
user,
context,
courseId,
navOptions,
admOptions,
);
if (enabled) {
userData.handlers.push({
handlersData.handlers.push({
name: name,
data: handler.getDisplayData(user, courseId),
data: handler.getDisplayData(user, context, courseId),
priority: handler.priority || 0,
type: handler.type || CoreUserDelegateService.TYPE_NEW_PAGE,
});
@ -326,9 +337,9 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
}));
// Sort them by priority.
userData.handlers.sort((a, b) => b.priority! - a.priority!);
userData.loaded = true;
userData.observable.next(userData.handlers);
handlersData.handlers.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
handlersData.loaded = true;
handlersData.observable.next(handlersData.handlers);
}
/**
@ -336,7 +347,8 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
*
* @param handler Handler object.
* @param user User object.
* @param courseId Course ID where to show.
* @param context Context.
* @param contextId Context ID.
* @param navOptions Navigation options for the course.
* @param admOptions Admin options for the course.
* @return Whether or not the handler is enabled for a user.
@ -344,12 +356,13 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
protected async getAndCacheEnabledForUserFromHandler(
handler: CoreUserProfileHandler,
user: CoreUserProfile,
courseId?: number,
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
admOptions?: CoreCourseUserAdminOrNavOptionIndexed,
context: CoreUserDelegateContext,
contextId: number,
navOptions: CoreCourseUserAdminOrNavOptionIndexed = {},
admOptions: CoreCourseUserAdminOrNavOptionIndexed = {},
): Promise<boolean> {
if (handler.isEnabledForCourse) {
const enabledOnCourse = await handler.isEnabledForCourse(courseId, navOptions, admOptions);
if (handler.isEnabledForContext) {
const enabledOnCourse = await handler.isEnabledForContext(context, contextId, navOptions, admOptions);
if (!enabledOnCourse) {
// If is not enabled in the course, is not enabled for the user.
@ -364,14 +377,14 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
return true;
}
return handler.isEnabledForUser(user, courseId);
return handler.isEnabledForUser(user, context, contextId);
}
if (this.enabledForUserCache[handler.name] === undefined) {
this.enabledForUserCache[handler.name] = {};
}
const cacheKey = this.getCacheKey(courseId, user.id);
const cacheKey = this.getCacheKey(user.id, context, contextId);
const cache = this.enabledForUserCache[handler.name][cacheKey];
if (cache !== undefined) {
@ -380,7 +393,7 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
let enabled = true; // Default value.
if (handler.isEnabledForUser) {
enabled = await handler.isEnabledForUser(user, courseId);
enabled = await handler.isEnabledForUser(user, context, contextId);
}
this.enabledForUserCache[handler.name][cacheKey] = enabled;
@ -390,14 +403,15 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
/**
* Clear handler enabled for user cache.
* If a courseId and userId are specified, it will only delete the entry for that user and course.
* If a userId and context are specified, it will only delete the entry for that user and context.
*
* @param courseId Course ID.
* @param userId User ID.
* @param context Context.
* @param contextId Context ID.
*/
protected clearHandlerCache(courseId?: number, userId?: number): void {
if (courseId && userId) {
const cacheKey = this.getCacheKey(courseId, userId);
protected clearHandlerCache(userId?: number, context?: CoreUserDelegateContext, contextId?: number): void {
if (userId && context) {
const cacheKey = this.getCacheKey(userId, context, contextId);
Object.keys(this.enabledHandlers).forEach((name) => {
const cache = this.enabledForUserCache[name];
@ -412,25 +426,81 @@ export class CoreUserDelegateService extends CoreDelegate<CoreUserProfileHandler
}
/**
* Get a cache key to identify a course and a user.
* Get a cache key to identify a user and context.
*
* @param courseId Course ID.
* @param userId User ID.
* @param context Context.
* @param contextId Context ID.
* @return Cache key.
*/
protected getCacheKey(courseId = 0, userId = 0): string {
return courseId + '#' + userId;
protected getCacheKey(userId: number, context: CoreUserDelegateContext, contextId?: number): string {
return `${userId}#${this.getContextKey(context, contextId)}`;
}
/**
* Get a string to identify a context.
*
* @param context Context.
* @param contextId Context ID.
* @return String to identify the context.
*/
protected getContextKey(context: CoreUserDelegateContext, contextId?: number): string {
return `${context}#${contextId ?? 0}`;
}
/**
* Get handlers data for a user and context.
*
* @param userId User ID.
* @param context Context.
* @param contextId Context ID.
* @return Handlers data.
*/
protected getHandlersData(userId: number, context: CoreUserDelegateContext, contextId?: number): CoreUserDelegateHandlersData {
// Initialize the data if it doesn't exist.
const contextKey = this.getContextKey(context, contextId);
this.userHandlers[userId] = this.userHandlers[userId] || {};
if (!this.userHandlers[userId][contextKey]) {
this.userHandlers[userId][contextKey] = {
loaded: false,
handlers: [],
observable: new BehaviorSubject<CoreUserProfileHandlerToDisplay[]>([]),
};
}
return this.userHandlers[userId][contextKey];
}
}
export const CoreUserDelegate = makeSingleton(CoreUserDelegateService);
/**
* Handlers data for a user and context.
*/
type CoreUserDelegateHandlersData = {
loaded: boolean; // Whether the handlers are loaded.
handlers: CoreUserProfileHandlerToDisplay[]; // List of handlers.
observable: Subject<CoreUserProfileHandlerToDisplay[]>; // Observable to notify the handlers.
};
/**
* Context levels enumeration.
*/
export enum CoreUserDelegateContext {
SITE = 'site',
COURSE = 'course',
USER_MENU = 'user_menu',
}
/**
* Data passed to UPDATE_HANDLER_EVENT event.
*/
export type CoreUserUpdateHandlerData = {
handler: string; // Name of the handler.
userId: number; // User affected.
context: CoreUserDelegateContext; // Context affected.
contextId?: number; // ID related to the context.
data: Record<string, unknown>; // Data to set to the handler.
};

View File

@ -11,6 +11,10 @@ information provided here is intended especially for developers.
- CoreCourse.getModuleBasicInfoByInstance and CoreCourse.getModuleBasicInfo have been modified to accept an "options" parameter instead of only siteId.
- The function CoreFilepool.isFileDownloadingByUrl now returns Promise<boolean> instead of relying on resolve/reject.
- downloadEnabled input has been removed from CoreBlockSideBlocksComponent, CoreCourseFormatComponent, CoreCourseFormatSingleActivityComponent and CoreCourseModuleComponent.
- There were several breaking changes done in CoreUserDelegate and its handlers:
The function CoreUserDelegate.getProfileHandlersFor must now receive a context + contextId instead of a courseId.
The user handler function isEnabledForCourse is now called isEnabledForContext and receives a context + contextId instead of a courseId.
Some user handler's functions have also changed to accept context + contextId instead of a courseId: isEnabledForUser, getDisplayData, action.
=== 3.9.5 ===