From 1024f6a96dd152483bdedeb0eea01577550a0620 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 24 Jul 2019 09:00:12 +0200 Subject: [PATCH 1/2] MOBILE-3055 courses: Trigger event if user courses list changes --- src/core/courses/providers/courses.ts | 56 +++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/src/core/courses/providers/courses.ts b/src/core/courses/providers/courses.ts index c50cc9656..c63780c5c 100644 --- a/src/core/courses/providers/courses.ts +++ b/src/core/courses/providers/courses.ts @@ -13,6 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; +import { CoreEventsProvider } from '@providers/events'; import { CoreLoggerProvider } from '@providers/logger'; import { CoreSitesProvider } from '@providers/sites'; import { CoreSite } from '@classes/site'; @@ -24,13 +25,16 @@ import { CoreSite } from '@classes/site'; export class CoreCoursesProvider { static SEARCH_PER_PAGE = 20; static ENROL_INVALID_KEY = 'CoreCoursesEnrolInvalidKey'; - static EVENT_MY_COURSES_UPDATED = 'courses_my_courses_updated'; + static EVENT_MY_COURSES_CHANGED = 'courses_my_courses_changed'; // User course list changed while app is running. + static EVENT_MY_COURSES_UPDATED = 'courses_my_courses_updated'; // A course was hidden/favourite, or user enroled in a course. static EVENT_MY_COURSES_REFRESHED = 'courses_my_courses_refreshed'; static EVENT_DASHBOARD_DOWNLOAD_ENABLED_CHANGED = 'dashboard_download_enabled_changed'; + protected ROOT_CACHE_KEY = 'mmCourses:'; protected logger; + protected userCoursesIds: {[id: number]: boolean}; // Use an object to make it faster to search. - constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider) { + constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private eventsProvider: CoreEventsProvider) { this.logger = logger.getInstance('CoreCoursesProvider'); } @@ -743,7 +747,53 @@ export class CoreCoursesProvider { data.returnusercount = 0; } - return site.read('core_enrol_get_users_courses', data, preSets); + return site.read('core_enrol_get_users_courses', data, preSets).then((courses) => { + if (this.userCoursesIds) { + // Check if the list of courses has changed. + const added = [], + removed = [], + previousIds = Object.keys(this.userCoursesIds), + currentIds = {}; // Use an object to make it faster to search. + + courses.forEach((course) => { + currentIds[course.id] = true; + + if (!this.userCoursesIds[course.id]) { + // Course added. + added.push(course.id); + } + }); + + if (courses.length - added.length != previousIds.length) { + // A course was removed, check which one. + previousIds.forEach((id) => { + if (!currentIds[id]) { + // Course removed. + removed.push(Number(id)); + } + }); + } + + if (added.length || removed.length) { + // At least 1 course was added or removed, trigger the event. + this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_CHANGED, { + added: added, + removed: removed + }, site.getId()); + } + + this.userCoursesIds = currentIds; + } else { + this.userCoursesIds = {}; + + // Store the list of courses. + courses.forEach((course) => { + this.userCoursesIds[course.id] = true; + }); + } + + return courses; + }); }); } From e3944a9e72355c526b959bbb1e05108f995c1608 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 24 Jul 2019 11:53:38 +0200 Subject: [PATCH 2/2] MOBILE-3055 core: Update site plugins init if course added --- src/core/course/providers/options-delegate.ts | 14 ++-- .../classes/handlers/course-option-handler.ts | 34 +++++++- .../classes/handlers/user-handler.ts | 25 +++++- src/core/siteplugins/providers/helper.ts | 82 +++++++++++++++++-- src/providers/events.ts | 1 + 5 files changed, 136 insertions(+), 20 deletions(-) diff --git a/src/core/course/providers/options-delegate.ts b/src/core/course/providers/options-delegate.ts index b99cc7760..8f0ac6124 100644 --- a/src/core/course/providers/options-delegate.ts +++ b/src/core/course/providers/options-delegate.ts @@ -552,16 +552,12 @@ export class CoreCourseOptionsDelegate extends CoreDelegate { /** * Update handlers for each course. - * - * @param {string} [siteId] Site ID. */ - updateData(siteId?: string): void { - if (this.sitesProvider.getCurrentSiteId() === siteId) { - // Update handlers for all courses. - for (const courseId in this.coursesHandlers) { - const handler = this.coursesHandlers[courseId]; - this.updateHandlersForCourse(parseInt(courseId, 10), handler.access, handler.navOptions, handler.admOptions); - } + updateData(): void { + // Update handlers for all courses. + for (const courseId in this.coursesHandlers) { + const handler = this.coursesHandlers[courseId]; + this.updateHandlersForCourse(parseInt(courseId, 10), handler.access, handler.navOptions, handler.admOptions); } } diff --git a/src/core/siteplugins/classes/handlers/course-option-handler.ts b/src/core/siteplugins/classes/handlers/course-option-handler.ts index e918ecf11..e3fde2bd2 100644 --- a/src/core/siteplugins/classes/handlers/course-option-handler.ts +++ b/src/core/siteplugins/classes/handlers/course-option-handler.ts @@ -19,6 +19,7 @@ import { } from '@core/course/providers/options-delegate'; import { CoreSitePluginsBaseHandler } from './base-handler'; import { CoreSitePluginsCourseOptionComponent } from '../../components/course-option/course-option'; +import { CoreUtilsProvider, PromiseDefer } from '@providers/utils/utils'; /** * Handler to display a site plugin in course options. @@ -27,8 +28,11 @@ export class CoreSitePluginsCourseOptionHandler extends CoreSitePluginsBaseHandl priority: number; isMenuHandler: boolean; + protected updatingDefer: PromiseDefer; + constructor(name: string, protected title: string, protected plugin: any, protected handlerSchema: any, - protected initResult: any, protected sitePluginsProvider: CoreSitePluginsProvider) { + protected initResult: any, protected sitePluginsProvider: CoreSitePluginsProvider, + protected utils: CoreUtilsProvider) { super(name); this.priority = handlerSchema.priority; @@ -45,8 +49,13 @@ export class CoreSitePluginsCourseOptionHandler extends CoreSitePluginsBaseHandl * @return {boolean|Promise} True or promise resolved with true if enabled. */ isEnabledForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): boolean | Promise { - return this.sitePluginsProvider.isHandlerEnabledForCourse( - courseId, this.handlerSchema.restricttoenrolledcourses, this.initResult.restrict); + // Wait for "init" result to be updated. + const promise = this.updatingDefer ? this.updatingDefer.promise : Promise.resolve(); + + return promise.then(() => { + return this.sitePluginsProvider.isHandlerEnabledForCourse( + courseId, this.handlerSchema.restricttoenrolledcourses, this.initResult.restrict); + }); } /** @@ -108,4 +117,23 @@ export class CoreSitePluginsCourseOptionHandler extends CoreSitePluginsBaseHandl return this.sitePluginsProvider.prefetchFunctions(component, args, this.handlerSchema, course.id, undefined, true); } + + /** + * Set init result. + * + * @param {any} result Result to set. + */ + setInitResult(result: any): void { + this.initResult = result; + + this.updatingDefer.resolve(); + delete this.updatingDefer; + } + + /** + * Mark init being updated. + */ + updatingInit(): void { + this.updatingDefer = this.utils.promiseDefer(); + } } diff --git a/src/core/siteplugins/classes/handlers/user-handler.ts b/src/core/siteplugins/classes/handlers/user-handler.ts index 044fd2317..5bd01b063 100644 --- a/src/core/siteplugins/classes/handlers/user-handler.ts +++ b/src/core/siteplugins/classes/handlers/user-handler.ts @@ -16,6 +16,7 @@ import { NavController } from 'ionic-angular'; import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '@core/user/providers/user-delegate'; import { CoreSitePluginsProvider } from '../../providers/siteplugins'; import { CoreSitePluginsBaseHandler } from './base-handler'; +import { CoreUtilsProvider, PromiseDefer } from '@providers/utils/utils'; /** * Handler to display a site plugin in the user profile. @@ -37,8 +38,11 @@ export class CoreSitePluginsUserProfileHandler extends CoreSitePluginsBaseHandle */ type: string; + protected updatingDefer: PromiseDefer; + constructor(name: string, protected title: string, protected plugin: any, protected handlerSchema: any, - protected initResult: any, protected sitePluginsProvider: CoreSitePluginsProvider) { + protected initResult: any, protected sitePluginsProvider: CoreSitePluginsProvider, + protected utils: CoreUtilsProvider) { super(name); this.priority = handlerSchema.priority; @@ -97,4 +101,23 @@ export class CoreSitePluginsUserProfileHandler extends CoreSitePluginsBaseHandle } }; } + + /** + * Set init result. + * + * @param {any} result Result to set. + */ + setInitResult(result: any): void { + this.initResult = result; + + this.updatingDefer.resolve(); + delete this.updatingDefer; + } + + /** + * Mark init being updated. + */ + updatingInit(): void { + this.updatingDefer = this.utils.promiseDefer(); + } } diff --git a/src/core/siteplugins/providers/helper.ts b/src/core/siteplugins/providers/helper.ts index 2b1242d85..ded88ec73 100644 --- a/src/core/siteplugins/providers/helper.ts +++ b/src/core/siteplugins/providers/helper.ts @@ -30,6 +30,7 @@ import { CoreSitePluginsProvider } from './siteplugins'; import { CoreCompileProvider } from '@core/compile/providers/compile'; import { CoreQuestionProvider } from '@core/question/providers/question'; import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; // Delegates import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; @@ -79,13 +80,14 @@ import { CoreSitePluginsBlockHandler } from '@core/siteplugins/classes/handlers/ @Injectable() export class CoreSitePluginsHelperProvider { protected logger; + protected courseRestrictHandlers = {}; constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider, private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate, private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider, private http: Http, private sitePluginsProvider: CoreSitePluginsProvider, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private compileProvider: CoreCompileProvider, private utils: CoreUtilsProvider, private urlUtils: CoreUrlUtilsProvider, - private courseOptionsDelegate: CoreCourseOptionsDelegate, eventsProvider: CoreEventsProvider, + private courseOptionsDelegate: CoreCourseOptionsDelegate, private eventsProvider: CoreEventsProvider, private courseFormatDelegate: CoreCourseFormatDelegate, private profileFieldDelegate: CoreUserProfileFieldDelegate, private textUtils: CoreTextUtilsProvider, private filepoolProvider: CoreFilepoolProvider, private settingsDelegate: CoreSettingsDelegate, private questionDelegate: CoreQuestionDelegate, @@ -122,6 +124,13 @@ export class CoreSitePluginsHelperProvider { window.location.reload(); } }); + + // Re-load plugins restricted for courses when the list of user courses changes. + eventsProvider.on(CoreCoursesProvider.EVENT_MY_COURSES_CHANGED, (data) => { + if (data && data.siteId && data.siteId == this.sitesProvider.getCurrentSiteId() && data.added && data.added.length) { + this.reloadCourseRestrictHandlers(); + } + }); } /** @@ -375,6 +384,7 @@ export class CoreSitePluginsHelperProvider { */ loadSitePlugins(plugins: any[]): Promise { const promises = []; + this.courseRestrictHandlers = {}; plugins.forEach((plugin) => { const pluginPromise = this.loadSitePlugin(plugin); @@ -683,10 +693,21 @@ export class CoreSitePluginsHelperProvider { // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), - prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title); + prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title), + handler = new CoreSitePluginsCourseOptionHandler(uniqueName, prefixedTitle, plugin, + handlerSchema, initResult, this.sitePluginsProvider, this.utils); - this.courseOptionsDelegate.registerHandler(new CoreSitePluginsCourseOptionHandler(uniqueName, prefixedTitle, plugin, - handlerSchema, initResult, this.sitePluginsProvider)); + this.courseOptionsDelegate.registerHandler(handler); + + if (initResult && initResult.restrict && initResult.restrict.courses) { + // This handler is restricted to certan courses, store it in the list. + this.courseRestrictHandlers[uniqueName] = { + plugin: plugin, + handlerName: handlerName, + handlerSchema: handlerSchema, + handler: handler + }; + } return uniqueName; } @@ -889,10 +910,21 @@ export class CoreSitePluginsHelperProvider { // Create and register the handler. const uniqueName = this.sitePluginsProvider.getHandlerUniqueName(plugin, handlerName), - prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title); + prefixedTitle = this.getPrefixedString(plugin.addon, handlerSchema.displaydata.title), + handler = new CoreSitePluginsUserProfileHandler(uniqueName, prefixedTitle, plugin, handlerSchema, + initResult, this.sitePluginsProvider, this.utils); - this.userDelegate.registerHandler(new CoreSitePluginsUserProfileHandler(uniqueName, prefixedTitle, plugin, handlerSchema, - initResult, this.sitePluginsProvider)); + this.userDelegate.registerHandler(handler); + + if (initResult && initResult.restrict && initResult.restrict.courses) { + // This handler is restricted to certan courses, store it in the list. + this.courseRestrictHandlers[uniqueName] = { + plugin: plugin, + handlerName: handlerName, + handlerSchema: handlerSchema, + handler: handler + }; + } return uniqueName; } @@ -935,4 +967,40 @@ export class CoreSitePluginsHelperProvider { return new CoreSitePluginsWorkshopAssessmentStrategyHandler(uniqueName, strategyName); }); } + + /** + * Reload the handlers that are restricted to certain courses. + * + * @return {Promise} Promise resolved when done. + */ + protected reloadCourseRestrictHandlers(): Promise { + if (!Object.keys(this.courseRestrictHandlers).length) { + // No course restrict handlers, nothing to do. + return Promise.resolve(); + } + + const promises = []; + + for (const name in this.courseRestrictHandlers) { + const data = this.courseRestrictHandlers[name]; + + if (!data.handler || !data.handler.setInitResult) { + // No handler or it doesn't implement a required function, ignore it. + continue; + } + + // Mark the handler as being updated. + data.handler.updatingInit && data.handler.updatingInit(); + + promises.push(this.executeHandlerInit(data.plugin, data.handlerSchema).then((initResult) => { + data.handler.setInitResult(initResult); + }).catch((error) => { + this.logger.error('Error reloading course restrict handler', error, data.plugin); + })); + } + + return Promise.all(promises).then(() => { + this.eventsProvider.trigger(CoreEventsProvider.SITE_PLUGINS_COURSE_RESTRICT_UPDATED, {}); + }); + } } diff --git a/src/providers/events.ts b/src/providers/events.ts index fe75a177a..48f3dcf32 100644 --- a/src/providers/events.ts +++ b/src/providers/events.ts @@ -48,6 +48,7 @@ export class CoreEventsProvider { static COURSE_STATUS_CHANGED = 'course_status_changed'; static SECTION_STATUS_CHANGED = 'section_status_changed'; static SITE_PLUGINS_LOADED = 'site_plugins_loaded'; + static SITE_PLUGINS_COURSE_RESTRICT_UPDATED = 'site_plugins_course_restrict_updated'; static LOGIN_SITE_CHECKED = 'login_site_checked'; static LOGIN_SITE_UNCHECKED = 'login_site_unchecked'; static IAB_LOAD_START = 'inappbrowser_load_start';