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 { Injectable } from '@angular/core';
import { CoreCourseUserAdminOrNavOptionIndexed } from '@features/courses/services/courses'; 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 { CoreNavigator } from '@services/navigator';
import { makeSingleton } from '@singletons'; import { makeSingleton } from '@singletons';
import { AddonBadges } from '../badges'; import { AddonBadges } from '../badges';
@ -30,22 +35,17 @@ export class AddonBadgesUserHandlerService implements CoreUserProfileHandler {
type = CoreUserDelegateService.TYPE_NEW_PAGE; type = CoreUserDelegateService.TYPE_NEW_PAGE;
/** /**
* Check if handler is enabled. * @inheritdoc
*
* @return Always enabled.
*/ */
isEnabled(): Promise<boolean> { isEnabled(): Promise<boolean> {
return AddonBadges.isPluginEnabled(); return AddonBadges.isPluginEnabled();
} }
/** /**
* Check if handler is enabled for this user in this context. * @inheritdoc
*
* @param courseId Course ID.
* @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
* @return True if enabled, false otherwise.
*/ */
async isEnabledForCourse( async isEnabledForContext(
context: CoreUserDelegateContext,
courseId: number, courseId: number,
navOptions?: CoreCourseUserAdminOrNavOptionIndexed, navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
): Promise<boolean> { ): Promise<boolean> {
@ -58,19 +58,17 @@ export class AddonBadgesUserHandlerService implements CoreUserProfileHandler {
} }
/** /**
* Returns the data needed to render the handler. * @inheritdoc
*
* @return Data needed to render the handler.
*/ */
getDisplayData(): CoreUserProfileHandlerData { getDisplayData(): CoreUserProfileHandlerData {
return { return {
icon: 'fas-trophy', icon: 'fas-trophy',
title: 'addon.badges.badges', title: 'addon.badges.badges',
action: (event, user, courseId): void => { action: (event, user, context, contextId): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
CoreNavigator.navigateToSitePath('/badges', { 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', icon: 'far-newspaper',
title: 'addon.blog.blogentries', title: 'addon.blog.blogentries',
class: 'addon-blog-handler', class: 'addon-blog-handler',
action: (event, user, courseId): void => { action: (event, user, context, contextId): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
CoreNavigator.navigateToSitePath('/blog', { 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 { Injectable } from '@angular/core';
import { COURSE_PAGE_NAME } from '@features/course/course.module'; import { COURSE_PAGE_NAME } from '@features/course/course.module';
import { CoreUserProfile } from '@features/user/services/user'; 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 { PARTICIPANTS_PAGE_NAME } from '@features/user/user.module';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { makeSingleton } from '@singletons'; import { makeSingleton } from '@singletons';
@ -43,10 +48,10 @@ export class AddonCompetencyUserHandlerService implements CoreUserProfileHandler
/** /**
* @inheritdoc * @inheritdoc
*/ */
async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise<boolean> { async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): Promise<boolean> {
try { try {
if (courseId) { if (context === CoreUserDelegateContext.COURSE) {
return AddonCompetency.canViewUserCompetenciesInCourse(courseId, user.id); return await AddonCompetency.canViewUserCompetenciesInCourse(contextId, user.id);
} else { } else {
const plans = await AddonCompetency.getLearningPlans(user.id); const plans = await AddonCompetency.getLearningPlans(user.id);
@ -61,21 +66,8 @@ export class AddonCompetencyUserHandlerService implements CoreUserProfileHandler
/** /**
* @inheritdoc * @inheritdoc
*/ */
getDisplayData(user: CoreUserProfile, courseId: number): CoreUserProfileHandlerData { getDisplayData(user: CoreUserProfile, context: CoreUserDelegateContext): CoreUserProfileHandlerData {
if (courseId) { if (context !== CoreUserDelegateContext.COURSE) {
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 {
return { return {
icon: 'fas-route', icon: 'fas-route',
title: 'addon.competency.learningplans', 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 { Injectable } from '@angular/core';
import { CoreUserProfile } from '@features/user/services/user'; 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 { CoreNavigator } from '@services/navigator';
import { makeSingleton } from '@singletons'; import { makeSingleton } from '@singletons';
import { AddonCourseCompletion } from '../coursecompletion'; import { AddonCourseCompletion } from '../coursecompletion';
@ -40,15 +45,19 @@ export class AddonCourseCompletionUserHandlerService implements CoreUserProfileH
/** /**
* @inheritdoc * @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); return AddonCourseCompletion.isPluginViewEnabledForCourse(courseId);
} }
/** /**
* @inheritdoc * @inheritdoc
*/ */
async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise<boolean> { async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): Promise<boolean> {
return await AddonCourseCompletion.isPluginViewEnabledForUser(courseId!, user.id); return await AddonCourseCompletion.isPluginViewEnabledForUser(contextId, user.id);
} }
/** /**
@ -59,11 +68,11 @@ export class AddonCourseCompletionUserHandlerService implements CoreUserProfileH
icon: 'fas-tasks', icon: 'fas-tasks',
title: 'addon.coursecompletion.coursecompletion', title: 'addon.coursecompletion.coursecompletion',
class: 'addon-coursecompletion-handler', class: 'addon-coursecompletion-handler',
action: (event, user, courseId): void => { action: (event, user, context, contextId): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
CoreNavigator.navigateToSitePath('/coursecompletion', { 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; type = CoreUserDelegateService.TYPE_COMMUNICATION;
/** /**
* Check if handler is enabled. * @inheritdoc
*
* @return Promise resolved with true if enabled, rejected or resolved with false otherwise.
*/ */
isEnabled(): Promise<boolean> { isEnabled(): Promise<boolean> {
return AddonMessages.isPluginEnabled(); return AddonMessages.isPluginEnabled();
@ -43,15 +41,12 @@ export class AddonMessagesSendMessageUserHandlerService implements CoreUserProfi
/** /**
* @inheritdoc * @inheritdoc
*/ */
async isEnabledForCourse(): Promise<boolean> { async isEnabledForContext(): Promise<boolean> {
return !!CoreSites.getCurrentSite(); return !!CoreSites.getCurrentSite();
} }
/** /**
* Check if handler is enabled for this user in this context. * @inheritdoc
*
* @param user User to check.
* @return Promise resolved with true if enabled, resolved with false otherwise.
*/ */
async isEnabledForUser(user: CoreUserProfile): Promise<boolean> { async isEnabledForUser(user: CoreUserProfile): Promise<boolean> {
const currentSite = CoreSites.getRequiredCurrentSite(); const currentSite = CoreSites.getRequiredCurrentSite();

View File

@ -14,7 +14,12 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CoreUserProfile } from '@features/user/services/user'; 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 { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { makeSingleton } from '@singletons'; import { makeSingleton } from '@singletons';
@ -41,14 +46,14 @@ export class AddonNotesUserHandlerService implements CoreUserProfileHandler {
/** /**
* @inheritdoc * @inheritdoc
*/ */
async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise<boolean> { async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): Promise<boolean> {
// Active course required. // Active course required.
if (!courseId || user.id == CoreSites.getCurrentSiteUserId()) { if (context !== CoreUserDelegateContext.COURSE || !contextId || user.id == CoreSites.getCurrentSiteUserId()) {
return false; return false;
} }
// We are not using isEnabledForCourse because we need to cache the call. // 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', icon: 'fas-receipt',
title: 'addon.notes.notes', title: 'addon.notes.notes',
class: 'addon-notes-handler', class: 'addon-notes-handler',
action: (event, user, courseId): void => { action: (event, user, context, contextId): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
CoreNavigator.navigateToSitePath('/notes', { 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 { AddonPrivateFiles } from '@/addons/privatefiles/services/privatefiles';
import { makeSingleton } from '@singletons'; 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 { CoreUserProfile } from '@features/user/services/user';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
@ -44,9 +49,9 @@ export class AddonPrivateFilesUserHandlerService implements CoreUserProfileHandl
/** /**
* @inheritdoc * @inheritdoc
*/ */
async isEnabledForUser(user: CoreUserProfile): Promise<boolean> { async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext): Promise<boolean> {
// Private files only available for the current user. // Private files only available for the current user in user menu.
return user.id == CoreSites.getCurrentSiteUserId(); 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 { CoreGrades } from '@features/grades/services/grades';
import { CoreUserProfile } from '@features/user/services/user'; import { CoreUserProfile } from '@features/user/services/user';
import { import {
CoreUserDelegateContext,
CoreUserDelegateService , CoreUserDelegateService ,
CoreUserProfileHandler, CoreUserProfileHandler,
CoreUserProfileHandlerData, CoreUserProfileHandlerData,
@ -50,8 +51,8 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler {
/** /**
* @inheritdoc * @inheritdoc
*/ */
async isEnabledForCourse(courseId?: number): Promise<boolean> { async isEnabledForContext(context: CoreUserDelegateContext, courseId: number): Promise<boolean> {
if (courseId) { if (context === CoreUserDelegateContext.COURSE) {
return CoreUtils.ignoreErrors(CoreGrades.isPluginEnabledForCourse(courseId), false); return CoreUtils.ignoreErrors(CoreGrades.isPluginEnabledForCourse(courseId), false);
} else { } else {
return CoreGrades.isCourseGradesEnabled(); return CoreGrades.isCourseGradesEnabled();
@ -61,9 +62,9 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler {
/** /**
* @inheritdoc * @inheritdoc
*/ */
async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise<boolean> { async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext, contextId: number): Promise<boolean> {
if (courseId) { if (context === CoreUserDelegateContext.COURSE) {
return CoreUtils.promiseWorks(CoreGrades.getCourseGradesTable(courseId, user.id)); return CoreUtils.promiseWorks(CoreGrades.getCourseGradesTable(contextId, user.id));
} }
// All course grades only available for the current user. // All course grades only available for the current user.
@ -73,17 +74,17 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler {
/** /**
* @inheritdoc * @inheritdoc
*/ */
getDisplayData(user: CoreUserProfile, courseId?: number): CoreUserProfileHandlerData { getDisplayData(user: CoreUserProfile, context: CoreUserDelegateContext): CoreUserProfileHandlerData {
if (courseId) { if (context === CoreUserDelegateContext.COURSE) {
return { return {
icon: 'fas-chart-bar', icon: 'fas-chart-bar',
title: 'core.grades.grades', title: 'core.grades.grades',
class: 'core-grades-user-handler', class: 'core-grades-user-handler',
action: (event, user, courseId): void => { action: (event, user, context, contextId): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
CoreNavigator.navigateToSitePath( 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 { CoreLoginSitesComponent } from '@features/login/components/sites/sites';
import { CoreLoginHelper } from '@features/login/services/login-helper'; import { CoreLoginHelper } from '@features/login/services/login-helper';
import { CoreUser, CoreUserProfile } from '@features/user/services/user'; 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 { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites'; import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom'; import { CoreDomUtils } from '@services/utils/dom';
@ -64,20 +69,21 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy {
if (this.siteInfo) { if (this.siteInfo) {
this.user = await CoreUser.getProfile(this.siteInfo.userid); this.user = await CoreUser.getProfile(this.siteInfo.userid);
this.subscription = CoreUserDelegate.getProfileHandlersFor(this.user).subscribe((handlers) => { this.subscription = CoreUserDelegate.getProfileHandlersFor(this.user, CoreUserDelegateContext.USER_MENU)
if (!handlers || !this.user) { .subscribe((handlers) => {
return; if (!handlers || !this.user) {
} return;
this.handlers = [];
handlers.forEach((handler) => {
if (handler.type == CoreUserDelegateService.TYPE_NEW_PAGE) {
this.handlers.push(handler.data);
} }
});
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); 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, CoreSitePluginsUserHandlerData,
} from '@features/siteplugins/services/siteplugins'; } from '@features/siteplugins/services/siteplugins';
import { CoreUserProfile } from '@features/user/services/user'; 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 { CoreNavigator } from '@services/navigator';
import { CoreSites } from '@services/sites';
import { CoreUtils, PromiseDefer } from '@services/utils/utils'; import { CoreUtils, PromiseDefer } from '@services/utils/utils';
import { Md5 } from 'ts-md5'; import { Md5 } from 'ts-md5';
import { CoreSitePluginsBaseHandler } from './base-handler'; import { CoreSitePluginsBaseHandler } from './base-handler';
@ -55,11 +59,7 @@ export class CoreSitePluginsUserProfileHandler extends CoreSitePluginsBaseHandle
/** /**
* @inheritdoc * @inheritdoc
*/ */
async isEnabledForCourse( async isEnabledForContext(context: CoreUserDelegateContext, courseId: number): Promise<boolean> {
courseId?: number,
): Promise<boolean> {
courseId = courseId || CoreSites.getCurrentSiteHomeId();
// Check if it's enabled for the course. // Check if it's enabled for the course.
return CoreSitePlugins.isHandlerEnabledForCourse( return CoreSitePlugins.isHandlerEnabledForCourse(
courseId, courseId,
@ -89,12 +89,12 @@ export class CoreSitePluginsUserProfileHandler extends CoreSitePluginsBaseHandle
title: this.title, title: this.title,
icon: this.handlerSchema.displaydata?.icon, icon: this.handlerSchema.displaydata?.icon,
class: this.handlerSchema.displaydata?.class, class: this.handlerSchema.displaydata?.class,
action: (event: Event, user: CoreUserProfile, courseId?: number): void => { action: (event, user, context, contextId): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
const args = { const args = {
courseid: courseId, courseid: contextId,
userid: user.id, userid: user.id,
}; };
const hash = <string> Md5.hashAsciiStr(JSON.stringify(args)); 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 { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreUser, CoreUserProfile, CoreUserProvider } from '@features/user/services/user'; import { CoreUser, CoreUserProfile, CoreUserProvider } from '@features/user/services/user';
import { CoreUserHelper } from '@features/user/services/user-helper'; 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 { CoreUtils } from '@services/utils/utils';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreCourses } from '@features/courses/services/courses'; 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. // If there's already a subscription, unsubscribe because we'll get a new one.
this.subscription?.unsubscribe(); 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.actionHandlers = [];
this.newPageHandlers = []; this.newPageHandlers = [];
this.communicationHandlers = []; 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) { } catch (error) {
@ -208,7 +215,8 @@ export class CoreUserProfilePage implements OnInit, OnDestroy {
return; 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', icon: 'mail',
title: 'core.user.sendemail', title: 'core.user.sendemail',
class: 'core-user-profile-mail', class: 'core-user-profile-mail',
action: (event: Event, user: CoreUserProfile): void => { action: (event, user): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();

View File

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