commit
						831f4f0d9e
					
				| @ -14,8 +14,14 @@ | ||||
| 
 | ||||
| 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 { CoreSites } from '@services/sites'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { AddonBadges } from '../badges'; | ||||
| 
 | ||||
| @ -25,52 +31,58 @@ import { AddonBadges } from '../badges'; | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonBadgesUserHandlerService implements CoreUserProfileHandler { | ||||
| 
 | ||||
|     name = 'AddonBadges'; | ||||
|     priority = 50; | ||||
|     name = 'AddonBadges:fakename'; // This name doesn't match any disabled feature, they'll be checked in isEnabledForContext.
 | ||||
|     priority = 300; | ||||
|     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> { | ||||
|         // Check if feature is disabled.
 | ||||
|         const currentSite = CoreSites.getCurrentSite(); | ||||
|         if (!currentSite) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (context === CoreUserDelegateContext.USER_MENU) { | ||||
|             if (currentSite.isFeatureDisabled('CoreUserDelegate_AddonBadges:account')) { | ||||
|                 return false; | ||||
|             } | ||||
|         } else if (currentSite.isFeatureDisabled('CoreUserDelegate_AddonBadges')) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (navOptions && navOptions.badges !== undefined) { | ||||
|             return navOptions.badges; | ||||
|         } | ||||
| 
 | ||||
|         // If we reach here, it means we are opening the user site profile.
 | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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 }, | ||||
|                 }); | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
| @ -13,8 +13,14 @@ | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { CoreUserProfileHandler, CoreUserProfileHandlerData, CoreUserDelegateService } from '@features/user/services/user-delegate'; | ||||
| import { | ||||
|     CoreUserProfileHandler, | ||||
|     CoreUserProfileHandlerData, | ||||
|     CoreUserDelegateService, | ||||
|     CoreUserDelegateContext, | ||||
| } from '@features/user/services/user-delegate'; | ||||
| import { CoreNavigator } from '@services/navigator'; | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { AddonBlog } from '../blog'; | ||||
| 
 | ||||
| @ -24,8 +30,8 @@ import { AddonBlog } from '../blog'; | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonBlogUserHandlerService implements CoreUserProfileHandler { | ||||
| 
 | ||||
|     name = 'AddonBlog:blogs'; | ||||
|     priority = 300; | ||||
|     name = 'AddonBlog'; // This name doesn't match any disabled feature, they'll be checked in isEnabledForContext.
 | ||||
|     priority = 200; | ||||
|     type = CoreUserDelegateService.TYPE_NEW_PAGE; | ||||
| 
 | ||||
|     /** | ||||
| @ -35,6 +41,27 @@ export class AddonBlogUserHandlerService implements CoreUserProfileHandler { | ||||
|         return AddonBlog.isPluginEnabled(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async isEnabledForContext(context: CoreUserDelegateContext): Promise<boolean> { | ||||
|         // Check if feature is disabled.
 | ||||
|         const currentSite = CoreSites.getCurrentSite(); | ||||
|         if (!currentSite) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (context === CoreUserDelegateContext.USER_MENU) { | ||||
|             if (currentSite.isFeatureDisabled('CoreUserDelegate_AddonBlog:account')) { | ||||
|                 return false; | ||||
|             } | ||||
|         } else if (currentSite.isFeatureDisabled('CoreUserDelegate_AddonBlog:blogs')) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
| @ -43,11 +70,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 }, | ||||
|                 }); | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
| @ -26,7 +26,7 @@ export class AddonCalendarMainMenuHandlerService implements CoreMainMenuHandler | ||||
|     static readonly PAGE_NAME = 'calendar'; | ||||
| 
 | ||||
|     name = 'AddonCalendar'; | ||||
|     priority = 800; | ||||
|     priority = 550; | ||||
| 
 | ||||
|     /** | ||||
|      * Check if the handler is enabled on a site level. | ||||
|  | ||||
| @ -16,9 +16,15 @@ 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 { CoreSites } from '@services/sites'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { AddonCompetency } from '../competency'; | ||||
| 
 | ||||
| @ -28,8 +34,8 @@ import { AddonCompetency } from '../competency'; | ||||
| @Injectable( { providedIn: 'root' }) | ||||
| export class AddonCompetencyUserHandlerService implements CoreUserProfileHandler { | ||||
| 
 | ||||
|     name = 'AddonCompetency:learningPlan'; | ||||
|     priority = 900; | ||||
|     name = 'AddonCompetency'; // This name doesn't match any disabled feature, they'll be checked in isEnabledForContext.
 | ||||
|     priority = 100; | ||||
|     type = CoreUserDelegateService.TYPE_NEW_PAGE; | ||||
|     cacheEnabled = true; | ||||
| 
 | ||||
| @ -43,10 +49,32 @@ export class AddonCompetencyUserHandlerService implements CoreUserProfileHandler | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async isEnabledForUser(user: CoreUserProfile, courseId?: number): Promise<boolean> { | ||||
|     async isEnabledForContext(context: CoreUserDelegateContext): Promise<boolean> { | ||||
|         // Check if feature is disabled.
 | ||||
|         const currentSite = CoreSites.getCurrentSite(); | ||||
|         if (!currentSite) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (context === CoreUserDelegateContext.USER_MENU) { | ||||
|             // This option used to belong to main menu, check the original disabled feature value.
 | ||||
|             if (currentSite.isFeatureDisabled('CoreMainMenuDelegate_AddonCompetency')) { | ||||
|                 return false; | ||||
|             } | ||||
|         } else if (currentSite.isFeatureDisabled('CoreUserDelegate_AddonCompetency:learningPlan')) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     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 +89,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 +104,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('/'), | ||||
|                 ); | ||||
|             }, | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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'; | ||||
| @ -25,9 +30,9 @@ import { AddonCourseCompletion } from '../coursecompletion'; | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonCourseCompletionUserHandlerService implements CoreUserProfileHandler { | ||||
| 
 | ||||
|     name = 'AddonCourseCompletion'; | ||||
|     name = 'AddonCourseCompletion:viewCompletion'; | ||||
|     type = CoreUserDelegateService.TYPE_NEW_PAGE; | ||||
|     priority = 200; | ||||
|     priority = 350; | ||||
|     cacheEnabled = true; | ||||
| 
 | ||||
|     /** | ||||
| @ -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 }, | ||||
|                 }); | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
| @ -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(); | ||||
|  | ||||
| @ -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'; | ||||
| @ -27,7 +32,7 @@ import { AddonNotes } from '../notes'; | ||||
| export class AddonNotesUserHandlerService implements CoreUserProfileHandler { | ||||
| 
 | ||||
|     name = 'AddonNotes:notes'; | ||||
|     priority = 100; | ||||
|     priority = 250; | ||||
|     type = CoreUserDelegateService.TYPE_NEW_PAGE; | ||||
|     cacheEnabled = true; | ||||
| 
 | ||||
| @ -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 }, | ||||
|                 }); | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
| @ -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'; | ||||
| @ -30,7 +35,7 @@ export class AddonPrivateFilesUserHandlerService implements CoreUserProfileHandl | ||||
|     static readonly PAGE_NAME = 'private'; | ||||
| 
 | ||||
|     name = 'AddonPrivateFiles'; | ||||
|     priority = 300; | ||||
|     priority = 400; | ||||
|     type = CoreUserDelegateService.TYPE_NEW_PAGE; | ||||
|     cacheEnabled = true; | ||||
| 
 | ||||
| @ -44,9 +49,28 @@ export class AddonPrivateFilesUserHandlerService implements CoreUserProfileHandl | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async isEnabledForUser(user: CoreUserProfile): Promise<boolean> { | ||||
|     async isEnabledForContext(context: CoreUserDelegateContext): Promise<boolean> { | ||||
|         // Private files only available in user menu.
 | ||||
|         if (context !== CoreUserDelegateContext.USER_MENU) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         // Check if feature is disabled.
 | ||||
|         const currentSite = CoreSites.getCurrentSite(); | ||||
|         if (!currentSite) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         // This option used to belong to main menu, check the original disabled feature value.
 | ||||
|         return !currentSite.isFeatureDisabled('CoreMainMenuDelegate_AddonPrivateFiles'); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async isEnabledForUser(user: CoreUserProfile, context: CoreUserDelegateContext): Promise<boolean> { | ||||
|         // Private files only available for the current user.
 | ||||
|         return user.id == CoreSites.getCurrentSiteUserId(); | ||||
|         return user.id == CoreSites.getCurrentSiteUserId() && context === CoreUserDelegateContext.USER_MENU; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -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, | ||||
| @ -35,8 +36,8 @@ import { makeSingleton } from '@singletons'; | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class CoreGradesUserHandlerService implements CoreUserProfileHandler { | ||||
| 
 | ||||
|     name = 'CoreGrades:viewGrades'; | ||||
|     priority = 400; | ||||
|     name = 'CoreGrades'; // This name doesn't match any disabled feature, they'll be checked in isEnabledForContext.
 | ||||
|     priority = 500; | ||||
|     type = CoreUserDelegateService.TYPE_NEW_PAGE; | ||||
|     cacheEnabled = true; | ||||
| 
 | ||||
| @ -50,8 +51,23 @@ export class CoreGradesUserHandlerService implements CoreUserProfileHandler { | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     async isEnabledForCourse(courseId?: number): Promise<boolean> { | ||||
|         if (courseId) { | ||||
|     async isEnabledForContext(context: CoreUserDelegateContext, courseId: number): Promise<boolean> { | ||||
|         // Check if feature is disabled.
 | ||||
|         const currentSite = CoreSites.getCurrentSite(); | ||||
|         if (!currentSite) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (context === CoreUserDelegateContext.USER_MENU) { | ||||
|             // This option used to belong to main menu, check the original disabled feature value.
 | ||||
|             if (currentSite.isFeatureDisabled('CoreMainMenuDelegate_CoreGrades')) { | ||||
|                 return false; | ||||
|             } | ||||
|         } else if (currentSite.isFeatureDisabled('CoreUserDelegate_CoreGrades:viewGrades')) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (context === CoreUserDelegateContext.COURSE) { | ||||
|             return CoreUtils.ignoreErrors(CoreGrades.isPluginEnabledForCourse(courseId), false); | ||||
|         } else { | ||||
|             return CoreGrades.isCourseGradesEnabled(); | ||||
| @ -61,9 +77,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 +89,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('/'), | ||||
|                     ); | ||||
|                 }, | ||||
|             }; | ||||
|  | ||||
| @ -68,7 +68,7 @@ | ||||
|     </ion-list> | ||||
| </ion-content> | ||||
| <ion-footer> | ||||
|     <ion-item button lines="full" (click)="switchAccounts($event)" detail="true" class="ion-text-wrap"> | ||||
|     <ion-item *ngIf="displaySwitchAccount" button lines="full" (click)="switchAccounts($event)" detail="true" class="ion-text-wrap"> | ||||
|         <ion-icon name="fas-exchange-alt" slot="start" aria-hidden="true"></ion-icon> | ||||
|         <ion-label> | ||||
|             <p class="item-heading">{{ 'core.mainmenu.switchaccount' | translate }}</p> | ||||
|  | ||||
| @ -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'; | ||||
| @ -44,6 +49,7 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy { | ||||
|     handlersLoaded = false; | ||||
|     loaded = false; | ||||
|     user?: CoreUserProfile; | ||||
|     displaySwitchAccount = true; | ||||
| 
 | ||||
|     protected subscription!: Subscription; | ||||
| 
 | ||||
| @ -55,6 +61,7 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy { | ||||
|         this.siteInfo = currentSite.getInfo(); | ||||
|         this.siteName = currentSite.getSiteName(); | ||||
|         this.siteUrl = currentSite.getURL(); | ||||
|         this.displaySwitchAccount = !currentSite.isFeatureDisabled('NoDelegate_SwitchAccount'); | ||||
| 
 | ||||
|         this.loaded = true; | ||||
| 
 | ||||
| @ -64,20 +71,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 +158,7 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy { | ||||
| 
 | ||||
|         await this.close(event); | ||||
| 
 | ||||
|         handler.action(event, this.user); | ||||
|         handler.action(event, this.user, CoreUserDelegateContext.USER_MENU); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -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)); | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | ||||
| @ -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(); | ||||
| 
 | ||||
|  | ||||
| @ -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.
 | ||||
| }; | ||||
|  | ||||
| @ -67,6 +67,7 @@ export class CoreTextUtilsProvider { | ||||
|         { old: /files_sitefiles/g, new: 'AddonPrivateFilesSiteFiles' }, | ||||
|         { old: /files_upload/g, new: 'AddonPrivateFilesUpload' }, | ||||
|         { old: /_mmaModAssign/g, new: '_AddonModAssign' }, | ||||
|         { old: /_mmaModBigbluebuttonbn/g, new: '_AddonModBBB' }, | ||||
|         { old: /_mmaModBook/g, new: '_AddonModBook' }, | ||||
|         { old: /_mmaModChat/g, new: '_AddonModChat' }, | ||||
|         { old: /_mmaModChoice/g, new: '_AddonModChoice' }, | ||||
| @ -89,6 +90,7 @@ export class CoreTextUtilsProvider { | ||||
|         { old: /_mmaModWiki/g, new: '_AddonModWiki' }, | ||||
|         { old: /_mmaModWorkshop/g, new: '_AddonModWorkshop' }, | ||||
|         { old: /remoteAddOn_/g, new: 'sitePlugin_' }, | ||||
|         { old: /AddonNotes:addNote/g, new: 'AddonNotes:notes' }, | ||||
|     ]; | ||||
| 
 | ||||
|     protected template: HTMLTemplateElement = document.createElement('template'); // A template element to convert HTML to element.
 | ||||
|  | ||||
| @ -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 === | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user