2017-12-29 09:18:17 +01:00
|
|
|
// (C) Copyright 2015 Martin Dougiamas
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
import { Injectable } from '@angular/core';
|
2018-01-23 09:08:49 +01:00
|
|
|
import { NavController } from 'ionic-angular';
|
2018-01-17 12:12:23 +01:00
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
2018-03-01 16:55:49 +01:00
|
|
|
import { CoreEventsProvider } from '@providers/events';
|
|
|
|
import { CoreFilepoolProvider } from '@providers/filepool';
|
|
|
|
import { CoreSitesProvider } from '@providers/sites';
|
|
|
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
|
|
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
|
|
|
import { CoreTimeUtilsProvider } from '@providers/utils/time';
|
|
|
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
2018-01-26 09:54:01 +01:00
|
|
|
import { CoreCourseOptionsDelegate, CoreCourseOptionsHandlerToDisplay } from './options-delegate';
|
2018-01-23 09:08:49 +01:00
|
|
|
import { CoreSiteHomeProvider } from '../../sitehome/providers/sitehome';
|
2017-12-29 09:18:17 +01:00
|
|
|
import { CoreCourseProvider } from './course';
|
2018-01-02 08:47:21 +01:00
|
|
|
import { CoreCourseModuleDelegate } from './module-delegate';
|
2018-01-25 13:19:11 +01:00
|
|
|
import { CoreCourseModulePrefetchDelegate } from './module-prefetch-delegate';
|
2018-01-23 09:08:49 +01:00
|
|
|
import { CoreLoginHelperProvider } from '../../login/providers/helper';
|
2018-01-17 12:12:23 +01:00
|
|
|
import { CoreConstants } from '../../constants';
|
2018-03-01 16:55:49 +01:00
|
|
|
import { CoreSite } from '@classes/site';
|
2018-01-17 12:12:23 +01:00
|
|
|
import * as moment from 'moment';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prefetch info of a module.
|
|
|
|
*/
|
|
|
|
export type CoreCourseModulePrefetchInfo = {
|
|
|
|
/**
|
|
|
|
* Downloaded size.
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
size?: number;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Downloadable size in a readable format.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
sizeReadable?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Module status.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
status?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Icon's name of the module status.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
statusIcon?: string;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Time when the module was last downloaded.
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
downloadTime?: number;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Download time in a readable format.
|
|
|
|
* @type {string}
|
|
|
|
*/
|
|
|
|
downloadTimeReadable?: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Progress of downloading a list of courses.
|
|
|
|
*/
|
|
|
|
export type CoreCourseCoursesProgress = {
|
|
|
|
/**
|
|
|
|
* Number of courses downloaded so far.
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
count: number;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toal of courses to download.
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
total: number;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Whether the download has been successful so far.
|
|
|
|
* @type {boolean}
|
|
|
|
*/
|
|
|
|
success: boolean;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Last downloaded course.
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
courseId?: number;
|
|
|
|
};
|
2017-12-29 09:18:17 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper to gather some common course functions.
|
|
|
|
*/
|
|
|
|
@Injectable()
|
|
|
|
export class CoreCourseHelperProvider {
|
|
|
|
|
2018-01-29 10:05:20 +01:00
|
|
|
protected courseDwnPromises: { [s: string]: { [id: number]: Promise<any> } } = {};
|
2018-01-17 12:12:23 +01:00
|
|
|
|
2018-01-02 08:47:21 +01:00
|
|
|
constructor(private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
|
2018-01-29 10:05:20 +01:00
|
|
|
private moduleDelegate: CoreCourseModuleDelegate, private prefetchDelegate: CoreCourseModulePrefetchDelegate,
|
|
|
|
private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider,
|
|
|
|
private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider,
|
|
|
|
private utils: CoreUtilsProvider, private translate: TranslateService, private loginHelper: CoreLoginHelperProvider,
|
2018-02-02 13:10:19 +01:00
|
|
|
private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider,
|
|
|
|
private eventsProvider: CoreEventsProvider) { }
|
2018-01-02 08:47:21 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This function treats every module on the sections provided to load the handler data, treat completion
|
|
|
|
* and navigate to a module page if required. It also returns if sections has content.
|
|
|
|
*
|
|
|
|
* @param {any[]} sections List of sections to treat modules.
|
|
|
|
* @param {number} courseId Course ID of the modules.
|
|
|
|
* @param {any[]} [completionStatus] List of completion status.
|
|
|
|
* @return {boolean} Whether the sections have content.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
addHandlerDataForModules(sections: any[], courseId: number, completionStatus?: any): boolean {
|
2018-01-02 08:47:21 +01:00
|
|
|
let hasContent = false;
|
|
|
|
|
|
|
|
sections.forEach((section) => {
|
|
|
|
if (!section || !this.sectionHasContent(section) || !section.modules) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
hasContent = true;
|
|
|
|
|
|
|
|
section.modules.forEach((module) => {
|
|
|
|
module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, section.id);
|
|
|
|
|
|
|
|
if (completionStatus && typeof completionStatus[module.id] != 'undefined') {
|
|
|
|
// Check if activity has completions and if it's marked.
|
|
|
|
module.completionstatus = completionStatus[module.id];
|
|
|
|
module.completionstatus.courseId = courseId;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return hasContent;
|
|
|
|
}
|
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
/**
|
|
|
|
* Calculate the status of a section.
|
|
|
|
*
|
|
|
|
* @param {any} section Section to calculate its status. It can't be "All sections".
|
|
|
|
* @param {number} courseId Course ID the section belongs to.
|
|
|
|
* @param {boolean} [refresh] True if it shouldn't use module status cache (slower).
|
|
|
|
* @return {Promise<any>} Promise resolved when the status is calculated.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
calculateSectionStatus(section: any, courseId: number, refresh?: boolean): Promise<any> {
|
2018-01-17 12:12:23 +01:00
|
|
|
|
|
|
|
if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) {
|
|
|
|
return Promise.reject(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the status of this section.
|
|
|
|
return this.prefetchDelegate.getModulesStatus(section.modules, courseId, section.id, refresh).then((result) => {
|
|
|
|
// Check if it's being downloaded.
|
|
|
|
const downloadId = this.getSectionDownloadId(section);
|
|
|
|
if (this.prefetchDelegate.isBeingDownloaded(downloadId)) {
|
|
|
|
result.status = CoreConstants.DOWNLOADING;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set this section data.
|
|
|
|
section.showDownload = result.status === CoreConstants.NOT_DOWNLOADED;
|
|
|
|
section.showRefresh = result.status === CoreConstants.OUTDATED;
|
|
|
|
|
|
|
|
if (result.status !== CoreConstants.DOWNLOADING || !this.prefetchDelegate.isBeingDownloaded(section.id)) {
|
|
|
|
section.isDownloading = false;
|
|
|
|
section.total = 0;
|
|
|
|
} else {
|
|
|
|
// Section is being downloaded.
|
|
|
|
section.isDownloading = true;
|
|
|
|
this.prefetchDelegate.setOnProgress(downloadId, (data) => {
|
|
|
|
section.count = data.count;
|
|
|
|
section.total = data.total;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate the status of a list of sections, setting attributes to determine the icons/data to be shown.
|
|
|
|
*
|
|
|
|
* @param {any[]} sections Sections to calculate their status.
|
|
|
|
* @param {number} courseId Course ID the sections belong to.
|
|
|
|
* @param {boolean} [refresh] True if it shouldn't use module status cache (slower).
|
|
|
|
* @return {Promise<void>} Promise resolved when the states are calculated.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
calculateSectionsStatus(sections: any[], courseId: number, refresh?: boolean): Promise<void> {
|
|
|
|
const promises = [];
|
2018-01-17 12:12:23 +01:00
|
|
|
let allSectionsSection,
|
2018-01-29 10:05:20 +01:00
|
|
|
allSectionsStatus;
|
2018-01-17 12:12:23 +01:00
|
|
|
|
|
|
|
sections.forEach((section) => {
|
|
|
|
if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) {
|
|
|
|
// "All sections" section status is calculated using the status of the rest of sections.
|
|
|
|
allSectionsSection = section;
|
|
|
|
section.isCalculating = true;
|
|
|
|
} else {
|
|
|
|
section.isCalculating = true;
|
|
|
|
promises.push(this.calculateSectionStatus(section, courseId, refresh).then((result) => {
|
|
|
|
// Calculate "All sections" status.
|
|
|
|
allSectionsStatus = this.filepoolProvider.determinePackagesStatus(allSectionsStatus, result.status);
|
|
|
|
}).finally(() => {
|
|
|
|
section.isCalculating = false;
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.all(promises).then(() => {
|
|
|
|
if (allSectionsSection) {
|
|
|
|
// Set "All sections" data.
|
|
|
|
allSectionsSection.showDownload = allSectionsStatus === CoreConstants.NOT_DOWNLOADED;
|
|
|
|
allSectionsSection.showRefresh = allSectionsStatus === CoreConstants.OUTDATED;
|
|
|
|
allSectionsSection.isDownloading = allSectionsStatus === CoreConstants.DOWNLOADING;
|
|
|
|
}
|
|
|
|
}).finally(() => {
|
|
|
|
if (allSectionsSection) {
|
|
|
|
allSectionsSection.isCalculating = false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Show a confirm and prefetch a course. It will retrieve the sections and the course options if not provided.
|
|
|
|
* This function will set the icon to "spinner" when starting and it will also set it back to the initial icon if the
|
|
|
|
* user cancels. All the other updates of the icon should be made when CoreEventsProvider.COURSE_STATUS_CHANGED is received.
|
|
|
|
*
|
|
|
|
* @param {any} iconData An object where to store the course icon. It will be stored with the name "prefetchCourseIcon".
|
|
|
|
* @param {any} course Course to prefetch.
|
|
|
|
* @param {any[]} [sections] List of course sections.
|
2018-01-26 09:54:01 +01:00
|
|
|
* @param {CoreCourseOptionsHandlerToDisplay[]} courseHandlers List of course handlers.
|
2018-01-17 12:12:23 +01:00
|
|
|
* @return {Promise<boolean>} Promise resolved with true when the download finishes, resolved with false if user doesn't
|
|
|
|
* confirm, rejected if an error occurs.
|
|
|
|
*/
|
2018-01-26 09:54:01 +01:00
|
|
|
confirmAndPrefetchCourse(iconData: any, course: any, sections?: any[], courseHandlers?: CoreCourseOptionsHandlerToDisplay[])
|
2018-01-17 12:12:23 +01:00
|
|
|
: Promise<boolean> {
|
2018-01-29 10:05:20 +01:00
|
|
|
|
|
|
|
const initialIcon = iconData.prefetchCourseIcon,
|
2018-01-17 12:12:23 +01:00
|
|
|
siteId = this.sitesProvider.getCurrentSiteId();
|
2018-01-29 10:05:20 +01:00
|
|
|
let promise;
|
2018-01-17 12:12:23 +01:00
|
|
|
|
|
|
|
iconData.prefetchCourseIcon = 'spinner';
|
|
|
|
|
|
|
|
// Get the sections first if needed.
|
|
|
|
if (sections) {
|
|
|
|
promise = Promise.resolve(sections);
|
|
|
|
} else {
|
|
|
|
promise = this.courseProvider.getSections(course.id, false, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return promise.then((sections) => {
|
|
|
|
// Confirm the download.
|
|
|
|
return this.confirmDownloadSizeSection(course.id, undefined, sections, true).then(() => {
|
|
|
|
// User confirmed, get the course handlers if needed.
|
|
|
|
if (courseHandlers) {
|
|
|
|
promise = Promise.resolve(courseHandlers);
|
|
|
|
} else {
|
2018-01-26 09:54:01 +01:00
|
|
|
promise = this.courseOptionsDelegate.getHandlersToDisplay(course);
|
2018-01-17 12:12:23 +01:00
|
|
|
}
|
|
|
|
|
2018-01-26 09:54:01 +01:00
|
|
|
return promise.then((handlers: CoreCourseOptionsHandlerToDisplay[]) => {
|
2018-01-17 12:12:23 +01:00
|
|
|
// Now we have all the data, download the course.
|
|
|
|
return this.prefetchCourse(course, sections, handlers, siteId);
|
|
|
|
}).then(() => {
|
|
|
|
// Download successful.
|
|
|
|
return true;
|
|
|
|
});
|
2018-01-29 10:05:20 +01:00
|
|
|
}, (error): any => {
|
2018-01-17 12:12:23 +01:00
|
|
|
// User cancelled or there was an error calculating the size.
|
|
|
|
iconData.prefetchCourseIcon = initialIcon;
|
|
|
|
if (error) {
|
|
|
|
return Promise.reject(error);
|
|
|
|
}
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
return false;
|
|
|
|
});
|
|
|
|
});
|
2018-01-29 10:05:20 +01:00
|
|
|
}
|
2018-01-17 12:12:23 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Confirm and prefetches a list of courses.
|
|
|
|
*
|
2018-01-23 13:00:00 +01:00
|
|
|
* @param {any[]} courses List of courses to download.
|
|
|
|
* @param {Function} [onProgress] Function to call everytime a course is downloaded.
|
|
|
|
* @return {Promise<boolean>} Resolved with true when downloaded, resolved with false if user cancels, rejected if error.
|
2018-01-17 12:12:23 +01:00
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
confirmAndPrefetchCourses(courses: any[], onProgress?: (data: CoreCourseCoursesProgress) => void): Promise<boolean> {
|
2018-01-17 12:12:23 +01:00
|
|
|
const siteId = this.sitesProvider.getCurrentSiteId();
|
|
|
|
|
|
|
|
// Confirm the download without checking size because it could take a while.
|
|
|
|
return this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => {
|
2018-01-29 10:05:20 +01:00
|
|
|
const promises = [],
|
|
|
|
total = courses.length;
|
|
|
|
let count = 0;
|
2018-01-17 12:12:23 +01:00
|
|
|
|
|
|
|
courses.forEach((course) => {
|
2018-01-29 10:05:20 +01:00
|
|
|
const subPromises = [];
|
|
|
|
let sections,
|
2018-01-17 12:12:23 +01:00
|
|
|
handlers,
|
|
|
|
success = true;
|
|
|
|
|
|
|
|
// Get the sections and the handlers.
|
|
|
|
subPromises.push(this.courseProvider.getSections(course.id, false, true).then((courseSections) => {
|
|
|
|
sections = courseSections;
|
|
|
|
}));
|
2018-01-26 09:54:01 +01:00
|
|
|
subPromises.push(this.courseOptionsDelegate.getHandlersToDisplay(course).then((cHandlers) => {
|
2018-01-17 12:12:23 +01:00
|
|
|
handlers = cHandlers;
|
|
|
|
}));
|
|
|
|
|
|
|
|
promises.push(Promise.all(subPromises).then(() => {
|
|
|
|
return this.prefetchCourse(course, sections, handlers, siteId);
|
|
|
|
}).catch((error) => {
|
|
|
|
success = false;
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
return Promise.reject(error);
|
|
|
|
}).finally(() => {
|
|
|
|
// Course downloaded or failed, notify the progress.
|
|
|
|
count++;
|
|
|
|
if (onProgress) {
|
2018-01-29 10:05:20 +01:00
|
|
|
onProgress({ count: count, total: total, courseId: course.id, success: success });
|
2018-01-17 12:12:23 +01:00
|
|
|
}
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
|
|
|
if (onProgress) {
|
|
|
|
// Notify the start of the download.
|
2018-01-29 10:05:20 +01:00
|
|
|
onProgress({ count: 0, total: total, success: true });
|
2018-01-17 12:12:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return this.utils.allPromises(promises).then(() => {
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}, () => {
|
|
|
|
// User cancelled.
|
|
|
|
return false;
|
|
|
|
});
|
2018-01-29 10:05:20 +01:00
|
|
|
}
|
2018-01-17 12:12:23 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Show confirmation dialog and then remove a module files.
|
|
|
|
*
|
|
|
|
* @param {any} module Module to remove the files.
|
|
|
|
* @param {number} courseId Course ID the module belongs to.
|
|
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
confirmAndRemoveFiles(module: any, courseId: number): Promise<any> {
|
2018-02-02 13:10:19 +01:00
|
|
|
return this.domUtils.showConfirm(this.translate.instant('core.course.confirmdeletemodulefiles')).then(() => {
|
2018-01-17 12:12:23 +01:00
|
|
|
return this.prefetchDelegate.removeModuleFiles(module, courseId);
|
2018-02-02 13:10:19 +01:00
|
|
|
}).catch((error) => {
|
|
|
|
if (error) {
|
|
|
|
this.domUtils.showErrorModal(error);
|
|
|
|
}
|
2018-01-17 12:12:23 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculate the size to download a section and show a confirm modal if needed.
|
|
|
|
*
|
|
|
|
* @param {number} courseId Course ID the section belongs to.
|
|
|
|
* @param {any} [section] Section. If not provided, all sections.
|
|
|
|
* @param {any[]} [sections] List of sections. Used when downloading all the sections.
|
|
|
|
* @param {boolean} [alwaysConfirm] True to show a confirm even if the size isn't high, false otherwise.
|
|
|
|
* @return {Promise<any>} Promise resolved if the user confirms or there's no need to confirm.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
confirmDownloadSizeSection(courseId: number, section?: any, sections?: any[], alwaysConfirm?: boolean): Promise<any> {
|
2018-01-17 12:12:23 +01:00
|
|
|
let sizePromise;
|
|
|
|
|
|
|
|
// Calculate the size of the download.
|
|
|
|
if (section && section.id != CoreCourseProvider.ALL_SECTIONS_ID) {
|
|
|
|
sizePromise = this.prefetchDelegate.getDownloadSize(section.modules, courseId);
|
|
|
|
} else {
|
2018-01-29 10:05:20 +01:00
|
|
|
const promises = [],
|
2018-01-17 12:12:23 +01:00
|
|
|
results = {
|
|
|
|
size: 0,
|
|
|
|
total: true
|
|
|
|
};
|
|
|
|
|
|
|
|
sections.forEach((s) => {
|
|
|
|
if (s.id != CoreCourseProvider.ALL_SECTIONS_ID) {
|
|
|
|
promises.push(this.prefetchDelegate.getDownloadSize(s.modules, courseId).then((sectionSize) => {
|
|
|
|
results.total = results.total && sectionSize.total;
|
|
|
|
results.size += sectionSize.size;
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
sizePromise = Promise.all(promises).then(() => {
|
|
|
|
return results;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return sizePromise.then((size) => {
|
|
|
|
// Show confirm modal if needed.
|
|
|
|
return this.domUtils.confirmDownloadSize(size, undefined, undefined, undefined, undefined, alwaysConfirm);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-02-02 13:10:19 +01:00
|
|
|
/**
|
|
|
|
* Helper function to prefetch a module, showing a confirmation modal if the size is big.
|
|
|
|
* This function is meant to be called from a context menu option. It will also modify some data like the prefetch icon.
|
|
|
|
*
|
|
|
|
* @param {any} instance The component instance that has the context menu. It should have prefetchStatusIcon and isDestroyed.
|
|
|
|
* @param {any} module Module to be prefetched
|
|
|
|
* @param {number} courseId Course ID the module belongs to.
|
|
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
|
|
*/
|
|
|
|
contextMenuPrefetch(instance: any, module: any, courseId: number): Promise<any> {
|
|
|
|
const initialIcon = instance.prefetchStatusIcon;
|
|
|
|
let cancelled = false;
|
|
|
|
|
|
|
|
instance.prefetchStatusIcon = 'spinner'; // Show spinner since this operation might take a while.
|
|
|
|
|
|
|
|
// We need to call getDownloadSize, the package might have been updated.
|
|
|
|
return this.prefetchDelegate.getModuleDownloadSize(module, courseId, true).then((size) => {
|
|
|
|
return this.domUtils.confirmDownloadSize(size).catch(() => {
|
|
|
|
// User hasn't confirmed, stop.
|
|
|
|
cancelled = true;
|
|
|
|
|
|
|
|
return Promise.reject(null);
|
|
|
|
}).then(() => {
|
|
|
|
return this.prefetchDelegate.prefetchModule(module, courseId, true);
|
|
|
|
});
|
|
|
|
}).catch((error) => {
|
|
|
|
instance.prefetchStatusIcon = initialIcon;
|
|
|
|
if (!instance.isDestroyed && !cancelled) {
|
|
|
|
this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
/**
|
|
|
|
* Determine the status of a list of courses.
|
|
|
|
*
|
|
|
|
* @param {any[]} courses Courses
|
|
|
|
* @return {Promise<string>} Promise resolved with the status.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
determineCoursesStatus(courses: any[]): Promise<string> {
|
2018-01-17 12:12:23 +01:00
|
|
|
// Get the status of each course.
|
|
|
|
const promises = [],
|
|
|
|
siteId = this.sitesProvider.getCurrentSiteId();
|
|
|
|
|
|
|
|
courses.forEach((course) => {
|
|
|
|
promises.push(this.courseProvider.getCourseStatus(course.id, siteId));
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.all(promises).then((statuses) => {
|
|
|
|
// Now determine the status of the whole list.
|
|
|
|
let status = statuses[0];
|
|
|
|
for (let i = 1; i < statuses.length; i++) {
|
|
|
|
status = this.filepoolProvider.determinePackagesStatus(status, statuses[i]);
|
|
|
|
}
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
return status;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-02-02 13:10:19 +01:00
|
|
|
/**
|
|
|
|
* Fill the Context Menu for a certain module.
|
|
|
|
*
|
|
|
|
* @param {any} instance The component instance that has the context menu.
|
|
|
|
* @param {any} module Module to be prefetched
|
|
|
|
* @param {number} courseId Course ID the module belongs to.
|
|
|
|
* @param {boolean} [invalidateCache] Invalidates the cache first.
|
|
|
|
* @param {string} [component] Component of the module.
|
|
|
|
* @return {Promise<any>} Promise resolved when done.
|
|
|
|
*/
|
|
|
|
fillContextMenu(instance: any, module: any, courseId: number, invalidateCache?: boolean, component?: string): Promise<any> {
|
|
|
|
return this.getModulePrefetchInfo(module, courseId, invalidateCache, component).then((moduleInfo) => {
|
|
|
|
instance.size = moduleInfo.size > 0 ? moduleInfo.sizeReadable : 0;
|
|
|
|
instance.prefetchStatusIcon = moduleInfo.statusIcon;
|
|
|
|
|
|
|
|
if (moduleInfo.status != CoreConstants.NOT_DOWNLOADABLE) {
|
|
|
|
// Module is downloadable, get the text to display to prefetch.
|
|
|
|
if (moduleInfo.downloadTime > 0) {
|
|
|
|
instance.prefetchText = this.translate.instant('core.lastdownloaded') + ': ' + moduleInfo.downloadTimeReadable;
|
|
|
|
} else {
|
|
|
|
// Module not downloaded, show a default text.
|
|
|
|
instance.prefetchText = this.translate.instant('core.download');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof instance.statusObserver == 'undefined' && component) {
|
|
|
|
instance.statusObserver = this.eventsProvider.on(CoreEventsProvider.PACKAGE_STATUS_CHANGED, (data) => {
|
|
|
|
if (data.componentId == module.id && data.component == component) {
|
|
|
|
this.fillContextMenu(instance, module, courseId, false, component);
|
|
|
|
}
|
|
|
|
}, this.sitesProvider.getCurrentSiteId());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
/**
|
|
|
|
* Get a course download promise (if any).
|
|
|
|
*
|
|
|
|
* @param {number} courseId Course ID.
|
|
|
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
|
|
|
* @return {Promise<any>} Download promise, undefined if not found.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
getCourseDownloadPromise(courseId: number, siteId?: string): Promise<any> {
|
2018-01-17 12:12:23 +01:00
|
|
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
return this.courseDwnPromises[siteId] && this.courseDwnPromises[siteId][courseId];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a course status icon.
|
|
|
|
*
|
|
|
|
* @param {number} courseId Course ID.
|
|
|
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
|
|
|
* @return {Promise<string>} Promise resolved with the icon name.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
getCourseStatusIcon(courseId: number, siteId?: string): Promise<string> {
|
2018-01-17 12:12:23 +01:00
|
|
|
return this.courseProvider.getCourseStatus(courseId, siteId).then((status) => {
|
|
|
|
return this.getCourseStatusIconFromStatus(status);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a course status icon from status.
|
|
|
|
*
|
|
|
|
* @module mm.core.course
|
|
|
|
* @ngdoc method
|
|
|
|
* @name $mmCourseHelper#getCourseStatusIconFromStatus
|
|
|
|
* @param {String} status Course status.
|
|
|
|
* @return {String} Icon name.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
getCourseStatusIconFromStatus(status: string): string {
|
2018-01-17 12:12:23 +01:00
|
|
|
if (status == CoreConstants.DOWNLOADED) {
|
|
|
|
// Always show refresh icon, we cannot knew if there's anything new in course options.
|
|
|
|
return 'refresh';
|
|
|
|
} else if (status == CoreConstants.DOWNLOADING) {
|
|
|
|
return 'spinner';
|
|
|
|
} else {
|
|
|
|
return 'cloud-download';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-02 08:47:21 +01:00
|
|
|
/**
|
|
|
|
* Get the course ID from a module instance ID, showing an error message if it can't be retrieved.
|
|
|
|
*
|
|
|
|
* @param {number} id Instance ID.
|
|
|
|
* @param {string} module Name of the module. E.g. 'glossary'.
|
|
|
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
|
|
|
* @return {Promise<number>} Promise resolved with the module's course ID.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
getModuleCourseIdByInstance(id: number, module: any, siteId?: string): Promise<number> {
|
2018-01-02 08:47:21 +01:00
|
|
|
return this.courseProvider.getModuleBasicInfoByInstance(id, module, siteId).then((cm) => {
|
|
|
|
return cm.course;
|
|
|
|
}).catch((error) => {
|
|
|
|
this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2018-01-02 08:47:21 +01:00
|
|
|
return Promise.reject(null);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
/**
|
|
|
|
* Get prefetch info for a module.
|
|
|
|
*
|
|
|
|
* @param {any} module Module to get the info from.
|
|
|
|
* @param {number} courseId Course ID the section belongs to.
|
|
|
|
* @param {boolean} [invalidateCache] Invalidates the cache first.
|
|
|
|
* @param {string} [component] Component of the module.
|
|
|
|
* @return {Promise<CoreCourseModulePrefetchInfo>} Promise resolved with the info.
|
|
|
|
*/
|
|
|
|
getModulePrefetchInfo(module: any, courseId: number, invalidateCache?: boolean, component?: string)
|
|
|
|
: Promise<CoreCourseModulePrefetchInfo> {
|
2018-01-29 10:05:20 +01:00
|
|
|
const moduleInfo: CoreCourseModulePrefetchInfo = {},
|
2018-01-17 12:12:23 +01:00
|
|
|
siteId = this.sitesProvider.getCurrentSiteId(),
|
|
|
|
promises = [];
|
|
|
|
|
|
|
|
if (invalidateCache) {
|
|
|
|
this.prefetchDelegate.invalidateModuleStatusCache(module);
|
|
|
|
}
|
|
|
|
|
|
|
|
promises.push(this.prefetchDelegate.getModuleDownloadedSize(module, courseId).then((moduleSize) => {
|
|
|
|
moduleInfo.size = moduleSize;
|
|
|
|
moduleInfo.sizeReadable = this.textUtils.bytesToSize(moduleSize, 2);
|
|
|
|
}));
|
|
|
|
|
|
|
|
// @todo: Decide what to display instead of timemodified. Last check_updates?
|
|
|
|
|
|
|
|
promises.push(this.prefetchDelegate.getModuleStatus(module, courseId).then((moduleStatus) => {
|
|
|
|
moduleInfo.status = moduleStatus;
|
|
|
|
switch (moduleStatus) {
|
|
|
|
case CoreConstants.NOT_DOWNLOADED:
|
|
|
|
moduleInfo.statusIcon = 'cloud-download';
|
|
|
|
break;
|
|
|
|
case CoreConstants.DOWNLOADING:
|
|
|
|
moduleInfo.statusIcon = 'spinner';
|
|
|
|
break;
|
|
|
|
case CoreConstants.OUTDATED:
|
|
|
|
moduleInfo.statusIcon = 'ion-android-refresh';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
moduleInfo.statusIcon = '';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
|
|
|
|
// Get the time it was downloaded (if it was downloaded).
|
|
|
|
promises.push(this.filepoolProvider.getPackageData(siteId, component, module.id).then((data) => {
|
|
|
|
if (data && data.downloadTime && (data.status == CoreConstants.OUTDATED || data.status == CoreConstants.DOWNLOADED)) {
|
|
|
|
const now = this.timeUtils.timestamp();
|
|
|
|
moduleInfo.downloadTime = data.downloadTime;
|
|
|
|
if (now - data.downloadTime < 7 * 86400) {
|
|
|
|
moduleInfo.downloadTimeReadable = moment(data.downloadTime * 1000).fromNow();
|
|
|
|
} else {
|
|
|
|
moduleInfo.downloadTimeReadable = moment(data.downloadTime * 1000).calendar();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}).catch(() => {
|
|
|
|
// Not downloaded.
|
|
|
|
moduleInfo.downloadTime = 0;
|
|
|
|
}));
|
|
|
|
|
|
|
|
return Promise.all(promises).then(() => {
|
|
|
|
return moduleInfo;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the download ID of a section. It's used to interact with CoreCourseModulePrefetchDelegate.
|
|
|
|
*
|
|
|
|
* @param {any} section Section.
|
|
|
|
* @return {string} Section download ID.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
getSectionDownloadId(section: any): string {
|
2018-01-17 12:12:23 +01:00
|
|
|
return 'Section-' + section.id;
|
|
|
|
}
|
|
|
|
|
2018-01-23 09:08:49 +01:00
|
|
|
/**
|
|
|
|
* Navigate to a module.
|
|
|
|
*
|
|
|
|
* @param {number} moduleId Module's ID.
|
|
|
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
|
|
|
* @param {number} [courseId] Course ID. If not defined we'll try to retrieve it from the site.
|
|
|
|
* @param {number} [sectionId] Section the module belongs to. If not defined we'll try to retrieve it from the site.
|
|
|
|
* @return {Promise<void>} Promise resolved when done.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
navigateToModule(moduleId: number, siteId?: string, courseId?: number, sectionId?: number): Promise<void> {
|
2018-01-23 09:08:49 +01:00
|
|
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
|
|
|
|
2018-01-29 10:05:20 +01:00
|
|
|
const modal = this.domUtils.showModalLoading();
|
|
|
|
let promise,
|
2018-01-23 09:08:49 +01:00
|
|
|
site: CoreSite;
|
|
|
|
|
|
|
|
if (courseId && sectionId) {
|
|
|
|
// No need to retrieve more data.
|
|
|
|
promise = Promise.resolve();
|
|
|
|
} else if (!courseId) {
|
|
|
|
// We don't have courseId.
|
|
|
|
promise = this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((module) => {
|
|
|
|
courseId = module.course;
|
|
|
|
sectionId = module.section;
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// We don't have sectionId but we have courseId.
|
|
|
|
promise = this.courseProvider.getModuleSectionId(moduleId, siteId).then((id) => {
|
|
|
|
sectionId = id;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return promise.then(() => {
|
|
|
|
// Get the site.
|
|
|
|
return this.sitesProvider.getSite(siteId);
|
|
|
|
}).then((s) => {
|
|
|
|
site = s;
|
|
|
|
|
|
|
|
// Get the module.
|
|
|
|
return this.courseProvider.getModule(moduleId, courseId, sectionId, false, false, siteId);
|
|
|
|
}).then((module) => {
|
|
|
|
const params = {
|
2018-01-29 10:05:20 +01:00
|
|
|
course: { id: courseId },
|
2018-01-23 09:08:49 +01:00
|
|
|
module: module,
|
|
|
|
sectionId: sectionId
|
|
|
|
};
|
|
|
|
|
|
|
|
module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, sectionId);
|
|
|
|
|
|
|
|
if (courseId == site.getSiteHomeId()) {
|
|
|
|
// Check if site home is available.
|
|
|
|
return this.siteHomeProvider.isAvailable().then(() => {
|
|
|
|
this.loginHelper.redirect('CoreSiteHomeIndexPage', params, siteId);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.loginHelper.redirect('CoreCourseSectionPage', params, siteId);
|
|
|
|
}
|
|
|
|
}).catch((error) => {
|
|
|
|
this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
|
|
|
|
}).finally(() => {
|
|
|
|
modal.dismiss();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Open a module.
|
|
|
|
*
|
|
|
|
* @param {NavController} navCtrl The NavController to use.
|
|
|
|
* @param {any} module The module to open.
|
|
|
|
* @param {number} courseId The course ID of the module.
|
|
|
|
* @param {number} [sectionId] The section ID of the module.
|
2018-01-31 11:37:42 +01:00
|
|
|
* @param {boolean} True if module can be opened, false otherwise.
|
2018-01-23 09:08:49 +01:00
|
|
|
*/
|
2018-01-31 11:37:42 +01:00
|
|
|
openModule(navCtrl: NavController, module: any, courseId: number, sectionId?: number): boolean {
|
2018-01-23 09:08:49 +01:00
|
|
|
if (!module.handlerData) {
|
|
|
|
module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, sectionId);
|
|
|
|
}
|
|
|
|
|
2018-01-31 11:37:42 +01:00
|
|
|
if (module.handlerData && module.handlerData.action) {
|
|
|
|
module.handlerData.action(new Event('click'), navCtrl, module, courseId, { animate: false });
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2018-01-23 09:08:49 +01:00
|
|
|
}
|
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
/**
|
|
|
|
* Prefetch all the activities in a course and also the course addons.
|
|
|
|
*
|
|
|
|
* @param {any} course The course to prefetch.
|
|
|
|
* @param {any[]} sections List of course sections.
|
2018-01-26 09:54:01 +01:00
|
|
|
* @param {CoreCourseOptionsHandlerToDisplay[]} courseHandlers List of course options handlers.
|
2018-01-17 12:12:23 +01:00
|
|
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
|
|
|
* @return {Promise} Promise resolved when the download finishes.
|
|
|
|
*/
|
2018-01-26 09:54:01 +01:00
|
|
|
prefetchCourse(course: any, sections: any[], courseHandlers: CoreCourseOptionsHandlerToDisplay[], siteId?: string)
|
|
|
|
: Promise<any> {
|
2018-01-17 12:12:23 +01:00
|
|
|
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
|
|
|
|
|
|
|
if (this.courseDwnPromises[siteId] && this.courseDwnPromises[siteId][course.id]) {
|
|
|
|
// There's already a download ongoing for this course, return the promise.
|
|
|
|
return this.courseDwnPromises[siteId][course.id];
|
|
|
|
} else if (!this.courseDwnPromises[siteId]) {
|
|
|
|
this.courseDwnPromises[siteId] = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
// First of all, mark the course as being downloaded.
|
|
|
|
this.courseDwnPromises[siteId][course.id] = this.courseProvider.setCourseStatus(course.id, CoreConstants.DOWNLOADING,
|
2018-01-29 10:05:20 +01:00
|
|
|
siteId).then(() => {
|
|
|
|
const promises = [];
|
|
|
|
let allSectionsSection = sections[0];
|
2018-01-17 12:12:23 +01:00
|
|
|
|
2018-01-29 10:05:20 +01:00
|
|
|
// Prefetch all the sections. If the first section is "All sections", use it. Otherwise, use a fake "All sections".
|
|
|
|
if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) {
|
|
|
|
allSectionsSection = { id: CoreCourseProvider.ALL_SECTIONS_ID };
|
2018-01-17 12:12:23 +01:00
|
|
|
}
|
2018-01-29 10:05:20 +01:00
|
|
|
promises.push(this.prefetchSection(allSectionsSection, course.id, sections));
|
2018-01-17 12:12:23 +01:00
|
|
|
|
2018-01-29 10:05:20 +01:00
|
|
|
// Prefetch course options.
|
|
|
|
courseHandlers.forEach((handler) => {
|
|
|
|
if (handler.prefetch) {
|
|
|
|
promises.push(handler.prefetch(course));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return this.utils.allPromises(promises);
|
|
|
|
}).then(() => {
|
|
|
|
// Download success, mark the course as downloaded.
|
|
|
|
return this.courseProvider.setCourseStatus(course.id, CoreConstants.DOWNLOADED, siteId);
|
|
|
|
}).catch((error) => {
|
|
|
|
// Error, restore previous status.
|
|
|
|
return this.courseProvider.setCoursePreviousStatus(course.id, siteId).then(() => {
|
|
|
|
return Promise.reject(error);
|
|
|
|
});
|
|
|
|
}).finally(() => {
|
|
|
|
delete this.courseDwnPromises[siteId][course.id];
|
2018-01-17 12:12:23 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
return this.courseDwnPromises[siteId][course.id];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper function to prefetch a module, showing a confirmation modal if the size is big
|
|
|
|
* and invalidating contents if refreshing.
|
|
|
|
*
|
|
|
|
* @param {handler} handler Prefetch handler to use. Must implement 'prefetch' and 'invalidateContent'.
|
|
|
|
* @param {any} module Module to download.
|
|
|
|
* @param {any} size Object containing size to download (in bytes) and a boolean to indicate if its totally calculated.
|
|
|
|
* @param {number} courseId Course ID of the module.
|
|
|
|
* @param {boolean} [refresh] True if refreshing, false otherwise.
|
|
|
|
* @return {Promise<any>} Promise resolved when downloaded.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
prefetchModule(handler: any, module: any, size: any, courseId: number, refresh?: boolean): Promise<any> {
|
2018-01-17 12:12:23 +01:00
|
|
|
// Show confirmation if needed.
|
|
|
|
return this.domUtils.confirmDownloadSize(size).then(() => {
|
|
|
|
// Invalidate content if refreshing and download the data.
|
2018-01-29 10:05:20 +01:00
|
|
|
const promise = refresh ? handler.invalidateContent(module.id, courseId) : Promise.resolve();
|
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
return promise.catch(() => {
|
|
|
|
// Ignore errors.
|
|
|
|
}).then(() => {
|
|
|
|
return handler.prefetch(module, courseId, true);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prefetch one section or all the sections.
|
|
|
|
* If the section is "All sections" it will prefetch all the sections.
|
|
|
|
*
|
|
|
|
* @param {any} section Section.
|
|
|
|
* @param {number} courseId Course ID the section belongs to.
|
|
|
|
* @param {any[]} [sections] List of sections. Used when downloading all the sections.
|
|
|
|
* @return {Promise<any>} Promise resolved when the prefetch is finished.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
prefetchSection(section: any, courseId: number, sections?: any[]): Promise<any> {
|
2018-01-17 12:12:23 +01:00
|
|
|
if (section.id != CoreCourseProvider.ALL_SECTIONS_ID) {
|
|
|
|
// Download only this section.
|
|
|
|
return this.prefetchSingleSectionIfNeeded(section, courseId).then(() => {
|
|
|
|
// Calculate the status of the section that finished.
|
|
|
|
return this.calculateSectionStatus(section, courseId);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// Download all the sections except "All sections".
|
2018-01-29 10:05:20 +01:00
|
|
|
const promises = [];
|
|
|
|
let allSectionsStatus;
|
2018-01-17 12:12:23 +01:00
|
|
|
|
|
|
|
section.isDownloading = true;
|
|
|
|
sections.forEach((section) => {
|
|
|
|
if (section.id != CoreCourseProvider.ALL_SECTIONS_ID) {
|
|
|
|
promises.push(this.prefetchSingleSectionIfNeeded(section, courseId).then(() => {
|
|
|
|
// Calculate the status of the section that finished.
|
|
|
|
return this.calculateSectionStatus(section, courseId).then((result) => {
|
|
|
|
// Calculate "All sections" status.
|
|
|
|
allSectionsStatus = this.filepoolProvider.determinePackagesStatus(allSectionsStatus, result.status);
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return this.utils.allPromises(promises).then(() => {
|
|
|
|
// Set "All sections" data.
|
|
|
|
section.showDownload = allSectionsStatus === CoreConstants.NOT_DOWNLOADED;
|
|
|
|
section.showRefresh = allSectionsStatus === CoreConstants.OUTDATED;
|
|
|
|
section.isDownloading = allSectionsStatus === CoreConstants.DOWNLOADING;
|
|
|
|
}).finally(() => {
|
|
|
|
section.isDownloading = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prefetch a certain section if it needs to be prefetched.
|
|
|
|
* If the section is "All sections" it will be ignored.
|
|
|
|
*
|
|
|
|
* @param {any} section Section to prefetch.
|
|
|
|
* @param {number} courseId Course ID the section belongs to.
|
|
|
|
* @return {Promise<any>} Promise resolved when the section is prefetched.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
protected prefetchSingleSectionIfNeeded(section: any, courseId: number): Promise<any> {
|
2018-01-17 12:12:23 +01:00
|
|
|
if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
section.isDownloading = true;
|
|
|
|
|
|
|
|
// Validate the section needs to be downloaded and calculate amount of modules that need to be downloaded.
|
|
|
|
return this.prefetchDelegate.getModulesStatus(section.modules, courseId, section.id).then((result) => {
|
|
|
|
if (result.status == CoreConstants.DOWNLOADED || result.status == CoreConstants.NOT_DOWNLOADABLE) {
|
|
|
|
// Section is downloaded or not downloadable, nothing to do.
|
|
|
|
return;
|
|
|
|
}
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
return this.prefetchSingleSection(section, result, courseId);
|
|
|
|
}, (error) => {
|
|
|
|
section.isDownloading = false;
|
2018-01-29 10:05:20 +01:00
|
|
|
|
2018-01-17 12:12:23 +01:00
|
|
|
return Promise.reject(error);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Start or restore the prefetch of a section.
|
|
|
|
* If the section is "All sections" it will be ignored.
|
|
|
|
*
|
|
|
|
* @param {any} section Section to download.
|
|
|
|
* @param {any} result Result of CoreCourseModulePrefetchDelegate.getModulesStatus for this section.
|
|
|
|
* @param {number} courseId Course ID the section belongs to.
|
|
|
|
* @return {Promise<any>} Promise resolved when the section has been prefetched.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
protected prefetchSingleSection(section: any, result: any, courseId: number): Promise<any> {
|
2018-01-17 12:12:23 +01:00
|
|
|
if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (section.total > 0) {
|
|
|
|
// Already being downloaded.
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
// We only download modules with status notdownloaded, downloading or outdated.
|
2018-01-29 10:05:20 +01:00
|
|
|
const modules = result[CoreConstants.OUTDATED].concat(result[CoreConstants.NOT_DOWNLOADED])
|
|
|
|
.concat(result[CoreConstants.DOWNLOADING]),
|
2018-01-17 12:12:23 +01:00
|
|
|
downloadId = this.getSectionDownloadId(section);
|
|
|
|
|
|
|
|
section.isDownloading = true;
|
|
|
|
|
2018-01-29 10:05:20 +01:00
|
|
|
// Prefetch all modules to prevent incoeherences in download count and to download stale data not marked as outdated.
|
2018-01-17 12:12:23 +01:00
|
|
|
return this.prefetchDelegate.prefetchModules(downloadId, modules, courseId, (data) => {
|
|
|
|
section.count = data.count;
|
|
|
|
section.total = data.total;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-12-29 09:18:17 +01:00
|
|
|
/**
|
|
|
|
* Check if a section has content.
|
|
|
|
*
|
|
|
|
* @param {any} section Section to check.
|
|
|
|
* @return {boolean} Whether the section has content.
|
|
|
|
*/
|
2018-01-29 10:05:20 +01:00
|
|
|
sectionHasContent(section: any): boolean {
|
2017-12-29 09:18:17 +01:00
|
|
|
if (section.id == CoreCourseProvider.ALL_SECTIONS_ID || section.hiddenbynumsections) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (typeof section.availabilityinfo != 'undefined' && section.availabilityinfo != '') ||
|
2018-01-29 10:05:20 +01:00
|
|
|
section.summary != '' || (section.modules && section.modules.length > 0);
|
2017-12-29 09:18:17 +01:00
|
|
|
}
|
|
|
|
}
|