From e8f72198839b779580cd50cff6ff0809a691e555 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Thu, 23 May 2019 14:25:25 +0200 Subject: [PATCH] MOBILE-2840 scorm: Launch automatically --- .../index/addon-mod-scorm-index.html | 10 +- src/addon/mod/scorm/components/index/index.ts | 123 +++++++++++------- .../mod/scorm/providers/prefetch-handler.ts | 3 + src/addon/mod/scorm/providers/scorm.ts | 56 ++++++++ .../course/classes/main-activity-component.ts | 8 +- 5 files changed, 148 insertions(+), 52 deletions(-) diff --git a/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html b/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html index 947e4be78..92fb199a2 100644 --- a/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html +++ b/src/addon/mod/scorm/components/index/addon-mod-scorm-index.html @@ -24,7 +24,7 @@
- +

{{ 'addon.mod_scorm.attempts' | translate }}

@@ -75,7 +75,7 @@
- +

{{ 'addon.mod_scorm.contents' | translate }}

@@ -128,7 +128,7 @@ -
+

{{ 'addon.mod_scorm.mode' | translate }}

@@ -143,14 +143,14 @@
- + {{ 'addon.mod_scorm.newattempt' | translate }} - +

{{ statusMessage | translate }}

diff --git a/src/addon/mod/scorm/components/index/index.ts b/src/addon/mod/scorm/components/index/index.ts index fce78f99c..181342b58 100644 --- a/src/addon/mod/scorm/components/index/index.ts +++ b/src/addon/mod/scorm/components/index/index.ts @@ -54,6 +54,8 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom organizations: any[]; // List of organizations. loadingToc: boolean; // Whether the TOC is being loaded. toc: any[]; // Table of contents (structure). + accessInfo: any; // Access information. + skip: boolean; // Launch immediately. protected fetchContentDefaultError = 'addon.mod_scorm.errorgetscorm'; // Default error to show when loading contents. protected syncEventName = AddonModScormSyncProvider.AUTO_SYNCED; @@ -83,6 +85,10 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom return; } + if (this.skip) { + this.open(); + } + this.scormProvider.logView(this.scorm.id, this.scorm.name).then(() => { this.checkCompletion(); }).catch((error) => { @@ -181,54 +187,72 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom this.syncTime = syncTime; }); - // Get the number of attempts. - return this.scormProvider.getAttemptCount(this.scorm.id); - }).then((attemptsData) => { - this.attempts = attemptsData; - this.hasOffline = !!this.attempts.offline.length; - - // Determine the attempt that will be continued or reviewed. - return this.scormHelper.determineAttemptToContinue(this.scorm, this.attempts); - }).then((attempt) => { - this.lastAttempt = attempt.number; - this.lastIsOffline = attempt.offline; - - if (this.lastAttempt != this.attempts.lastAttempt.number) { - this.attemptToContinue = this.lastAttempt; - } else { - this.attemptToContinue = undefined; - } - - // Check if the last attempt is incomplete. - return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.lastAttempt, this.lastIsOffline); - }).then((incomplete) => { const promises = []; - this.scorm.incomplete = incomplete; - this.scorm.numAttempts = this.attempts.total; - this.scorm.gradeMethodReadable = this.scormProvider.getScormGradeMethod(this.scorm); - this.scorm.attemptsLeft = this.scormProvider.countAttemptsLeft(this.scorm, this.attempts.lastAttempt.number); + // Get access information. + promises.push(this.scormProvider.getAccessInformation(this.scorm.id).then((accessInfo) => { + this.accessInfo = accessInfo; + })); - if (this.scorm.forcenewattempt == AddonModScormProvider.SCORM_FORCEATTEMPT_ALWAYS || - (this.scorm.forcenewattempt && !this.scorm.incomplete)) { - this.scormOptions.newAttempt = true; - } + // Get the number of attempts. + promises.push(this.scormProvider.getAttemptCount(this.scorm.id).then((attemptsData) => { + this.attempts = attemptsData; + this.hasOffline = !!this.attempts.offline.length; - promises.push(this.getReportedGrades()); + // Determine the attempt that will be continued or reviewed. + return this.scormHelper.determineAttemptToContinue(this.scorm, this.attempts); + }).then((attempt) => { + this.lastAttempt = attempt.number; + this.lastIsOffline = attempt.offline; - promises.push(this.fetchStructure()); + if (this.lastAttempt != this.attempts.lastAttempt.number) { + this.attemptToContinue = this.lastAttempt; + } else { + this.attemptToContinue = undefined; + } - if (!this.scorm.packagesize && this.errorMessage === '') { - // SCORM is supported but we don't have package size. Try to calculate it. - promises.push(this.scormProvider.calculateScormSize(this.scorm).then((size) => { - this.scorm.packagesize = size; - })); - } + // Check if the last attempt is incomplete. + return this.scormProvider.isAttemptIncomplete(this.scorm.id, this.lastAttempt, this.lastIsOffline); + }).then((incomplete) => { + const promises = []; - // Handle status. - this.setStatusListener(); + this.scorm.incomplete = incomplete; + this.scorm.numAttempts = this.attempts.total; + this.scorm.gradeMethodReadable = this.scormProvider.getScormGradeMethod(this.scorm); + this.scorm.attemptsLeft = this.scormProvider.countAttemptsLeft(this.scorm, this.attempts.lastAttempt.number); - return Promise.all(promises); + if (this.scorm.forcenewattempt == AddonModScormProvider.SCORM_FORCEATTEMPT_ALWAYS || + (this.scorm.forcenewattempt && !this.scorm.incomplete)) { + this.scormOptions.newAttempt = true; + } + + promises.push(this.getReportedGrades()); + + promises.push(this.fetchStructure()); + + if (!this.scorm.packagesize && this.errorMessage === '') { + // SCORM is supported but we don't have package size. Try to calculate it. + promises.push(this.scormProvider.calculateScormSize(this.scorm).then((size) => { + this.scorm.packagesize = size; + })); + } + + // Handle status. + promises.push(this.setStatusListener()); + + return Promise.all(promises); + })); + + return Promise.all(promises).then(() => { + // Check whether to launch the SCORM immediately. + if (typeof this.skip == 'undefined' && !this.hasOffline && !this.errorMessage && + (!this.scorm.lastattemptlock || this.scorm.attemptsLeft > 0) && + this.accessInfo.canskipview && !this.accessInfo.canviewreport && + this.scorm.skipview >= AddonModScormProvider.SKIPVIEW_FIRST && + (this.scorm.skipview == AddonModScormProvider.SKIPVIEW_ALWAYS || this.lastAttempt == 0)) { + this.skip = true; + } + }); }); }).then(() => { // All data obtained, now fill the context menu. @@ -368,6 +392,9 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom ionViewDidLeave(): void { super.ionViewDidLeave(); + // Display the full page when returning to the page. + this.skip = false; + if (this.navCtrl.getActive().component.name == 'AddonModScormPlayerPage') { this.hasPlayed = true; @@ -460,11 +487,17 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom }); } - // Open a SCORM. It will download the SCORM package if it's not downloaded or it has changed. - // The scoId param indicates the SCO that needs to be loaded when the SCORM is opened. If not defined, load first SCO. - open(e: Event, scoId: number): void { - e.preventDefault(); - e.stopPropagation(); + /** + * Open a SCORM. It will download the SCORM package if it's not downloaded or it has changed. + * + * @param {Event} [event] Event. + * @param {string} [scoId] SCO that needs to be loaded when the SCORM is opened. If not defined, load first SCO. + */ + open(event?: Event, scoId?: number): void { + if (event) { + event.preventDefault(); + event.stopPropagation(); + } if (this.downloading) { // Scope is being downloaded, abort. diff --git a/src/addon/mod/scorm/providers/prefetch-handler.ts b/src/addon/mod/scorm/providers/prefetch-handler.ts index d64ad764b..ceb157f56 100644 --- a/src/addon/mod/scorm/providers/prefetch-handler.ts +++ b/src/addon/mod/scorm/providers/prefetch-handler.ts @@ -123,6 +123,9 @@ export class AddonModScormPrefetchHandler extends CoreCourseActivityPrefetchHand // Ignore errors. })); + // Prefetch access information. + promises.push(this.scormProvider.getAccessInformation(scorm.id)); + return Promise.all(promises); }).then(() => { // Success, return the hash. diff --git a/src/addon/mod/scorm/providers/scorm.ts b/src/addon/mod/scorm/providers/scorm.ts index 712020716..40fe3b162 100644 --- a/src/addon/mod/scorm/providers/scorm.ts +++ b/src/addon/mod/scorm/providers/scorm.ts @@ -83,6 +83,10 @@ export class AddonModScormProvider { static SCORM_FORCEATTEMPT_ONCOMPLETE = 1; static SCORM_FORCEATTEMPT_ALWAYS = 2; + static SKIPVIEW_NEVER = 0; + static SKIPVIEW_FIRST = 1; + static SKIPVIEW_ALWAYS = 2; + // Events. static LAUNCH_NEXT_SCO_EVENT = 'addon_mod_scorm_launch_next_sco'; static LAUNCH_PREV_SCO_EVENT = 'addon_mod_scorm_launch_prev_sco'; @@ -442,6 +446,44 @@ export class AddonModScormProvider { return formatted; } + /** + * Get access information for a given SCORM. + * + * @param {number} scormId SCORM ID. + * @param {boolean} [forceCache] True to always get the value from cache. false otherwise. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Object with access information. + * @since 3.7 + */ + getAccessInformation(scormId: number, forceCache?: boolean, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + if (!site.wsAvailable('mod_scorm_get_scorm_access_information')) { + // Access information not available for 3.6 or older sites. + return Promise.resolve({}); + } + + const params = { + scormid: scormId + }; + const preSets = { + cacheKey: this.getAccessInformationCacheKey(scormId), + omitExpires: forceCache + }; + + return site.read('mod_scorm_get_scorm_access_information', params, preSets); + }); + } + + /** + * Get cache key for access information WS calls. + * + * @param {number} scormId SCORM ID. + * @return {string} Cache key. + */ + protected getAccessInformationCacheKey(scormId: number): string { + return this.ROOT_CACHE_KEY + 'accessInfo:' + scormId; + } + /** * Get the number of attempts done by a user in the given SCORM. * @@ -1176,6 +1218,19 @@ export class AddonModScormProvider { } } + /** + * Invalidates access information. + * + * @param {number} forumId SCORM ID. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateAccessInformation(scormId: number, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getAccessInformationCacheKey(scormId)); + }); + } + /** * Invalidates all the data related to a certain SCORM. * @@ -1190,6 +1245,7 @@ export class AddonModScormProvider { promises.push(this.invalidateAttemptCount(scormId, siteId, userId)); promises.push(this.invalidateScos(scormId, siteId)); promises.push(this.invalidateScormUserData(scormId, siteId)); + promises.push(this.invalidateAccessInformation(scormId, siteId)); return Promise.all(promises); } diff --git a/src/core/course/classes/main-activity-component.ts b/src/core/course/classes/main-activity-component.ts index 16b6e8dd4..925f9109b 100644 --- a/src/core/course/classes/main-activity-component.ts +++ b/src/core/course/classes/main-activity-component.ts @@ -254,8 +254,10 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR /** * Watch for changes on the status. + * + * @return {Promise} Promise resolved when done. */ - protected setStatusListener(): void { + protected setStatusListener(): Promise { if (typeof this.statusObserver == 'undefined') { // Listen for changes on this module status. this.statusObserver = this.eventsProvider.on(CoreEventsProvider.PACKAGE_STATUS_CHANGED, (data) => { @@ -269,11 +271,13 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR }, this.siteId); // Also, get the current status. - this.modulePrefetchDelegate.getModuleStatus(this.module, this.courseId).then((status) => { + return this.modulePrefetchDelegate.getModuleStatus(this.module, this.courseId).then((status) => { this.currentStatus = status; this.showStatus(status); }); } + + return Promise.resolve(); } /**