From 453d97135947c083eed3f06b1e67f2604c77ac60 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 7 Jan 2019 09:49:53 +0100 Subject: [PATCH 1/3] MOBILE-2795 lesson: Prioritize prevent access reasons --- .../mod/lesson/components/index/index.ts | 21 +++++++++--- src/addon/mod/lesson/pages/player/player.ts | 5 +-- src/addon/mod/lesson/providers/lesson.ts | 32 +++++++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/addon/mod/lesson/components/index/index.ts b/src/addon/mod/lesson/components/index/index.ts index 34170064f..015434ce0 100644 --- a/src/addon/mod/lesson/components/index/index.ts +++ b/src/addon/mod/lesson/components/index/index.ts @@ -133,6 +133,7 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo this.accessInfo = info; this.canManage = info.canmanage; this.canViewReports = info.canviewreports; + this.preventMessages = []; if (this.lessonProvider.isLessonOffline(this.lesson)) { // Handle status. @@ -162,7 +163,8 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo } if (info.preventaccessreasons && info.preventaccessreasons.length) { - const askPassword = info.preventaccessreasons.length == 1 && this.lessonProvider.isPasswordProtected(info); + let preventReason = this.lessonProvider.getPreventAccessReason(info, false); + const askPassword = preventReason.reason == 'passwordprotectedlesson'; if (askPassword) { // The lesson requires a password. Check if there is one in memory or DB. @@ -171,15 +173,21 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo promises.push(promise.then((password) => { return this.validatePassword(password); + }).then(() => { + // Now that we have the password, get the access reason again ignoring the password. + preventReason = this.lessonProvider.getPreventAccessReason(info, true); + if (preventReason) { + this.preventMessages = [preventReason]; + } }).catch(() => { // No password or the validation failed. Show password form. this.askPassword = true; - this.preventMessages = info.preventaccessreasons; + this.preventMessages = [preventReason]; lessonReady = false; })); } else { // Lesson cannot be started. - this.preventMessages = info.preventaccessreasons; + this.preventMessages = [preventReason]; lessonReady = false; } } @@ -293,7 +301,6 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo */ protected lessonReady(refresh?: boolean): void { this.askPassword = false; - this.preventMessages = []; this.leftDuringTimed = this.hasOffline || this.lessonProvider.leftDuringTimed(this.accessInfo); if (this.password) { @@ -524,6 +531,12 @@ export class AddonModLessonIndexComponent extends CoreCourseModuleMainActivityCo // Password validated. this.lessonReady(false); + // Now that we have the password, get the access reason again ignoring the password. + const preventReason = this.lessonProvider.getPreventAccessReason(this.accessInfo, true); + if (preventReason) { + this.preventMessages = [preventReason]; + } + // Log view now that we have the password. this.logView(); }).catch((error) => { diff --git a/src/addon/mod/lesson/pages/player/player.ts b/src/addon/mod/lesson/pages/player/player.ts index 01fd5b4bf..c40f6ed8c 100644 --- a/src/addon/mod/lesson/pages/player/player.ts +++ b/src/addon/mod/lesson/pages/player/player.ts @@ -245,9 +245,10 @@ export class AddonModLessonPlayerPage implements OnInit, OnDestroy { if (info.preventaccessreasons && info.preventaccessreasons.length) { // If it's a password protected lesson and we have the password, allow playing it. - if (!this.password || info.preventaccessreasons.length > 1 || !this.lessonProvider.isPasswordProtected(info)) { + const preventReason = this.lessonProvider.getPreventAccessReason(info, !!this.password); + if (preventReason) { // Lesson cannot be played, show message and go back. - return Promise.reject(info.preventaccessreasons[0].message); + return Promise.reject(preventReason.message); } } diff --git a/src/addon/mod/lesson/providers/lesson.ts b/src/addon/mod/lesson/providers/lesson.ts index 96578dad8..dd15ed778 100644 --- a/src/addon/mod/lesson/providers/lesson.ts +++ b/src/addon/mod/lesson/providers/lesson.ts @@ -2335,6 +2335,38 @@ export class AddonModLessonProvider { return this.ROOT_CACHE_KEY + 'userRetake:' + lessonId; } + /** + * Get the prevent access reason to display for a certain lesson. + * + * @param {any} info Lesson access info. + * @param {boolean} [ignorePassword] Whether password protected reason should be ignored (user already entered the password). + * @return {any} Prevent access reason. + */ + getPreventAccessReason(info: any, ignorePassword?: boolean): any { + let result; + + if (info && info.preventaccessreasons) { + for (let i = 0; i < info.preventaccessreasons.length; i++) { + const entry = info.preventaccessreasons[i]; + + if (entry.reason == 'lessonopen' || entry.reason == 'lessonclosed') { + // Time restrictions are the most prioritary, return it. + return entry; + } else if (entry.reason == 'passwordprotectedlesson') { + if (!ignorePassword) { + // Treat password before all other reasons. + result = entry; + } + } else if (!result) { + // Rest of cases, just return any of them. + result = entry; + } + } + } + + return result; + } + /** * Check if a jump is correct. * Based in Moodle's jumpto_is_correct. From 13872e2209ec1865a99a0028b3feb0d2333fff21 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 7 Jan 2019 11:17:17 +0100 Subject: [PATCH 2/3] MOBILE-2795 mainmenu: Fix tab loaded without params --- src/core/mainmenu/pages/menu/menu.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/mainmenu/pages/menu/menu.ts b/src/core/mainmenu/pages/menu/menu.ts index 4bd7b97d5..ae3f68830 100644 --- a/src/core/mainmenu/pages/menu/menu.ts +++ b/src/core/mainmenu/pages/menu/menu.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnDestroy, ViewChild } from '@angular/core'; +import { Component, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core'; import { IonicPage, NavController, NavParams } from 'ionic-angular'; import { CoreSitesProvider } from '@providers/sites'; import { CoreEventsProvider } from '@providers/events'; @@ -42,7 +42,7 @@ export class CoreMainMenuPage implements OnDestroy { @ViewChild('mainTabs') mainTabs: CoreIonTabsComponent; constructor(private menuDelegate: CoreMainMenuDelegate, private sitesProvider: CoreSitesProvider, navParams: NavParams, - private navCtrl: NavController, private eventsProvider: CoreEventsProvider) { + private navCtrl: NavController, private eventsProvider: CoreEventsProvider, private cdr: ChangeDetectorRef) { // Check if the menu was loaded with a redirect. const redirectPage = navParams.get('redirectPage'); @@ -138,6 +138,9 @@ export class CoreMainMenuPage implements OnDestroy { this.redirectParams = data.redirectParams; } + // Force change detection, otherwise sometimes the tab was selected before the params were applied. + this.cdr.detectChanges(); + setTimeout(() => { // Let the tab load the params before navigating. this.mainTabs.selectTabRootByIndex(i + 1); From 49d283c22da721b49e172c87c7afa21510cc5d89 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 7 Jan 2019 13:19:02 +0100 Subject: [PATCH 3/3] MOBILE-2795 core: Allow setting several dismiss callbacks to alerts --- src/classes/site.ts | 4 +++- src/core/login/providers/helper.ts | 4 +++- src/providers/utils/dom.ts | 37 ++++++++++++++++++++++++++---- upgrade.txt | 1 + 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/classes/site.ts b/src/classes/site.ts index 18089cc1e..08b3512da 100644 --- a/src/classes/site.ts +++ b/src/classes/site.ts @@ -1273,7 +1273,9 @@ export class CoreSite { return this.domUtils.showAlert(this.translate.instant('core.notice'), alertMessage, undefined, 3000).then((alert) => { return new Promise((resolve, reject): void => { - alert.onDidDismiss(() => { + const subscription = alert.didDismiss.subscribe(() => { + subscription && subscription.unsubscribe(); + if (inApp) { resolve(this.utils.openInApp(url, options)); } else { diff --git a/src/core/login/providers/helper.ts b/src/core/login/providers/helper.ts index 2f0dc2d78..9cebad238 100644 --- a/src/core/login/providers/helper.ts +++ b/src/core/login/providers/helper.ts @@ -697,7 +697,9 @@ export class CoreLoginHelperProvider { */ openChangePassword(siteUrl: string, error: string): void { this.domUtils.showAlert(this.translate.instant('core.notice'), error, undefined, 3000).then((alert) => { - alert.onDidDismiss(() => { + const subscription = alert.didDismiss.subscribe(() => { + subscription && subscription.unsubscribe(); + this.utils.openInApp(siteUrl + '/login/change_password.php'); }); }); diff --git a/src/providers/utils/dom.ts b/src/providers/utils/dom.ts index faf948769..f4f2bbf62 100644 --- a/src/providers/utils/dom.ts +++ b/src/providers/utils/dom.ts @@ -25,6 +25,24 @@ import { CoreConfigProvider } from '../config'; import { CoreUrlUtilsProvider } from './url'; import { CoreConstants } from '@core/constants'; import { Md5 } from 'ts-md5/dist/md5'; +import { Subject } from 'rxjs'; + +/** + * Interface that defines an extension of the Ionic Alert class, to support multiple listeners. + */ +export interface CoreAlert extends Alert { + /** + * Observable that will notify when the alert is dismissed. + * @type {Subject<{data: any, role: string}>} + */ + didDismiss: Subject<{data: any, role: string}>; + + /** + * Observable that will notify when the alert will be dismissed. + * @type {Subject<{data: any, role: string}>} + */ + willDismiss: Subject<{data: any, role: string}>; +} /* * "Utils" service with helper functions for UI, DOM elements and HTML code. @@ -886,9 +904,9 @@ export class CoreDomUtilsProvider { * @param {string} message Message to show. * @param {string} [buttonText] Text of the button. * @param {number} [autocloseTime] Number of milliseconds to wait to close the modal. If not defined, modal won't be closed. - * @return {Promise} Promise resolved with the alert modal. + * @return {Promise} Promise resolved with the alert modal. */ - showAlert(title: string, message: string, buttonText?: string, autocloseTime?: number): Promise { + showAlert(title: string, message: string, buttonText?: string, autocloseTime?: number): Promise { const hasHTMLTags = this.textUtils.hasHTMLTags(message); let promise; @@ -907,7 +925,7 @@ export class CoreDomUtilsProvider { return this.displayedAlerts[alertId]; } - const alert = this.alertCtrl.create({ + const alert: CoreAlert = this.alertCtrl.create({ title: title, message: message, buttons: [buttonText || this.translate.instant('core.ok')] @@ -924,8 +942,19 @@ export class CoreDomUtilsProvider { // Store the alert and remove it when dismissed. this.displayedAlerts[alertId] = alert; - alert.onDidDismiss(() => { + // Define the observables to extend the Alert class. This will allow several callbacks instead of just one. + alert.didDismiss = new Subject(); + alert.willDismiss = new Subject(); + + // Set the callbacks to trigger an observable event. + alert.onDidDismiss((data: any, role: string) => { delete this.displayedAlerts[alertId]; + + alert.didDismiss.next({data: data, role: role}); + }); + + alert.onWillDismiss((data: any, role: string) => { + alert.willDismiss.next({data: data, role: role}); }); if (autocloseTime > 0) { diff --git a/upgrade.txt b/upgrade.txt index 1a0b363c3..86e4a9132 100644 --- a/upgrade.txt +++ b/upgrade.txt @@ -8,6 +8,7 @@ information provided here is intended especially for developers. - The value of the constant CoreCourseProvider.ALL_SECTIONS_ID has changed from -1 to -2. - Use of completionstatus on the module object has been deprecated, use completiondata instead. - The function CoreSitesProvider.loadSite has changed, now it will trigger SESSION_EXPIRED event if the site is logged out. Its params and return value have changed. +- When using CoreDomUtils.showAlert, please use alert.didDismiss.subscribe() instead of alert.onDidDismiss(). - The following strings have been deprecated: core.dfdaymonthyear. Please use core.strftimedatefullshort instead. core.dfdayweekmonth. Please use core.strftimedayshort instead.