MOBILE-2840 scorm: Launch automatically

main
Albert Gasset 2019-05-23 14:25:25 +02:00
parent c62237e1db
commit e8f7219883
5 changed files with 148 additions and 52 deletions

View File

@ -24,7 +24,7 @@
<div *ngIf="scorm && loaded && !scorm.warningMessage">
<!-- Attempts status. -->
<ion-card *ngIf="scorm.displayattemptstatus || (scorm.offlineAttempts && scorm.offlineAttempts.length)">
<ion-card *ngIf="(scorm.displayattemptstatus || (scorm.offlineAttempts && scorm.offlineAttempts.length)) && !skip">
<ion-card-header text-wrap>
<h2>{{ 'addon.mod_scorm.attempts' | translate }}</h2>
</ion-card-header>
@ -75,7 +75,7 @@
</div>
<!-- TOC. -->
<ion-card *ngIf="scorm && organizations && ((scorm.displaycoursestructure && organizations.length) || organizations.length > 1)" class="addon-mod_scorm-toc">
<ion-card *ngIf="scorm && organizations && ((scorm.displaycoursestructure && organizations.length) || organizations.length > 1) && !skip" class="addon-mod_scorm-toc">
<ion-card-header text-wrap>
<h2>{{ 'addon.mod_scorm.contents' | translate }}</h2>
</ion-card-header>
@ -128,7 +128,7 @@
<ion-card *ngIf="!errorMessage && scorm && (!scorm.lastattemptlock || scorm.attemptsLeft > 0)">
<ion-list>
<!-- Open mode (Preview or Normal) -->
<div *ngIf="!scorm.hidebrowse" radio-group [(ngModel)]="scormOptions.mode" name="mode">
<div *ngIf="!scorm.hidebrowse && !skip" radio-group [(ngModel)]="scormOptions.mode" name="mode">
<ion-item>
<p class="item-heading">{{ 'addon.mod_scorm.mode' | translate }}</p>
</ion-item>
@ -143,14 +143,14 @@
</div>
<!-- Create new attempt -->
<ion-item text-wrap *ngIf="!scorm.forcenewattempt && scorm.numAttempts > 0 && !scorm.incomplete && scorm.attemptsLeft > 0">
<ion-item text-wrap *ngIf="!scorm.forcenewattempt && scorm.numAttempts > 0 && !scorm.incomplete && scorm.attemptsLeft > 0 && !skip">
<ion-label>{{ 'addon.mod_scorm.newattempt' | translate }}</ion-label>
<ion-checkbox item-end name="newAttempt" [(ngModel)]="scormOptions.newAttempt">
</ion-checkbox>
</ion-item>
<!-- Button to open the SCORM. -->
<ng-container *ngIf="!downloading">
<ng-container *ngIf="!downloading && !skip">
<ion-item text-wrap *ngIf="statusMessage">
<p >{{ statusMessage | translate }}</p>
</ion-item>

View File

@ -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.

View File

@ -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.

View File

@ -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<any>} Object with access information.
* @since 3.7
*/
getAccessInformation(scormId: number, forceCache?: boolean, siteId?: string): Promise<any> {
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<any>} Promise resolved when the data is invalidated.
*/
invalidateAccessInformation(scormId: number, siteId?: string): Promise<any> {
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);
}

View File

@ -254,8 +254,10 @@ export class CoreCourseModuleMainActivityComponent extends CoreCourseModuleMainR
/**
* Watch for changes on the status.
*
* @return {Promise<any>} Promise resolved when done.
*/
protected setStatusListener(): void {
protected setStatusListener(): Promise<any> {
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();
}
/**