MOBILE-2317 core: Fix tslint warnings

main
Dani Palou 2018-01-29 10:05:20 +01:00
parent 76eed98cb5
commit 01ddf7019c
204 changed files with 4243 additions and 3872 deletions

View File

@ -6,7 +6,21 @@ var gulp = require('gulp'),
slash = require('gulp-slash'), slash = require('gulp-slash'),
clipEmptyFiles = require('gulp-clip-empty-files'), clipEmptyFiles = require('gulp-clip-empty-files'),
gutil = require('gulp-util'), gutil = require('gulp-util'),
File = gutil.File; File = gutil.File,
license = '' +
'// (C) Copyright 2015 Martin Dougiamas\n' +
'//\n' +
'// Licensed under the Apache License, Version 2.0 (the "License");\n' +
'// you may not use this file except in compliance with the License.\n' +
'// You may obtain a copy of the License at\n' +
'//\n' +
'// http://www.apache.org/licenses/LICENSE-2.0\n' +
'//\n' +
'// Unless required by applicable law or agreed to in writing, software\n' +
'// distributed under the License is distributed on an "AS IS" BASIS,\n' +
'// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' +
'// See the License for the specific language governing permissions and\n' +
'// limitations under the License.\n\n';
// Get the names of the JSON files inside a directory. // Get the names of the JSON files inside a directory.
function getFilenames(dir) { function getFilenames(dir) {
@ -211,15 +225,40 @@ gulp.task('config', function(done) {
gulp.src(paths.config) gulp.src(paths.config)
.pipe(through(function(file) { .pipe(through(function(file) {
// Convert the contents of the file into a TypeScript class. // Convert the contents of the file into a TypeScript class.
// Disable the rule variable-name in the file.
var config = JSON.parse(file.contents.toString()), var config = JSON.parse(file.contents.toString()),
contents = 'export class CoreConfigConstants {\n'; contents = license + '// tslint:disable: variable-name\n' + 'export class CoreConfigConstants {\n';
for (var key in config) { for (var key in config) {
var value = config[key]; var value = config[key];
if (typeof value != 'number' && typeof value != 'boolean') { if (typeof value == 'string') {
value = JSON.stringify(value); // Wrap the string in ' .
value = "'" + value + "'";
} else if (typeof value != 'number' && typeof value != 'boolean') {
// Stringify with 4 spaces of indentation, and then add 4 more spaces in each line.
value = JSON.stringify(value, null, 4).replace(/^(?: )/gm, ' ').replace(/^(?:})/gm, ' }');
// Replace " by ' in values.
value = value.replace(/: "([^"]*)"/g, ": '$1'");
// Check if the keys have "-" in it.
var matches = value.match(/"([^"]*\-[^"]*)":/g);
if (matches) {
// Replace " by ' in keys. We cannot remove them because keys have chars like '-'.
value = value.replace(/"([^"]*)":/g, "'$1':");
} else {
// Remove ' in keys.
value = value.replace(/"([^"]*)":/g, "$1:");
}
// Add type any to the key.
key = key + ': any';
} }
contents += ' public static ' + key + ' = ' + value + ';\n';
// If key has quotation marks, remove them.
if (key[0] == '"') {
key = key.substr(1, key.length - 2);
}
contents += ' static ' + key + ' = ' + value + ';\n';
} }
contents += '}\n'; contents += '}\n';

View File

@ -42,7 +42,6 @@ export class AddonCalendarModule {
calendarProvider.scheduleAllSitesEventsNotifications(); calendarProvider.scheduleAllSitesEventsNotifications();
}); });
localNotificationsProvider.registerClick(AddonCalendarProvider.COMPONENT, (data) => { localNotificationsProvider.registerClick(AddonCalendarProvider.COMPONENT, (data) => {
if (data.eventid) { if (data.eventid) {
initDelegate.ready().then(() => { initDelegate.ready().then(() => {
@ -58,4 +57,4 @@ export class AddonCalendarModule {
} }
}); });
} }
} }

View File

@ -27,7 +27,7 @@ import * as moment from 'moment';
/** /**
* Page that displays a single calendar event. * Page that displays a single calendar event.
*/ */
@IonicPage({segment: "addon-calendar-event"}) @IonicPage({ segment: 'addon-calendar-event' })
@Component({ @Component({
selector: 'page-addon-calendar-event', selector: 'page-addon-calendar-event',
templateUrl: 'event.html', templateUrl: 'event.html',
@ -72,13 +72,13 @@ export class AddonCalendarEventPage {
/** /**
* View loaded. * View loaded.
*/ */
ionViewDidLoad() { ionViewDidLoad(): void {
this.fetchEvent().finally(() => { this.fetchEvent().finally(() => {
this.eventLoaded = true; this.eventLoaded = true;
}); });
} }
updateNotificationTime() { updateNotificationTime(): void {
if (!isNaN(this.notificationTime) && this.event && this.event.id) { if (!isNaN(this.notificationTime) && this.event && this.event.id) {
this.calendarProvider.updateNotificationTime(this.event, this.notificationTime); this.calendarProvider.updateNotificationTime(this.event, this.notificationTime);
} }
@ -86,8 +86,10 @@ export class AddonCalendarEventPage {
/** /**
* Fetches the event and updates the view. * Fetches the event and updates the view.
*
* @return {Promise<any>} Promise resolved when done.
*/ */
fetchEvent() { fetchEvent(): Promise<any> {
return this.calendarProvider.getEvent(this.eventId).then((event) => { return this.calendarProvider.getEvent(this.eventId).then((event) => {
this.calendarHelper.formatEventData(event); this.calendarHelper.formatEventData(event);
this.event = event; this.event = event;
@ -96,14 +98,14 @@ export class AddonCalendarEventPage {
let title = this.translate.instant('addon.calendar.type' + event.eventtype); let title = this.translate.instant('addon.calendar.type' + event.eventtype);
if (event.moduleIcon) { if (event.moduleIcon) {
// It's a module event, translate the module name to the current language. // It's a module event, translate the module name to the current language.
let name = this.courseProvider.translateModuleName(event.modulename); const name = this.courseProvider.translateModuleName(event.modulename);
if (name.indexOf('core.mod_') === -1) { if (name.indexOf('core.mod_') === -1) {
event.moduleName = name; event.moduleName = name;
} }
if (title == 'addon.calendar.type' + event.eventtype) { if (title == 'addon.calendar.type' + event.eventtype) {
title = this.translate.instant('core.mod_'+ event.modulename + '.' + event.eventtype); title = this.translate.instant('core.mod_' + event.modulename + '.' + event.eventtype);
if (title == 'core.mod_'+ event.modulename + '.' + event.eventtype) { if (title == 'core.mod_' + event.modulename + '.' + event.eventtype) {
title = name; title = name;
} }
} }
@ -130,11 +132,11 @@ export class AddonCalendarEventPage {
* *
* @param {any} refresher Refresher. * @param {any} refresher Refresher.
*/ */
refreshEvent(refresher: any) { refreshEvent(refresher: any): void {
this.calendarProvider.invalidateEvent(this.eventId).finally(() => { this.calendarProvider.invalidateEvent(this.eventId).finally(() => {
this.fetchEvent().finally(() => { this.fetchEvent().finally(() => {
refresher.complete(); refresher.complete();
}); });
}); });
} }
} }

View File

@ -30,7 +30,7 @@ import { CoreSplitViewComponent } from '../../../../components/split-view/split-
/** /**
* Page that displays the list of calendar events. * Page that displays the list of calendar events.
*/ */
@IonicPage({segment: "addon-calendar-list"}) @IonicPage({ segment: 'addon-calendar-list' })
@Component({ @Component({
selector: 'page-addon-calendar-list', selector: 'page-addon-calendar-list',
templateUrl: 'list.html', templateUrl: 'list.html',
@ -84,7 +84,7 @@ export class AddonCalendarListPage implements OnDestroy {
/** /**
* View loaded. * View loaded.
*/ */
ionViewDidLoad() { ionViewDidLoad(): void {
if (this.eventId) { if (this.eventId) {
// There is an event to load, open the event in a new state. // There is an event to load, open the event in a new state.
this.gotoEvent(this.eventId); this.gotoEvent(this.eventId);
@ -104,8 +104,9 @@ export class AddonCalendarListPage implements OnDestroy {
* Fetch all the data required for the view. * Fetch all the data required for the view.
* *
* @param {boolean} [refresh] Empty events array first. * @param {boolean} [refresh] Empty events array first.
* @return {Promise<any>} Promise resolved when done.
*/ */
fetchData(refresh = false) { fetchData(refresh: boolean = false): Promise<any> {
this.daysLoaded = 0; this.daysLoaded = 0;
this.emptyEventsTimes = 0; this.emptyEventsTimes = 0;
@ -114,6 +115,7 @@ export class AddonCalendarListPage implements OnDestroy {
// Add "All courses". // Add "All courses".
courses.unshift(this.allCourses); courses.unshift(this.allCourses);
this.courses = courses; this.courses = courses;
return this.fetchEvents(refresh); return this.fetchEvents(refresh);
}); });
} }
@ -122,8 +124,9 @@ export class AddonCalendarListPage implements OnDestroy {
* Fetches the events and updates the view. * Fetches the events and updates the view.
* *
* @param {boolean} [refresh] Empty events array first. * @param {boolean} [refresh] Empty events array first.
* @return {Promise<any>} Promise resolved when done.
*/ */
fetchEvents(refresh = false) { fetchEvents(refresh: boolean = false): Promise<any> {
return this.calendarProvider.getEventsList(this.daysLoaded, AddonCalendarProvider.DAYS_INTERVAL).then((events) => { return this.calendarProvider.getEventsList(this.daysLoaded, AddonCalendarProvider.DAYS_INTERVAL).then((events) => {
this.daysLoaded += AddonCalendarProvider.DAYS_INTERVAL; this.daysLoaded += AddonCalendarProvider.DAYS_INTERVAL;
if (events.length === 0) { if (events.length === 0) {
@ -170,6 +173,7 @@ export class AddonCalendarListPage implements OnDestroy {
// Success retrieving events. Get categories if needed. // Success retrieving events. Get categories if needed.
if (this.getCategories) { if (this.getCategories) {
this.getCategories = false; this.getCategories = false;
return this.loadCategories(); return this.loadCategories();
} }
}); });
@ -177,8 +181,10 @@ export class AddonCalendarListPage implements OnDestroy {
/** /**
* Get filtered events. * Get filtered events.
*
* @return {any[]} Filtered events.
*/ */
protected getFilteredEvents() { protected getFilteredEvents(): any[] {
if (this.filter.course.id == -1) { if (this.filter.course.id == -1) {
// No filter, display everything. // No filter, display everything.
return this.events; return this.events;
@ -191,8 +197,9 @@ export class AddonCalendarListPage implements OnDestroy {
* Check if an event should be displayed based on the filter. * Check if an event should be displayed based on the filter.
* *
* @param {any} event Event object. * @param {any} event Event object.
* @return {boolean} Whether it should be displayed.
*/ */
protected shouldDisplayEvent(event: any) { protected shouldDisplayEvent(event: any): boolean {
if (event.eventtype == 'user' || event.eventtype == 'site') { if (event.eventtype == 'user' || event.eventtype == 'site') {
// User or site event, display it. // User or site event, display it.
return true; return true;
@ -234,21 +241,24 @@ export class AddonCalendarListPage implements OnDestroy {
* @param {any[]} events Events to parse. * @param {any[]} events Events to parse.
* @return {boolean} True if categories should be loaded. * @return {boolean} True if categories should be loaded.
*/ */
protected shouldLoadCategories(events: any[]) : boolean { protected shouldLoadCategories(events: any[]): boolean {
if (this.categoriesRetrieved || this.getCategories) { if (this.categoriesRetrieved || this.getCategories) {
// Use previous value // Use previous value
return this.getCategories; return this.getCategories;
} }
// Categories not loaded yet. We should get them if there's any category event. // Categories not loaded yet. We should get them if there's any category event.
let found = events.some(event => event.categoryid != 'undefined' && event.categoryid > 0); const found = events.some((event) => event.categoryid != 'undefined' && event.categoryid > 0);
return found || this.getCategories; return found || this.getCategories;
} }
/** /**
* Load categories to be able to filter events. * Load categories to be able to filter events.
*
* @return {Promise<any>} Promise resolved when done.
*/ */
protected loadCategories() { protected loadCategories(): Promise<any> {
return this.coursesProvider.getCategories(0, true).then((cats) => { return this.coursesProvider.getCategories(0, true).then((cats) => {
this.categoriesRetrieved = true; this.categoriesRetrieved = true;
this.categories = {}; this.categories = {};
@ -266,8 +276,8 @@ export class AddonCalendarListPage implements OnDestroy {
* *
* @param {any} refresher Refresher. * @param {any} refresher Refresher.
*/ */
refreshEvents(refresher: any) { refreshEvents(refresher: any): void {
let promises = []; const promises = [];
promises.push(this.calendarProvider.invalidateEventsList(this.courses)); promises.push(this.calendarProvider.invalidateEventsList(this.courses));
@ -288,9 +298,11 @@ export class AddonCalendarListPage implements OnDestroy {
* *
* @param {MouseEvent} event Event. * @param {MouseEvent} event Event.
*/ */
openCourseFilter(event: MouseEvent) : void { openCourseFilter(event: MouseEvent): void {
let popover = this.popoverCtrl.create(CoreCoursePickerMenuPopoverComponent, {courses: this.courses, const popover = this.popoverCtrl.create(CoreCoursePickerMenuPopoverComponent, {
courseId: this.filter.course.id}); courses: this.courses,
courseId: this.filter.course.id
});
popover.onDidDismiss((course) => { popover.onDidDismiss((course) => {
if (course) { if (course) {
this.filter.course = course; this.filter.course = course;
@ -306,22 +318,24 @@ export class AddonCalendarListPage implements OnDestroy {
/** /**
* Open calendar events settings. * Open calendar events settings.
*/ */
openSettings() { openSettings(): void {
this.navCtrl.push('AddonCalendarSettingsPage'); this.navCtrl.push('AddonCalendarSettingsPage');
} }
/** /**
* Navigate to a particular event. * Navigate to a particular event.
*
* @param {number} eventId Event to load.
*/ */
gotoEvent(eventId) { gotoEvent(eventId: number): void {
this.eventId = eventId; this.eventId = eventId;
this.splitviewCtrl.push('AddonCalendarEventPage', {id: eventId}); this.splitviewCtrl.push('AddonCalendarEventPage', { id: eventId });
} }
/** /**
* Page destroyed. * Page destroyed.
*/ */
ngOnDestroy() { ngOnDestroy(): void {
this.obsDefaultTimeChange && this.obsDefaultTimeChange.off(); this.obsDefaultTimeChange && this.obsDefaultTimeChange.off();
} }
} }

View File

@ -21,7 +21,7 @@ import { CoreSitesProvider } from '../../../../providers/sites';
/** /**
* Page that displays the calendar settings. * Page that displays the calendar settings.
*/ */
@IonicPage({segment: "addon-calendar-settings"}) @IonicPage({ segment: 'addon-calendar-settings' })
@Component({ @Component({
selector: 'page-addon-calendar-settings', selector: 'page-addon-calendar-settings',
templateUrl: 'settings.html', templateUrl: 'settings.html',
@ -31,20 +31,25 @@ export class AddonCalendarSettingsPage {
defaultTime = 0; defaultTime = 0;
constructor(private calendarProvider: AddonCalendarProvider, private eventsProvider: CoreEventsProvider, constructor(private calendarProvider: AddonCalendarProvider, private eventsProvider: CoreEventsProvider,
private sitesProvider: CoreSitesProvider) {} private sitesProvider: CoreSitesProvider) { }
/** /**
* View loaded. * View loaded.
*/ */
ionViewDidLoad() { ionViewDidLoad(): void {
this.calendarProvider.getDefaultNotificationTime().then((time) => { this.calendarProvider.getDefaultNotificationTime().then((time) => {
this.defaultTime = time; this.defaultTime = time;
}); });
} }
updateDefaultTime(newTime) { /**
* Update default time.
*
* @param {number} newTime New time.
*/
updateDefaultTime(newTime: number): void {
this.calendarProvider.setDefaultNotificationTime(newTime); this.calendarProvider.setDefaultNotificationTime(newTime);
this.eventsProvider.trigger(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, {time: newTime}, this.eventsProvider.trigger(AddonCalendarProvider.DEFAULT_NOTIFICATION_TIME_CHANGED, { time: newTime },
this.sitesProvider.getCurrentSiteId()); this.sitesProvider.getCurrentSiteId());
}; }
} }

View File

@ -28,9 +28,9 @@ import { CoreConfigProvider } from '../../../providers/config';
*/ */
@Injectable() @Injectable()
export class AddonCalendarProvider { export class AddonCalendarProvider {
public static DAYS_INTERVAL = 30; static DAYS_INTERVAL = 30;
public static COMPONENT = 'AddonCalendarEvents'; static COMPONENT = 'AddonCalendarEvents';
public static DEFAULT_NOTIFICATION_TIME_CHANGED = 'AddonCalendarDefaultNotificationTimeChangedEvent'; static DEFAULT_NOTIFICATION_TIME_CHANGED = 'AddonCalendarDefaultNotificationTimeChangedEvent';
protected DEFAULT_NOTIFICATION_TIME_SETTING = 'mmaCalendarDefaultNotifTime'; protected DEFAULT_NOTIFICATION_TIME_SETTING = 'mmaCalendarDefaultNotifTime';
protected ROOT_CACHE_KEY = 'mmaCalendar:'; protected ROOT_CACHE_KEY = 'mmaCalendar:';
protected DEFAULT_NOTIFICATION_TIME = 60; protected DEFAULT_NOTIFICATION_TIME = 60;
@ -118,10 +118,11 @@ export class AddonCalendarProvider {
* @param {string} [siteId] ID of the site. If not defined, use current site. * @param {string} [siteId] ID of the site. If not defined, use current site.
* @return {Promise<number>} Promise resolved with the default time. * @return {Promise<number>} Promise resolved with the default time.
*/ */
getDefaultNotificationTime(siteId?: string) : Promise<number> { getDefaultNotificationTime(siteId?: string): Promise<number> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
let key = this.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId; const key = this.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId;
return this.configProvider.get(key, this.DEFAULT_NOTIFICATION_TIME); return this.configProvider.get(key, this.DEFAULT_NOTIFICATION_TIME);
} }
@ -133,19 +134,27 @@ export class AddonCalendarProvider {
* @param {string} [siteId] ID of the site. If not defined, use current site. * @param {string} [siteId] ID of the site. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the event data is retrieved. * @return {Promise<any>} Promise resolved when the event data is retrieved.
*/ */
getEvent(id: number, siteId?: string) : Promise<any> { getEvent(id: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let presets = { const preSets = {
cacheKey: this.getEventCacheKey(id) cacheKey: this.getEventCacheKey(id)
}, },
data = { data = {
"options[userevents]": 0, options: {
"options[siteevents]": 0, userevents: 0,
"events[eventids][0]": id siteevents: 0,
},
events: {
eventids: [
id
]
}
}; };
return site.read('core_calendar_get_calendar_events', data, presets).then((response) => {
return site.read('core_calendar_get_calendar_events', data, preSets).then((response) => {
// The WebService returns all category events. Check the response to search for the event we want. // The WebService returns all category events. Check the response to search for the event we want.
let event = response.events.find((e) => {return e.id == id}); const event = response.events.find((e) => { return e.id == id; });
return event || this.getEventFromLocalDb(id); return event || this.getEventFromLocalDb(id);
}).catch(() => { }).catch(() => {
return this.getEventFromLocalDb(id); return this.getEventFromLocalDb(id);
@ -170,9 +179,9 @@ export class AddonCalendarProvider {
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site. * @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the event data is retrieved. * @return {Promise<any>} Promise resolved when the event data is retrieved.
*/ */
getEventFromLocalDb(id: number, siteId?: string) : Promise<any> { getEventFromLocalDb(id: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.getDb().getRecord(this.EVENTS_TABLE, {id: id}); return site.getDb().getRecord(this.EVENTS_TABLE, { id: id });
}); });
} }
@ -183,13 +192,14 @@ export class AddonCalendarProvider {
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site. * @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
* @return {Promise<number>} Event notification time in minutes. 0 if disabled. * @return {Promise<number>} Event notification time in minutes. 0 if disabled.
*/ */
getEventNotificationTime(id: number, siteId?: string) : Promise<number> { getEventNotificationTime(id: number, siteId?: string): Promise<number> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
return this.getEventNotificationTimeOption(id, siteId).then((time: number) => { return this.getEventNotificationTimeOption(id, siteId).then((time: number) => {
if (time == -1) { if (time == -1) {
return this.getDefaultNotificationTime(siteId); return this.getDefaultNotificationTime(siteId);
} }
return time; return time;
}); });
} }
@ -201,7 +211,7 @@ export class AddonCalendarProvider {
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site. * @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
* @return {Promise<number>} Promise with wvent notification time in minutes. 0 if disabled, -1 if default time. * @return {Promise<number>} Promise with wvent notification time in minutes. 0 if disabled, -1 if default time.
*/ */
getEventNotificationTimeOption(id: number, siteId?: string) : Promise<number> { getEventNotificationTimeOption(id: number, siteId?: string): Promise<number> {
return this.getEventFromLocalDb(id, siteId).then((e) => { return this.getEventFromLocalDb(id, siteId).then((e) => {
return e.notificationtime || -1; return e.notificationtime || -1;
}).catch(() => { }).catch(() => {
@ -221,42 +231,48 @@ export class AddonCalendarProvider {
* @param {string} [siteId] Site to get the events from. If not defined, use current site. * @param {string} [siteId] Site to get the events from. If not defined, use current site.
* @return {Promise<any[]>} Promise to be resolved when the participants are retrieved. * @return {Promise<any[]>} Promise to be resolved when the participants are retrieved.
*/ */
getEventsList(daysToStart = 0, daysInterval = AddonCalendarProvider.DAYS_INTERVAL, siteId?: string) : Promise<any[]> { getEventsList(daysToStart: number = 0, daysInterval: number = AddonCalendarProvider.DAYS_INTERVAL, siteId?: string)
: Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
siteId = site.getId(); siteId = site.getId();
return this.coursesProvider.getUserCourses(false, siteId).then((courses) => { return this.coursesProvider.getUserCourses(false, siteId).then((courses) => {
courses.push({id: site.getSiteHomeId()}); // Add front page. courses.push({ id: site.getSiteHomeId() }); // Add front page.
return this.groupsProvider.getUserGroups(courses, siteId).then((groups) => { return this.groupsProvider.getUserGroups(courses, siteId).then((groups) => {
let now = this.timeUtils.timestamp(), const now = this.timeUtils.timestamp(),
start = now + (CoreConstants.SECONDS_DAY * daysToStart), start = now + (CoreConstants.SECONDS_DAY * daysToStart),
end = start + (CoreConstants.SECONDS_DAY * daysInterval); end = start + (CoreConstants.SECONDS_DAY * daysInterval),
data = {
// The core_calendar_get_calendar_events needs all the current user courses and groups. options: {
let data = { userevents: 1,
"options[userevents]": 1, siteevents: 1,
"options[siteevents]": 1, timestart: start,
"options[timestart]": start, timeend: end
"options[timeend]": end },
}; events: {
courseids: [],
groupids: []
}
};
courses.forEach((course, index) => { courses.forEach((course, index) => {
data["events[courseids][" + index + "]"] = course.id; data.events.courseids[index] = course.id;
}); });
groups.forEach((group, index) => { groups.forEach((group, index) => {
data["events[groupids][" + index + "]"] = group.id; data.events.groupids[index] = group.id;
}); });
// We need to retrieve cached data using cache key because we have timestamp in the params. // We need to retrieve cached data using cache key because we have timestamp in the params.
let preSets = { const preSets = {
cacheKey: this.getEventsListCacheKey(daysToStart, daysInterval), cacheKey: this.getEventsListCacheKey(daysToStart, daysInterval),
getCacheUsingCacheKey: true getCacheUsingCacheKey: true
}; };
return site.read('core_calendar_get_calendar_events', data, preSets).then((response) => { return site.read('core_calendar_get_calendar_events', data, preSets).then((response) => {
this.storeEventsInLocalDB(response.events, siteId); this.storeEventsInLocalDB(response.events, siteId);
return response.events; return response.events;
}); });
}); });
@ -269,7 +285,7 @@ export class AddonCalendarProvider {
* *
* @return {string} Prefix Cache key. * @return {string} Prefix Cache key.
*/ */
protected getEventsListPrefixCacheKey() : string { protected getEventsListPrefixCacheKey(): string {
return this.ROOT_CACHE_KEY + 'eventslist:'; return this.ROOT_CACHE_KEY + 'eventslist:';
} }
@ -280,7 +296,7 @@ export class AddonCalendarProvider {
* @param {number} daysInterval Number of days between timestart and timeend. * @param {number} daysInterval Number of days between timestart and timeend.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getEventsListCacheKey(daysToStart: number, daysInterval: number) : string { protected getEventsListCacheKey(daysToStart: number, daysInterval: number): string {
return this.getEventsListPrefixCacheKey() + daysToStart + ':' + daysInterval; return this.getEventsListPrefixCacheKey() + daysToStart + ':' + daysInterval;
} }
@ -291,27 +307,28 @@ export class AddonCalendarProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<any[]>} Promise resolved when the list is invalidated. * @return {Promise<any[]>} Promise resolved when the list is invalidated.
*/ */
invalidateEventsList(courses: any[], siteId?: string) : Promise<any[]> { invalidateEventsList(courses: any[], siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
siteId = site.getId(); siteId = site.getId();
let promises = []; const promises = [];
promises.push(this.coursesProvider.invalidateUserCourses(siteId)); promises.push(this.coursesProvider.invalidateUserCourses(siteId));
promises.push(this.groupsProvider.invalidateUserGroups(courses, siteId)); promises.push(this.groupsProvider.invalidateUserGroups(courses, siteId));
promises.push(site.invalidateWsCacheForKeyStartingWith(this.getEventsListPrefixCacheKey())); promises.push(site.invalidateWsCacheForKeyStartingWith(this.getEventsListPrefixCacheKey()));
return Promise.all(promises); return Promise.all(promises);
}); });
} }
/** /**
* Invalidates a single event. * Invalidates a single event.
* *
* @param {number} eventId List of courses or course ids. * @param {number} eventId List of courses or course ids.
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the list is invalidated. * @return {Promise<any>} Promise resolved when the list is invalidated.
*/ */
invalidateEvent(eventId: number, siteId?: string) : Promise<any> { invalidateEvent(eventId: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getEventCacheKey(eventId)); return site.invalidateWsCacheForKey(this.getEventCacheKey(eventId));
}); });
@ -323,8 +340,9 @@ export class AddonCalendarProvider {
* @param {CoreSite} [site] Site. If not defined, use current site. * @param {CoreSite} [site] Site. If not defined, use current site.
* @return {boolean} Whether it's disabled. * @return {boolean} Whether it's disabled.
*/ */
isCalendarDisabledInSite(site?: CoreSite) : boolean { isCalendarDisabledInSite(site?: CoreSite): boolean {
site = site || this.sitesProvider.getCurrentSite(); site = site || this.sitesProvider.getCurrentSite();
return site.isFeatureDisabled('$mmSideMenuDelegate_mmaCalendar'); return site.isFeatureDisabled('$mmSideMenuDelegate_mmaCalendar');
} }
@ -334,13 +352,12 @@ export class AddonCalendarProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<boolean>} Promise resolved with true if disabled, rejected or resolved with false otherwise. * @return {Promise<boolean>} Promise resolved with true if disabled, rejected or resolved with false otherwise.
*/ */
isDisabled(siteId?: string) : Promise<boolean> { isDisabled(siteId?: string): Promise<boolean> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return this.isCalendarDisabledInSite(site); return this.isCalendarDisabledInSite(site);
}); });
} }
/** /**
* Get the next events for all the sites and schedules their notifications. * Get the next events for all the sites and schedules their notifications.
* If an event notification time is 0, cancel its scheduled notification (if any). * If an event notification time is 0, cancel its scheduled notification (if any).
@ -348,10 +365,10 @@ export class AddonCalendarProvider {
* *
* @return {Promise} Promise resolved when all the notifications have been scheduled. * @return {Promise} Promise resolved when all the notifications have been scheduled.
*/ */
scheduleAllSitesEventsNotifications() : Promise<any[]> { scheduleAllSitesEventsNotifications(): Promise<any[]> {
if (this.localNotificationsProvider.isAvailable()) { if (this.localNotificationsProvider.isAvailable()) {
return this.sitesProvider.getSitesIds().then((siteIds) => { return this.sitesProvider.getSitesIds().then((siteIds) => {
let promises = []; const promises = [];
siteIds.forEach((siteId) => { siteIds.forEach((siteId) => {
// Check if calendar is disabled for the site. // Check if calendar is disabled for the site.
@ -381,7 +398,7 @@ export class AddonCalendarProvider {
* @param {string} [siteId] Site ID the event belongs to. If not defined, use current site. * @param {string} [siteId] Site ID the event belongs to. If not defined, use current site.
* @return {Promise<void>} Promise resolved when the notification is scheduled. * @return {Promise<void>} Promise resolved when the notification is scheduled.
*/ */
scheduleEventNotification(event: any, time: number, siteId?: string) : Promise<void> { scheduleEventNotification(event: any, time: number, siteId?: string): Promise<void> {
if (this.localNotificationsProvider.isAvailable()) { if (this.localNotificationsProvider.isAvailable()) {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
@ -391,16 +408,16 @@ export class AddonCalendarProvider {
} }
// If time is -1, get event default time. // If time is -1, get event default time.
let promise = time == -1 ? this.getDefaultNotificationTime(siteId) : Promise.resolve(time); const promise = time == -1 ? this.getDefaultNotificationTime(siteId) : Promise.resolve(time);
return promise.then((time) => { return promise.then((time) => {
let timeEnd = (event.timestart + event.timeduration) * 1000; const timeEnd = (event.timestart + event.timeduration) * 1000;
if (timeEnd <= new Date().getTime()) { if (timeEnd <= new Date().getTime()) {
// The event has finished already, don't schedule it. // The event has finished already, don't schedule it.
return Promise.resolve(); return Promise.resolve();
} }
let dateTriggered = new Date((event.timestart - (time * 60)) * 1000), const dateTriggered = new Date((event.timestart - (time * 60)) * 1000),
startDate = new Date(event.timestart * 1000), startDate = new Date(event.timestart * 1000),
notification = { notification = {
id: event.id, id: event.id,
@ -421,7 +438,6 @@ export class AddonCalendarProvider {
} }
} }
/** /**
* Schedules the notifications for a list of events. * Schedules the notifications for a list of events.
* If an event notification time is 0, cancel its scheduled notification (if any). * If an event notification time is 0, cancel its scheduled notification (if any).
@ -431,8 +447,8 @@ export class AddonCalendarProvider {
* @param {string} [siteId] ID of the site the events belong to. If not defined, use current site. * @param {string} [siteId] ID of the site the events belong to. If not defined, use current site.
* @return {Promise<any[]>} Promise resolved when all the notifications have been scheduled. * @return {Promise<any[]>} Promise resolved when all the notifications have been scheduled.
*/ */
scheduleEventsNotifications(events: any[], siteId?: string) : Promise<any[]> { scheduleEventsNotifications(events: any[], siteId?: string): Promise<any[]> {
var promises = []; const promises = [];
if (this.localNotificationsProvider.isAvailable()) { if (this.localNotificationsProvider.isAvailable()) {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
@ -453,10 +469,11 @@ export class AddonCalendarProvider {
* @param {string} [siteId] ID of the site. If not defined, use current site. * @param {string} [siteId] ID of the site. If not defined, use current site.
* @return {Promise<any[]>} Promise resolved when stored. * @return {Promise<any[]>} Promise resolved when stored.
*/ */
setDefaultNotificationTime(time: number, siteId?: string) : Promise<any[]> { setDefaultNotificationTime(time: number, siteId?: string): Promise<any[]> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
let key = this.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId; const key = this.DEFAULT_NOTIFICATION_TIME_SETTING + '#' + siteId;
return this.configProvider.set(key, time); return this.configProvider.set(key, time);
} }
@ -467,11 +484,11 @@ export class AddonCalendarProvider {
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site. * @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
* @return {Promise<any[]>} Promise resolved when the events are stored. * @return {Promise<any[]>} Promise resolved when the events are stored.
*/ */
protected storeEventsInLocalDB(events: any[], siteId?: string) : Promise<any[]> { protected storeEventsInLocalDB(events: any[], siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
siteId = site.getId(); siteId = site.getId();
let promises = [], const promises = [],
db = site.getDb(); db = site.getDb();
events.forEach((event) => { events.forEach((event) => {
@ -480,7 +497,7 @@ export class AddonCalendarProvider {
// Event not stored, return empty object. // Event not stored, return empty object.
return {}; return {};
}).then((e) => { }).then((e) => {
let eventRecord = { const eventRecord = {
id: event.id, id: event.id,
name: event.name, name: event.name,
description: event.description, description: event.description,
@ -497,7 +514,7 @@ export class AddonCalendarProvider {
notificationtime: e.notificationtime || -1 notificationtime: e.notificationtime || -1
}; };
return db.insertOrUpdateRecord(this.EVENTS_TABLE, eventRecord, {id: eventRecord.id}); return db.insertOrUpdateRecord(this.EVENTS_TABLE, eventRecord, { id: eventRecord.id });
})); }));
}); });
@ -513,7 +530,7 @@ export class AddonCalendarProvider {
* @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site. * @param {string} [siteId] ID of the site the event belongs to. If not defined, use current site.
* @return {Promise<void>} Promise resolved when the notification is updated. * @return {Promise<void>} Promise resolved when the notification is updated.
*/ */
updateNotificationTime(event: any, time: number, siteId?: string) : Promise<void> { updateNotificationTime(event: any, time: number, siteId?: string): Promise<void> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
if (!this.sitesProvider.isLoggedIn()) { if (!this.sitesProvider.isLoggedIn()) {
// Not logged in, we can't get the site DB. User logged out or session expired while an operation was ongoing. // Not logged in, we can't get the site DB. User logged out or session expired while an operation was ongoing.
@ -522,7 +539,7 @@ export class AddonCalendarProvider {
event.notificationtime = time; event.notificationtime = time;
return site.getDb().insertOrUpdateRecord(this.EVENTS_TABLE, event, {id: event.id}).then(() => { return site.getDb().insertOrUpdateRecord(this.EVENTS_TABLE, event, { id: event.id }).then(() => {
return this.scheduleEventNotification(event, time); return this.scheduleEventNotification(event, time);
}); });
}); });

View File

@ -23,12 +23,12 @@ import { CoreCourseProvider } from '../../../core/course/providers/course';
export class AddonCalendarHelperProvider { export class AddonCalendarHelperProvider {
protected logger; protected logger;
private EVENTICONS = { protected EVENTICONS = {
'course': 'ionic', course: 'ionic',
'group': 'people', group: 'people',
'site': 'globe', site: 'globe',
'user': 'person', user: 'person',
'category': 'albums' category: 'albums'
}; };
constructor(logger: CoreLoggerProvider, private courseProvider: CoreCourseProvider) { constructor(logger: CoreLoggerProvider, private courseProvider: CoreCourseProvider) {
@ -40,11 +40,11 @@ export class AddonCalendarHelperProvider {
* *
* @param {any} e Event to format. * @param {any} e Event to format.
*/ */
formatEventData(e: any) { formatEventData(e: any): void {
e.icon = this.EVENTICONS[e.eventtype] || false; e.icon = this.EVENTICONS[e.eventtype] || false;
if (!e.icon) { if (!e.icon) {
e.icon = this.courseProvider.getModuleIconSrc(e.modulename); e.icon = this.courseProvider.getModuleIconSrc(e.modulename);
e.moduleIcon = e.icon; e.moduleIcon = e.icon;
} }
}; }
} }

View File

@ -24,16 +24,15 @@ export class AddonCalendarMainMenuHandler implements CoreMainMenuHandler {
name = 'AddonCalendar'; name = 'AddonCalendar';
priority = 400; priority = 400;
constructor(private calendarProvider: AddonCalendarProvider) {} constructor(private calendarProvider: AddonCalendarProvider) { }
/** /**
* Check if the handler is enabled on a site level. * Check if the handler is enabled on a site level.
* *
* @return {boolean} Whether or not the handler is enabled on a site level. * @return {boolean} Whether or not the handler is enabled on a site level.
*/ */
isEnabled(): boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
let isDisabled = this.calendarProvider.isCalendarDisabledInSite(); return !this.calendarProvider.isCalendarDisabledInSite();
return !isDisabled;
} }
/** /**

View File

@ -43,4 +43,4 @@ export class AddonUserProfileFieldCheckboxModule {
constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldCheckboxHandler) { constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldCheckboxHandler) {
userProfileFieldDelegate.registerHandler(handler); userProfileFieldDelegate.registerHandler(handler);
} }
} }

View File

@ -25,23 +25,23 @@ import { CoreUtilsProvider } from '../../../../providers/utils/utils';
}) })
export class AddonUserProfileFieldCheckboxComponent implements OnInit { export class AddonUserProfileFieldCheckboxComponent implements OnInit {
@Input() field: any; // The profile field to be rendered. @Input() field: any; // The profile field to be rendered.
@Input() edit?: boolean = false; // True if editing the field. Defaults to false. @Input() edit?: false; // True if editing the field. Defaults to false.
@Input() disabled?: boolean = false; // True if disabled. Defaults to false. @Input() disabled?: false; // True if disabled. Defaults to false.
@Input() form?: FormGroup; // Form where to add the form control. @Input() form?: FormGroup; // Form where to add the form control.
constructor(private fb: FormBuilder, protected utils: CoreUtilsProvider) {} constructor(private fb: FormBuilder, protected utils: CoreUtilsProvider) { }
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
let field = this.field; const field = this.field;
if (field && this.edit && this.form) { if (field && this.edit && this.form) {
field.modelName = 'profile_field_' + field.shortname; field.modelName = 'profile_field_' + field.shortname;
// Initialize the value. // Initialize the value.
let formData = { const formData = {
value: this.utils.isTrueOrOne(field.defaultdata), value: this.utils.isTrueOrOne(field.defaultdata),
disabled: this.disabled disabled: this.disabled
}; };
@ -49,5 +49,4 @@ export class AddonUserProfileFieldCheckboxComponent implements OnInit {
field.required && !field.locked ? Validators.requiredTrue : null)); field.required && !field.locked ? Validators.requiredTrue : null));
} }
} }
}
}

View File

@ -24,14 +24,16 @@ import { AddonUserProfileFieldCheckboxComponent } from '../component/checkbox';
export class AddonUserProfileFieldCheckboxHandler implements CoreUserProfileFieldHandler { export class AddonUserProfileFieldCheckboxHandler implements CoreUserProfileFieldHandler {
name = 'checkbox'; name = 'checkbox';
constructor() {} constructor() {
// Nothing to do.
}
/** /**
* Whether or not the handler is enabled on a site level. * Whether or not the handler is enabled on a site level.
* *
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled. * @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/ */
isEnabled() : boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
return true; return true;
} }
@ -45,7 +47,7 @@ export class AddonUserProfileFieldCheckboxHandler implements CoreUserProfileFiel
* @return {CoreUserProfileFieldHandlerData} Data to send for the field. * @return {CoreUserProfileFieldHandlerData} Data to send for the field.
*/ */
getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData { getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData {
let name = 'profile_field_' + field.shortname; const name = 'profile_field_' + field.shortname;
if (typeof formValues[name] != 'undefined') { if (typeof formValues[name] != 'undefined') {
return { return {
@ -61,8 +63,7 @@ export class AddonUserProfileFieldCheckboxHandler implements CoreUserProfileFiel
* *
* @return {any} The component to use, undefined if not found. * @return {any} The component to use, undefined if not found.
*/ */
getComponent() { getComponent(): any {
return AddonUserProfileFieldCheckboxComponent; return AddonUserProfileFieldCheckboxComponent;
} }
}
}

View File

@ -26,23 +26,24 @@ import { CoreUtilsProvider } from '../../../../providers/utils/utils';
}) })
export class AddonUserProfileFieldDatetimeComponent implements OnInit { export class AddonUserProfileFieldDatetimeComponent implements OnInit {
@Input() field: any; // The profile field to be rendered. @Input() field: any; // The profile field to be rendered.
@Input() edit?: boolean = false; // True if editing the field. Defaults to false. @Input() edit? = false; // True if editing the field. Defaults to false.
@Input() disabled?: boolean = false; // True if disabled. Defaults to false. @Input() disabled? = false; // True if disabled. Defaults to false.
@Input() form?: FormGroup; // Form where to add the form control. @Input() form?: FormGroup; // Form where to add the form control.
constructor(private fb: FormBuilder, private timeUtils: CoreTimeUtilsProvider, protected utils: CoreUtilsProvider) {} constructor(private fb: FormBuilder, private timeUtils: CoreTimeUtilsProvider, protected utils: CoreUtilsProvider) { }
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
let field = this.field, const field = this.field;
year; let year;
if (field && this.edit && this.form) { if (field && this.edit && this.form) {
field.modelName = 'profile_field_' + field.shortname; field.modelName = 'profile_field_' + field.shortname;
// Check if it's only date or it has time too. // Check if it's only date or it has time too.
let hasTime = this.utils.isTrueOrOne(field.param3); const hasTime = this.utils.isTrueOrOne(field.param3);
field.format = hasTime ? this.timeUtils.getLocalizedDateFormat('LLL') : this.timeUtils.getLocalizedDateFormat('LL'); field.format = hasTime ? this.timeUtils.getLocalizedDateFormat('LLL') : this.timeUtils.getLocalizedDateFormat('LL');
// Check min value. // Check min value.
@ -61,7 +62,7 @@ export class AddonUserProfileFieldDatetimeComponent implements OnInit {
} }
} }
let formData = { const formData = {
value: field.defaultdata, value: field.defaultdata,
disabled: this.disabled disabled: this.disabled
}; };
@ -69,5 +70,4 @@ export class AddonUserProfileFieldDatetimeComponent implements OnInit {
field.required && !field.locked ? Validators.required : null)); field.required && !field.locked ? Validators.required : null));
} }
} }
}
}

View File

@ -45,4 +45,4 @@ export class AddonUserProfileFieldDatetimeModule {
constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldDatetimeHandler) { constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldDatetimeHandler) {
userProfileFieldDelegate.registerHandler(handler); userProfileFieldDelegate.registerHandler(handler);
} }
} }

View File

@ -24,14 +24,16 @@ import { AddonUserProfileFieldDatetimeComponent } from '../component/datetime';
export class AddonUserProfileFieldDatetimeHandler implements CoreUserProfileFieldHandler { export class AddonUserProfileFieldDatetimeHandler implements CoreUserProfileFieldHandler {
name = 'datetime'; name = 'datetime';
constructor() {} constructor() {
// Nothing to do.
}
/** /**
* Whether or not the handler is enabled on a site level. * Whether or not the handler is enabled on a site level.
* *
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled. * @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/ */
isEnabled() : boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
return true; return true;
} }
@ -45,10 +47,11 @@ export class AddonUserProfileFieldDatetimeHandler implements CoreUserProfileFiel
* @return {CoreUserProfileFieldHandlerData} Data to send for the field. * @return {CoreUserProfileFieldHandlerData} Data to send for the field.
*/ */
getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData { getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData {
let name = 'profile_field_' + field.shortname; const name = 'profile_field_' + field.shortname;
if (formValues[name]) { if (formValues[name]) {
let milliseconds = new Date(formValues[name]).getTime(); const milliseconds = new Date(formValues[name]).getTime();
return { return {
type: 'datetime', type: 'datetime',
name: 'profile_field_' + field.shortname, name: 'profile_field_' + field.shortname,
@ -62,8 +65,7 @@ export class AddonUserProfileFieldDatetimeHandler implements CoreUserProfileFiel
* *
* @return {any} The component to use, undefined if not found. * @return {any} The component to use, undefined if not found.
*/ */
getComponent() { getComponent(): any {
return AddonUserProfileFieldDatetimeComponent; return AddonUserProfileFieldDatetimeComponent;
} }
}
}

View File

@ -24,17 +24,17 @@ import { FormGroup, FormBuilder, Validators } from '@angular/forms';
}) })
export class AddonUserProfileFieldMenuComponent implements OnInit { export class AddonUserProfileFieldMenuComponent implements OnInit {
@Input() field: any; // The profile field to be rendered. @Input() field: any; // The profile field to be rendered.
@Input() edit?: boolean = false; // True if editing the field. Defaults to false. @Input() edit? = false; // True if editing the field. Defaults to false.
@Input() disabled?: boolean = false; // True if disabled. Defaults to false. @Input() disabled? = false; // True if disabled. Defaults to false.
@Input() form?: FormGroup; // Form where to add the form control. @Input() form?: FormGroup; // Form where to add the form control.
constructor(private fb: FormBuilder) {} constructor(private fb: FormBuilder) { }
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
let field = this.field; const field = this.field;
if (field && this.edit && this.form) { if (field && this.edit && this.form) {
field.modelName = 'profile_field_' + field.shortname; field.modelName = 'profile_field_' + field.shortname;
@ -46,7 +46,7 @@ export class AddonUserProfileFieldMenuComponent implements OnInit {
field.options = []; field.options = [];
} }
let formData = { const formData = {
value: field.defaultdata, value: field.defaultdata,
disabled: this.disabled disabled: this.disabled
}; };
@ -56,5 +56,4 @@ export class AddonUserProfileFieldMenuComponent implements OnInit {
} }
} }
}
}

View File

@ -45,4 +45,4 @@ export class AddonUserProfileFieldMenuModule {
constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldMenuHandler) { constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldMenuHandler) {
userProfileFieldDelegate.registerHandler(handler); userProfileFieldDelegate.registerHandler(handler);
} }
} }

View File

@ -24,14 +24,16 @@ import { AddonUserProfileFieldMenuComponent } from '../component/menu';
export class AddonUserProfileFieldMenuHandler implements CoreUserProfileFieldHandler { export class AddonUserProfileFieldMenuHandler implements CoreUserProfileFieldHandler {
name = 'menu'; name = 'menu';
constructor() {} constructor() {
// Nothing to do.
}
/** /**
* Whether or not the handler is enabled on a site level. * Whether or not the handler is enabled on a site level.
* *
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled. * @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/ */
isEnabled() : boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
return true; return true;
} }
@ -45,7 +47,7 @@ export class AddonUserProfileFieldMenuHandler implements CoreUserProfileFieldHan
* @return {CoreUserProfileFieldHandlerData} Data to send for the field. * @return {CoreUserProfileFieldHandlerData} Data to send for the field.
*/ */
getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData { getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData {
let name = 'profile_field_' + field.shortname; const name = 'profile_field_' + field.shortname;
if (formValues[name]) { if (formValues[name]) {
return { return {
@ -61,8 +63,7 @@ export class AddonUserProfileFieldMenuHandler implements CoreUserProfileFieldHan
* *
* @return {any} The component to use, undefined if not found. * @return {any} The component to use, undefined if not found.
*/ */
getComponent() { getComponent(): any {
return AddonUserProfileFieldMenuComponent; return AddonUserProfileFieldMenuComponent;
} }
}
}

View File

@ -25,17 +25,17 @@ import { CoreUtilsProvider } from '../../../../providers/utils/utils';
}) })
export class AddonUserProfileFieldTextComponent implements OnInit { export class AddonUserProfileFieldTextComponent implements OnInit {
@Input() field: any; // The profile field to be rendered. @Input() field: any; // The profile field to be rendered.
@Input() edit?: boolean = false; // True if editing the field. Defaults to false. @Input() edit? = false; // True if editing the field. Defaults to false.
@Input() disabled?: boolean = false; // True if disabled. Defaults to false. @Input() disabled? = false; // True if disabled. Defaults to false.
@Input() form?: FormGroup; // Form where to add the form control. @Input() form?: FormGroup; // Form where to add the form control.
constructor(private fb: FormBuilder, protected utils: CoreUtilsProvider) {} constructor(private fb: FormBuilder, protected utils: CoreUtilsProvider) { }
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
let field = this.field; const field = this.field;
if (field && this.edit && this.form) { if (field && this.edit && this.form) {
field.modelName = 'profile_field_' + field.shortname; field.modelName = 'profile_field_' + field.shortname;
@ -48,7 +48,7 @@ export class AddonUserProfileFieldTextComponent implements OnInit {
// Check if it's a password or text. // Check if it's a password or text.
field.inputType = this.utils.isTrueOrOne(field.param3) ? 'password' : 'text'; field.inputType = this.utils.isTrueOrOne(field.param3) ? 'password' : 'text';
let formData = { const formData = {
value: field.defaultdata, value: field.defaultdata,
disabled: this.disabled disabled: this.disabled
}; };
@ -57,5 +57,4 @@ export class AddonUserProfileFieldTextComponent implements OnInit {
field.required && !field.locked ? Validators.required : null)); field.required && !field.locked ? Validators.required : null));
} }
} }
}
}

View File

@ -25,14 +25,14 @@ import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
export class AddonUserProfileFieldTextHandler implements CoreUserProfileFieldHandler { export class AddonUserProfileFieldTextHandler implements CoreUserProfileFieldHandler {
name = 'text'; name = 'text';
constructor(private textUtils: CoreTextUtilsProvider) {} constructor(private textUtils: CoreTextUtilsProvider) { }
/** /**
* Whether or not the handler is enabled on a site level. * Whether or not the handler is enabled on a site level.
* *
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled. * @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/ */
isEnabled() : boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
return true; return true;
} }
@ -46,7 +46,7 @@ export class AddonUserProfileFieldTextHandler implements CoreUserProfileFieldHan
* @return {CoreUserProfileFieldHandlerData} Data to send for the field. * @return {CoreUserProfileFieldHandlerData} Data to send for the field.
*/ */
getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData { getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData {
let name = 'profile_field_' + field.shortname; const name = 'profile_field_' + field.shortname;
return { return {
type: 'text', type: 'text',
@ -60,8 +60,7 @@ export class AddonUserProfileFieldTextHandler implements CoreUserProfileFieldHan
* *
* @return {any} The component to use, undefined if not found. * @return {any} The component to use, undefined if not found.
*/ */
getComponent() { getComponent(): any {
return AddonUserProfileFieldTextComponent; return AddonUserProfileFieldTextComponent;
} }
}
}

View File

@ -45,4 +45,4 @@ export class AddonUserProfileFieldTextModule {
constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldTextHandler) { constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldTextHandler) {
userProfileFieldDelegate.registerHandler(handler); userProfileFieldDelegate.registerHandler(handler);
} }
} }

View File

@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit } from '@angular/core';
import { FormGroup, Validators, FormControl} from '@angular/forms'; import { FormGroup, Validators, FormControl } from '@angular/forms';
/** /**
* Directive to render a textarea user profile field. * Directive to render a textarea user profile field.
@ -24,24 +24,26 @@ import { FormGroup, Validators, FormControl} from '@angular/forms';
}) })
export class AddonUserProfileFieldTextareaComponent implements OnInit { export class AddonUserProfileFieldTextareaComponent implements OnInit {
@Input() field: any; // The profile field to be rendered. @Input() field: any; // The profile field to be rendered.
@Input() edit?: boolean = false; // True if editing the field. Defaults to false. @Input() edit? = false; // True if editing the field. Defaults to false.
@Input() disabled?: boolean = false; // True if disabled. Defaults to false. @Input() disabled? = false; // True if disabled. Defaults to false.
@Input() form?: FormGroup; // Form where to add the form control. @Input() form?: FormGroup; // Form where to add the form control.
control: FormControl control: FormControl;
constructor() {} constructor() {
// Nothing to do.
}
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
let field = this.field; const field = this.field;
if (field && this.edit && this.form) { if (field && this.edit && this.form) {
field.modelName = 'profile_field_' + field.shortname; field.modelName = 'profile_field_' + field.shortname;
let formData = { const formData = {
value: field.defaultdata, value: field.defaultdata,
disabled: this.disabled disabled: this.disabled
}; };
@ -50,5 +52,4 @@ export class AddonUserProfileFieldTextareaComponent implements OnInit {
this.form.addControl(field.modelName, this.control); this.form.addControl(field.modelName, this.control);
} }
} }
}
}

View File

@ -25,14 +25,14 @@ import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
export class AddonUserProfileFieldTextareaHandler implements CoreUserProfileFieldHandler { export class AddonUserProfileFieldTextareaHandler implements CoreUserProfileFieldHandler {
name = 'textarea'; name = 'textarea';
constructor(private textUtils: CoreTextUtilsProvider) {} constructor(private textUtils: CoreTextUtilsProvider) { }
/** /**
* Whether or not the handler is enabled on a site level. * Whether or not the handler is enabled on a site level.
* *
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled. * @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/ */
isEnabled() : boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
return true; return true;
} }
@ -46,10 +46,10 @@ export class AddonUserProfileFieldTextareaHandler implements CoreUserProfileFiel
* @return {CoreUserProfileFieldHandlerData} Data to send for the field. * @return {CoreUserProfileFieldHandlerData} Data to send for the field.
*/ */
getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData { getData(field: any, signup: boolean, registerAuth: string, formValues: any): CoreUserProfileFieldHandlerData {
let name = 'profile_field_' + field.shortname; const name = 'profile_field_' + field.shortname;
if (formValues[name]) { if (formValues[name]) {
let text = formValues[name] || ''; let text = formValues[name] || '';
// Add some HTML to the message in case the user edited with textarea. // Add some HTML to the message in case the user edited with textarea.
text = this.textUtils.formatHtmlLines(text); text = this.textUtils.formatHtmlLines(text);
@ -69,8 +69,7 @@ export class AddonUserProfileFieldTextareaHandler implements CoreUserProfileFiel
* *
* @return {any} The component to use, undefined if not found. * @return {any} The component to use, undefined if not found.
*/ */
getComponent() { getComponent(): any {
return AddonUserProfileFieldTextareaComponent; return AddonUserProfileFieldTextareaComponent;
} }
}
}

View File

@ -45,4 +45,4 @@ export class AddonUserProfileFieldTextareaModule {
constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldTextareaHandler) { constructor(userProfileFieldDelegate: CoreUserProfileFieldDelegate, handler: AddonUserProfileFieldTextareaHandler) {
userProfileFieldDelegate.registerHandler(handler); userProfileFieldDelegate.registerHandler(handler);
} }
} }

View File

@ -18,7 +18,6 @@ import { AddonUserProfileFieldMenuModule } from './menu/menu.module';
import { AddonUserProfileFieldTextModule } from './text/text.module'; import { AddonUserProfileFieldTextModule } from './text/text.module';
import { AddonUserProfileFieldTextareaModule } from './textarea/textarea.module'; import { AddonUserProfileFieldTextareaModule } from './textarea/textarea.module';
@NgModule({ @NgModule({
declarations: [], declarations: [],
imports: [ imports: [
@ -32,4 +31,4 @@ import { AddonUserProfileFieldTextareaModule } from './textarea/textarea.module'
], ],
exports: [] exports: []
}) })
export class AddonUserProfileFieldModule {} export class AddonUserProfileFieldModule { }

View File

@ -25,15 +25,15 @@ import { CoreLoginHelperProvider } from '../core/login/providers/helper';
templateUrl: 'app.html' templateUrl: 'app.html'
}) })
export class MoodleMobileApp implements OnInit { export class MoodleMobileApp implements OnInit {
// Use the page name (string) because the page is lazy loaded (Ionic feature). That way we can load pages without // Use page name (string) because the page is lazy loaded (Ionic feature). That way we can load pages without importing them.
// having to import them. The downside is that each page needs to implement a ngModule. // The downside is that each page needs to implement a ngModule.
rootPage:any = 'CoreLoginInitPage'; rootPage: any = 'CoreLoginInitPage';
protected logger; protected logger;
protected lastUrls = {}; protected lastUrls = {};
constructor(private platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, logger: CoreLoggerProvider, constructor(private platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen, logger: CoreLoggerProvider,
private eventsProvider: CoreEventsProvider, private loginHelper: CoreLoginHelperProvider, private eventsProvider: CoreEventsProvider, private loginHelper: CoreLoginHelperProvider,
private appProvider: CoreAppProvider) { private appProvider: CoreAppProvider) {
this.logger = logger.getInstance('AppComponent'); this.logger = logger.getInstance('AppComponent');
platform.ready().then(() => { platform.ready().then(() => {
@ -48,7 +48,7 @@ export class MoodleMobileApp implements OnInit {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
// Go to sites page when user is logged out. // Go to sites page when user is logged out.
this.eventsProvider.on(CoreEventsProvider.LOGOUT, () => { this.eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
this.appProvider.getRootNavController().setRoot('CoreLoginSitesPage'); this.appProvider.getRootNavController().setRoot('CoreLoginSitesPage');
@ -93,7 +93,7 @@ export class MoodleMobileApp implements OnInit {
}); });
// Handle app launched with a certain URL (custom URL scheme). // Handle app launched with a certain URL (custom URL scheme).
(<any>window).handleOpenURL = (url: string) => { (<any> window).handleOpenURL = (url: string): void => {
// First check that the URL hasn't been treated a few seconds ago. Sometimes this function is called more than once. // First check that the URL hasn't been treated a few seconds ago. Sometimes this function is called more than once.
if (this.lastUrls[url] && Date.now() - this.lastUrls[url] < 3000) { if (this.lastUrls[url] && Date.now() - this.lastUrls[url] < 3000) {
// Function called more than once, stop. // Function called more than once, stop.
@ -113,4 +113,3 @@ export class MoodleMobileApp implements OnInit {
}); });
} }
} }

View File

@ -66,9 +66,8 @@ import { CoreUserModule } from '../core/user/user.module';
import { AddonCalendarModule } from '../addon/calendar/calendar.module'; import { AddonCalendarModule } from '../addon/calendar/calendar.module';
import { AddonUserProfileFieldModule } from '../addon/userprofilefield/userprofilefield.module'; import { AddonUserProfileFieldModule } from '../addon/userprofilefield/userprofilefield.module';
// For translate loader. AoT requires an exported function for factories. // For translate loader. AoT requires an exported function for factories.
export function createTranslateLoader(http: HttpClient) { export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http, './assets/lang/', '.json'); return new TranslateHttpLoader(http, './assets/lang/', '.json');
} }

View File

@ -1,3 +1,17 @@
// (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 { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module'; import { AppModule } from './app.module';

View File

@ -32,9 +32,9 @@ export class CoreSyncBaseProvider {
syncInterval = 300000; syncInterval = 300000;
// Store sync promises. // Store sync promises.
protected syncPromises: {[siteId: string]: {[uniqueId: string]: Promise<any>}} = {}; protected syncPromises: { [siteId: string]: { [uniqueId: string]: Promise<any> } } = {};
constructor(private sitesProvider: CoreSitesProvider) {} constructor(private sitesProvider: CoreSitesProvider) { }
/** /**
* Add an ongoing sync to the syncPromises list. On finish the promise will be removed. * Add an ongoing sync to the syncPromises list. On finish the promise will be removed.
@ -44,7 +44,7 @@ export class CoreSyncBaseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} The sync promise. * @return {Promise<any>} The sync promise.
*/ */
addOngoingSync(id: number, promise: Promise<any>, siteId?: string) : Promise<any> { addOngoingSync(id: number, promise: Promise<any>, siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
const uniqueId = this.getUniqueSyncId(id); const uniqueId = this.getUniqueSyncId(id);
@ -67,12 +67,13 @@ export class CoreSyncBaseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise of the current sync or undefined if there isn't any. * @return {Promise<any>} Promise of the current sync or undefined if there isn't any.
*/ */
getOngoingSync(id: number, siteId?: string) : Promise<any> { getOngoingSync(id: number, siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
if (this.isSyncing(id, siteId)) { if (this.isSyncing(id, siteId)) {
// There's already a sync ongoing for this discussion, return the promise. // There's already a sync ongoing for this discussion, return the promise.
const uniqueId = this.getUniqueSyncId(id); const uniqueId = this.getUniqueSyncId(id);
return this.syncPromises[siteId][uniqueId]; return this.syncPromises[siteId][uniqueId];
} }
} }
@ -84,9 +85,9 @@ export class CoreSyncBaseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<number>} Promise resolved with the time. * @return {Promise<number>} Promise resolved with the time.
*/ */
getSyncTime(id: number, siteId?: string) : Promise<number> { getSyncTime(id: number, siteId?: string): Promise<number> {
return this.sitesProvider.getSiteDb(siteId).then((db) => { return this.sitesProvider.getSiteDb(siteId).then((db) => {
return db.getRecord(CoreSyncProvider.SYNC_TABLE, {component: this.component, id: id}).then((entry) => { return db.getRecord(CoreSyncProvider.SYNC_TABLE, { component: this.component, id: id }).then((entry) => {
return entry.time; return entry.time;
}).catch(() => { }).catch(() => {
return 0; return 0;
@ -101,12 +102,12 @@ export class CoreSyncBaseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<string[]>} Promise resolved with the warnings. * @return {Promise<string[]>} Promise resolved with the warnings.
*/ */
getSyncWarnings(id: number, siteId?: string) : Promise<string[]> { getSyncWarnings(id: number, siteId?: string): Promise<string[]> {
return this.sitesProvider.getSiteDb(siteId).then((db) => { return this.sitesProvider.getSiteDb(siteId).then((db) => {
return db.getRecord(CoreSyncProvider.SYNC_TABLE, {component: this.component, id: id}).then((entry) => { return db.getRecord(CoreSyncProvider.SYNC_TABLE, { component: this.component, id: id }).then((entry) => {
try { try {
return JSON.parse(entry.warnings); return JSON.parse(entry.warnings);
} catch(ex) { } catch (ex) {
return []; return [];
} }
}).catch(() => { }).catch(() => {
@ -121,7 +122,7 @@ export class CoreSyncBaseProvider {
* @param {number} id Unique sync identifier per component. * @param {number} id Unique sync identifier per component.
* @return {string} Unique identifier from component and id. * @return {string} Unique identifier from component and id.
*/ */
protected getUniqueSyncId(id: number) : string { protected getUniqueSyncId(id: number): string {
return this.component + '#' + id; return this.component + '#' + id;
} }
@ -132,10 +133,11 @@ export class CoreSyncBaseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {boolean} Whether it's synchronizing. * @return {boolean} Whether it's synchronizing.
*/ */
isSyncing(id: number, siteId?: string) : boolean { isSyncing(id: number, siteId?: string): boolean {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
const uniqueId = this.getUniqueSyncId(id); const uniqueId = this.getUniqueSyncId(id);
return !!(this.syncPromises[siteId] && this.syncPromises[siteId][uniqueId]); return !!(this.syncPromises[siteId] && this.syncPromises[siteId][uniqueId]);
} }
@ -146,7 +148,7 @@ export class CoreSyncBaseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<boolean>} Promise resolved with boolean: whether sync is needed. * @return {Promise<boolean>} Promise resolved with boolean: whether sync is needed.
*/ */
isSyncNeeded(id: number, siteId?: string) : Promise<boolean> { isSyncNeeded(id: number, siteId?: string): Promise<boolean> {
return this.getSyncTime(id, siteId).then((time) => { return this.getSyncTime(id, siteId).then((time) => {
return Date.now() - this.syncInterval >= time; return Date.now() - this.syncInterval >= time;
}); });
@ -160,10 +162,11 @@ export class CoreSyncBaseProvider {
* @param {number} [time] Time to set. If not defined, current time. * @param {number} [time] Time to set. If not defined, current time.
* @return {Promise<any>} Promise resolved when the time is set. * @return {Promise<any>} Promise resolved when the time is set.
*/ */
setSyncTime(id: number, siteId?: string, time?: number) : Promise<any> { setSyncTime(id: number, siteId?: string, time?: number): Promise<any> {
return this.sitesProvider.getSiteDb(siteId).then((db) => { return this.sitesProvider.getSiteDb(siteId).then((db) => {
time = typeof time != 'undefined' ? time : Date.now(); time = typeof time != 'undefined' ? time : Date.now();
return db.insertOrUpdateRecord(CoreSyncProvider.SYNC_TABLE, {time: time}, {component: this.component, id: id});
return db.insertOrUpdateRecord(CoreSyncProvider.SYNC_TABLE, { time: time }, { component: this.component, id: id });
}); });
} }
@ -175,11 +178,12 @@ export class CoreSyncBaseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
setSyncWarnings(id: number, warnings: string[], siteId?: string) : Promise<any> { setSyncWarnings(id: number, warnings: string[], siteId?: string): Promise<any> {
return this.sitesProvider.getSiteDb(siteId).then((db) => { return this.sitesProvider.getSiteDb(siteId).then((db) => {
warnings = warnings || []; warnings = warnings || [];
return db.insertOrUpdateRecord(CoreSyncProvider.SYNC_TABLE, {warnings: JSON.stringify(warnings)},
{component: this.component, id: id}); return db.insertOrUpdateRecord(CoreSyncProvider.SYNC_TABLE, { warnings: JSON.stringify(warnings) },
{ component: this.component, id: id });
}); });
} }
@ -191,11 +195,14 @@ export class CoreSyncBaseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when there's no sync going on for the identifier. * @return {Promise<any>} Promise resolved when there's no sync going on for the identifier.
*/ */
waitForSync(id: number, siteId?: string) : Promise<any> { waitForSync(id: number, siteId?: string): Promise<any> {
const promise = this.getOngoingSync(id, siteId); const promise = this.getOngoingSync(id, siteId);
if (promise) { if (promise) {
return promise.catch(() => {}); return promise.catch(() => {
// Ignore errors.
});
} }
return Promise.resolve(); return Promise.resolve();
} }
} }

View File

@ -23,12 +23,14 @@
export class CoreCache { export class CoreCache {
protected cacheStore = {}; protected cacheStore = {};
constructor() {} constructor() {
// Nothing to do.
}
/** /**
* Clear the cache. * Clear the cache.
*/ */
clear() { clear(): void {
this.cacheStore = {}; this.cacheStore = {};
} }
@ -38,7 +40,7 @@ export class CoreCache {
* @param {any} id The ID to identify the entry. * @param {any} id The ID to identify the entry.
* @return {any} The data from the cache. Undefined if not found. * @return {any} The data from the cache. Undefined if not found.
*/ */
getEntry(id: any) : any { getEntry(id: any): any {
if (!this.cacheStore[id]) { if (!this.cacheStore[id]) {
this.cacheStore[id] = {}; this.cacheStore[id] = {};
} }
@ -54,7 +56,7 @@ export class CoreCache {
* @param {boolean} [ignoreInvalidate] Whether it should always return the cached data, even if it's expired. * @param {boolean} [ignoreInvalidate] Whether it should always return the cached data, even if it's expired.
* @return {any} Cached value. Undefined if not cached or expired. * @return {any} Cached value. Undefined if not cached or expired.
*/ */
getValue(id: any, name: string, ignoreInvalidate?: boolean) : any { getValue(id: any, name: string, ignoreInvalidate?: boolean): any {
const entry = this.getEntry(id); const entry = this.getEntry(id);
if (entry[name] && typeof entry[name].value != 'undefined') { if (entry[name] && typeof entry[name].value != 'undefined') {
@ -73,9 +75,9 @@ export class CoreCache {
* *
* @param {any} id The ID to identify the entry. * @param {any} id The ID to identify the entry.
*/ */
invalidate(id: any) : void { invalidate(id: any): void {
const entry = this.getEntry(id); const entry = this.getEntry(id);
for (let name in entry) { for (const name in entry) {
entry[name].timemodified = 0; entry[name].timemodified = 0;
} }
} }
@ -88,12 +90,13 @@ export class CoreCache {
* @param {any} value Value to set. * @param {any} value Value to set.
* @return {any} The set value. * @return {any} The set value.
*/ */
setValue(id: any, name: string, value: any) : any { setValue(id: any, name: string, value: any): any {
const entry = this.getEntry(id); const entry = this.getEntry(id);
entry[name] = { entry[name] = {
value: value, value: value,
timemodified: Date.now() timemodified: Date.now()
}; };
return value; return value;
} }
} }

View File

@ -84,7 +84,7 @@ export class CoreDelegate {
* If not set, no events will be fired. * If not set, no events will be fired.
*/ */
constructor(delegateName: string, protected loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, constructor(delegateName: string, protected loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider,
protected eventsProvider?: CoreEventsProvider) { protected eventsProvider?: CoreEventsProvider) {
this.logger = this.loggerProvider.getInstance(delegateName); this.logger = this.loggerProvider.getInstance(delegateName);
if (eventsProvider) { if (eventsProvider) {
@ -145,7 +145,7 @@ export class CoreDelegate {
* @param {boolean} [enabled] Only enabled, or any. * @param {boolean} [enabled] Only enabled, or any.
* @return {any} Handler. * @return {any} Handler.
*/ */
protected getHandler(handlerName: string, enabled = false): any { protected getHandler(handlerName: string, enabled: boolean = false): any {
return enabled ? this.enabledHandlers[handlerName] : this.handlers[handlerName]; return enabled ? this.enabledHandlers[handlerName] : this.handlers[handlerName];
} }
@ -156,7 +156,7 @@ export class CoreDelegate {
* @param {boolean} [enabled] Only enabled, or any. * @param {boolean} [enabled] Only enabled, or any.
* @return {boolean} If the handler is registered or not. * @return {boolean} If the handler is registered or not.
*/ */
hasHandler(name: string, enabled = false): boolean { hasHandler(name: string, enabled: boolean = false): boolean {
return enabled ? typeof this.enabledHandlers[name] !== 'undefined' : typeof this.handlers[name] !== 'undefined'; return enabled ? typeof this.enabledHandlers[name] !== 'undefined' : typeof this.handlers[name] !== 'undefined';
} }
@ -171,6 +171,7 @@ export class CoreDelegate {
if (!this.lastUpdateHandlersStart) { if (!this.lastUpdateHandlersStart) {
return true; return true;
} }
return time == this.lastUpdateHandlersStart; return time == this.lastUpdateHandlersStart;
} }
@ -183,11 +184,13 @@ export class CoreDelegate {
registerHandler(handler: CoreDelegateHandler): boolean { registerHandler(handler: CoreDelegateHandler): boolean {
if (typeof this.handlers[handler.name] !== 'undefined') { if (typeof this.handlers[handler.name] !== 'undefined') {
this.logger.log(`Addon '${handler.name}' already registered`); this.logger.log(`Addon '${handler.name}' already registered`);
return false; return false;
} }
this.logger.log(`Registered addon '${handler.name}'`); this.logger.log(`Registered addon '${handler.name}'`);
this.handlers[handler.name] = handler; this.handlers[handler.name] = handler;
return true; return true;
} }
@ -199,9 +202,9 @@ export class CoreDelegate {
* @return {Promise<void>} Resolved when done. * @return {Promise<void>} Resolved when done.
*/ */
protected updateHandler(handler: CoreDelegateHandler, time: number): Promise<void> { protected updateHandler(handler: CoreDelegateHandler, time: number): Promise<void> {
let promise, const siteId = this.sitesProvider.getCurrentSiteId(),
siteId = this.sitesProvider.getCurrentSiteId(),
currentSite = this.sitesProvider.getCurrentSite(); currentSite = this.sitesProvider.getCurrentSite();
let promise;
if (!this.sitesProvider.isLoggedIn()) { if (!this.sitesProvider.isLoggedIn()) {
promise = Promise.reject(null); promise = Promise.reject(null);
@ -235,7 +238,7 @@ export class CoreDelegate {
* @return {boolean} Whether is enabled or disabled in site. * @return {boolean} Whether is enabled or disabled in site.
*/ */
protected isFeatureDisabled(handler: CoreDelegateHandler, site: any): boolean { protected isFeatureDisabled(handler: CoreDelegateHandler, site: any): boolean {
return typeof this.featurePrefix != "undefined" && site.isFeatureDisabled(this.featurePrefix + handler.name); return typeof this.featurePrefix != 'undefined' && site.isFeatureDisabled(this.featurePrefix + handler.name);
} }
/** /**
@ -244,7 +247,7 @@ export class CoreDelegate {
* @return {Promise<void>} Resolved when done. * @return {Promise<void>} Resolved when done.
*/ */
protected updateHandlers(): Promise<void> { protected updateHandlers(): Promise<void> {
let promises = [], const promises = [],
now = Date.now(); now = Date.now();
this.logger.debug('Updating handlers for current site.'); this.logger.debug('Updating handlers for current site.');
@ -252,7 +255,7 @@ export class CoreDelegate {
this.lastUpdateHandlersStart = now; this.lastUpdateHandlersStart = now;
// Loop over all the handlers. // Loop over all the handlers.
for (let name in this.handlers) { for (const name in this.handlers) {
promises.push(this.updateHandler(this.handlers[name], now)); promises.push(this.updateHandler(this.handlers[name], now));
} }
@ -274,7 +277,7 @@ export class CoreDelegate {
* Update handlers Data. * Update handlers Data.
* Override this function to update handlers data. * Override this function to update handlers data.
*/ */
updateData() { updateData(): any {
// To be overridden.
} }
} }

View File

@ -23,14 +23,16 @@ import { Observable } from 'rxjs';
@Injectable() @Injectable()
export class CoreInterceptor implements HttpInterceptor { export class CoreInterceptor implements HttpInterceptor {
constructor() {} constructor() {
// Nothing to do.
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
// Add the header and serialize the body if needed. // Add the header and serialize the body if needed.
const newReq = req.clone({ const newReq = req.clone({
headers: req.headers.set('Content-Type', 'application/x-www-form-urlencoded'), headers: req.headers.set('Content-Type', 'application/x-www-form-urlencoded'),
body: typeof req.body == 'object' && String(req.body) != '[object File]' ? body: typeof req.body == 'object' && String(req.body) != '[object File]' ?
CoreInterceptor.serialize(req.body) : req.body CoreInterceptor.serialize(req.body) : req.body
}); });
// Pass on the cloned request instead of the original request. // Pass on the cloned request instead of the original request.
@ -44,14 +46,14 @@ export class CoreInterceptor implements HttpInterceptor {
* @param {boolean} [addNull] Add null values to the serialized as empty parameters. * @param {boolean} [addNull] Add null values to the serialized as empty parameters.
* @return {string} Serialization of the object. * @return {string} Serialization of the object.
*/ */
public static serialize(obj: any, addNull?: boolean) : string { static serialize(obj: any, addNull?: boolean): string {
let query = '', let query = '',
fullSubName, fullSubName,
subValue, subValue,
innerObj; innerObj;
for (let name in obj) { for (const name in obj) {
let value = obj[name]; const value = obj[name];
if (value instanceof Array) { if (value instanceof Array) {
for (let i = 0; i < value.length; ++i) { for (let i = 0; i < value.length; ++i) {
@ -62,7 +64,7 @@ export class CoreInterceptor implements HttpInterceptor {
query += this.serialize(innerObj) + '&'; query += this.serialize(innerObj) + '&';
} }
} else if (value instanceof Object) { } else if (value instanceof Object) {
for (let subName in value) { for (const subName in value) {
subValue = value[subName]; subValue = value[subName];
fullSubName = name + '[' + subName + ']'; fullSubName = name + '[' + subName + ']';
innerObj = {}; innerObj = {};

View File

@ -107,7 +107,7 @@ export interface CoreSiteWSPreSets {
* @type {string} * @type {string}
*/ */
typeExpected?: string; typeExpected?: string;
}; }
/** /**
* Response of checking local_mobile status. * Response of checking local_mobile status.
@ -190,10 +190,10 @@ export class CoreSite {
protected cleanUnicode = false; protected cleanUnicode = false;
protected lastAutoLogin = 0; protected lastAutoLogin = 0;
protected moodleReleases = { protected moodleReleases = {
'3.1': 2016052300, 3.1: 2016052300,
'3.2': 2016120500, 3.2: 2016120500,
'3.3': 2017051503, 3.3: 2017051503,
'3.4': 2017111300 3.4: 2017111300
}; };
/** /**
@ -211,7 +211,7 @@ export class CoreSite {
constructor(injector: Injector, public id: string, public siteUrl: string, public token?: string, public infos?: any, constructor(injector: Injector, public id: string, public siteUrl: string, public token?: string, public infos?: any,
public privateToken?: string, public config?: any, public loggedOut?: boolean) { public privateToken?: string, public config?: any, public loggedOut?: boolean) {
// Inject the required services. // Inject the required services.
let logger = injector.get(CoreLoggerProvider); const logger = injector.get(CoreLoggerProvider);
this.appProvider = injector.get(CoreAppProvider); this.appProvider = injector.get(CoreAppProvider);
this.dbProvider = injector.get(CoreDbProvider); this.dbProvider = injector.get(CoreDbProvider);
this.domUtils = injector.get(CoreDomUtilsProvider); this.domUtils = injector.get(CoreDomUtilsProvider);
@ -235,7 +235,7 @@ export class CoreSite {
/** /**
* Initialize the database. * Initialize the database.
*/ */
initDB() : void { initDB(): void {
this.db = this.dbProvider.getDB('Site-' + this.id); this.db = this.dbProvider.getDB('Site-' + this.id);
this.db.createTableFromSchema(this.tableSchema); this.db.createTableFromSchema(this.tableSchema);
} }
@ -245,7 +245,7 @@ export class CoreSite {
* *
* @return {string} Site ID. * @return {string} Site ID.
*/ */
getId() : string { getId(): string {
return this.id; return this.id;
} }
@ -254,7 +254,7 @@ export class CoreSite {
* *
* @return {string} Site URL. * @return {string} Site URL.
*/ */
getURL() : string { getURL(): string {
return this.siteUrl; return this.siteUrl;
} }
@ -263,7 +263,7 @@ export class CoreSite {
* *
* @return {string} Site token. * @return {string} Site token.
*/ */
getToken() : string { getToken(): string {
return this.token; return this.token;
} }
@ -272,7 +272,7 @@ export class CoreSite {
* *
* @return {any} Site info. * @return {any} Site info.
*/ */
getInfo() : any { getInfo(): any {
return this.infos; return this.infos;
} }
@ -281,7 +281,7 @@ export class CoreSite {
* *
* @return {string} Site private token. * @return {string} Site private token.
*/ */
getPrivateToken() : string { getPrivateToken(): string {
return this.privateToken; return this.privateToken;
} }
@ -290,7 +290,7 @@ export class CoreSite {
* *
* @return {SQLiteDB} Site DB. * @return {SQLiteDB} Site DB.
*/ */
getDb() : SQLiteDB { getDb(): SQLiteDB {
return this.db; return this.db;
} }
@ -299,7 +299,7 @@ export class CoreSite {
* *
* @return {number} User's ID. * @return {number} User's ID.
*/ */
getUserId() : number { getUserId(): number {
if (typeof this.infos != 'undefined' && typeof this.infos.userid != 'undefined') { if (typeof this.infos != 'undefined' && typeof this.infos.userid != 'undefined') {
return this.infos.userid; return this.infos.userid;
} }
@ -310,7 +310,7 @@ export class CoreSite {
* *
* @return {number} Site Home ID. * @return {number} Site Home ID.
*/ */
getSiteHomeId() : number { getSiteHomeId(): number {
return this.infos && this.infos.siteid || 1; return this.infos && this.infos.siteid || 1;
} }
@ -319,7 +319,7 @@ export class CoreSite {
* *
* @param {string} New ID. * @param {string} New ID.
*/ */
setId(id: string) : void { setId(id: string): void {
this.id = id; this.id = id;
this.initDB(); this.initDB();
} }
@ -329,7 +329,7 @@ export class CoreSite {
* *
* @param {string} New token. * @param {string} New token.
*/ */
setToken(token: string) : void { setToken(token: string): void {
this.token = token; this.token = token;
} }
@ -338,7 +338,7 @@ export class CoreSite {
* *
* @param {string} privateToken New private token. * @param {string} privateToken New private token.
*/ */
setPrivateToken(privateToken: string) : void { setPrivateToken(privateToken: string): void {
this.privateToken = privateToken; this.privateToken = privateToken;
} }
@ -347,7 +347,7 @@ export class CoreSite {
* *
* @return {boolean} Whether is logged out. * @return {boolean} Whether is logged out.
*/ */
isLoggedOut() : boolean { isLoggedOut(): boolean {
return !!this.loggedOut; return !!this.loggedOut;
} }
@ -356,7 +356,7 @@ export class CoreSite {
* *
* @param {any} New info. * @param {any} New info.
*/ */
setInfo(infos: any) : void { setInfo(infos: any): void {
this.infos = infos; this.infos = infos;
} }
@ -365,7 +365,7 @@ export class CoreSite {
* *
* @param {any} Config. * @param {any} Config.
*/ */
setConfig(config: any) : void { setConfig(config: any): void {
this.config = config; this.config = config;
} }
@ -374,7 +374,7 @@ export class CoreSite {
* *
* @param {boolean} loggedOut True if logged out and needs to authenticate again, false otherwise. * @param {boolean} loggedOut True if logged out and needs to authenticate again, false otherwise.
*/ */
setLoggedOut(loggedOut: boolean) : void { setLoggedOut(loggedOut: boolean): void {
this.loggedOut = !!loggedOut; this.loggedOut = !!loggedOut;
} }
@ -383,8 +383,9 @@ export class CoreSite {
* *
* @return {boolean} Whether can access my files. * @return {boolean} Whether can access my files.
*/ */
canAccessMyFiles() : boolean { canAccessMyFiles(): boolean {
const infos = this.getInfo(); const infos = this.getInfo();
return infos && (typeof infos.usercanmanageownfiles === 'undefined' || infos.usercanmanageownfiles); return infos && (typeof infos.usercanmanageownfiles === 'undefined' || infos.usercanmanageownfiles);
} }
@ -393,8 +394,9 @@ export class CoreSite {
* *
* @return {boolean} Whether can download files. * @return {boolean} Whether can download files.
*/ */
canDownloadFiles() : boolean { canDownloadFiles(): boolean {
const infos = this.getInfo(); const infos = this.getInfo();
return infos && infos.downloadfiles; return infos && infos.downloadfiles;
} }
@ -405,15 +407,15 @@ export class CoreSite {
* @param {boolean} [whenUndefined=true] The value to return when the parameter is undefined. * @param {boolean} [whenUndefined=true] The value to return when the parameter is undefined.
* @return {boolean} Whether can use advanced feature. * @return {boolean} Whether can use advanced feature.
*/ */
canUseAdvancedFeature(feature: string, whenUndefined = true) : boolean { canUseAdvancedFeature(feature: string, whenUndefined: boolean = true): boolean {
let infos = this.getInfo(), const infos = this.getInfo();
canUse = true; let canUse = true;
if (typeof infos.advancedfeatures === 'undefined') { if (typeof infos.advancedfeatures === 'undefined') {
canUse = whenUndefined; canUse = whenUndefined;
} else { } else {
for (let i in infos.advancedfeatures) { for (const i in infos.advancedfeatures) {
let item = infos.advancedfeatures[i]; const item = infos.advancedfeatures[i];
if (item.name === feature && parseInt(item.value, 10) === 0) { if (item.name === feature && parseInt(item.value, 10) === 0) {
canUse = false; canUse = false;
} }
@ -429,8 +431,9 @@ export class CoreSite {
* *
* @return {boolean} Whether can upload files. * @return {boolean} Whether can upload files.
*/ */
canUploadFiles() : boolean { canUploadFiles(): boolean {
const infos = this.getInfo(); const infos = this.getInfo();
return infos && infos.uploadfiles; return infos && infos.uploadfiles;
} }
@ -439,12 +442,12 @@ export class CoreSite {
* *
* @return {Promise<any>} A promise to be resolved when the site info is retrieved. * @return {Promise<any>} A promise to be resolved when the site info is retrieved.
*/ */
fetchSiteInfo() : Promise<any> { fetchSiteInfo(): Promise<any> {
// get_site_info won't be cached. // The get_site_info WS call won't be cached.
let preSets = { const preSets = {
getFromCache: false, getFromCache: false,
saveToCache: false saveToCache: false
} };
// Reset clean Unicode to check if it's supported again. // Reset clean Unicode to check if it's supported again.
this.cleanUnicode = false; this.cleanUnicode = false;
@ -460,7 +463,7 @@ export class CoreSite {
* @param {CoreSiteWSPreSets} [preSets] Extra options. * @param {CoreSiteWSPreSets} [preSets] Extra options.
* @return {Promise<any>} Promise resolved with the response, rejected with CoreWSError if it fails. * @return {Promise<any>} Promise resolved with the response, rejected with CoreWSError if it fails.
*/ */
read(method: string, data: any, preSets?: CoreSiteWSPreSets) : Promise<any> { read(method: string, data: any, preSets?: CoreSiteWSPreSets): Promise<any> {
preSets = preSets || {}; preSets = preSets || {};
if (typeof preSets.getFromCache == 'undefined') { if (typeof preSets.getFromCache == 'undefined') {
preSets.getFromCache = true; preSets.getFromCache = true;
@ -468,6 +471,7 @@ export class CoreSite {
if (typeof preSets.saveToCache == 'undefined') { if (typeof preSets.saveToCache == 'undefined') {
preSets.saveToCache = true; preSets.saveToCache = true;
} }
return this.request(method, data, preSets); return this.request(method, data, preSets);
} }
@ -479,7 +483,7 @@ export class CoreSite {
* @param {CoreSiteWSPreSets} [preSets] Extra options. * @param {CoreSiteWSPreSets} [preSets] Extra options.
* @return {Promise<any>} Promise resolved with the response, rejected with CoreWSError if it fails. * @return {Promise<any>} Promise resolved with the response, rejected with CoreWSError if it fails.
*/ */
write(method: string, data: any, preSets?: CoreSiteWSPreSets) : Promise<any> { write(method: string, data: any, preSets?: CoreSiteWSPreSets): Promise<any> {
preSets = preSets || {}; preSets = preSets || {};
if (typeof preSets.getFromCache == 'undefined') { if (typeof preSets.getFromCache == 'undefined') {
preSets.getFromCache = false; preSets.getFromCache = false;
@ -490,6 +494,7 @@ export class CoreSite {
if (typeof preSets.emergencyCache == 'undefined') { if (typeof preSets.emergencyCache == 'undefined') {
preSets.emergencyCache = false; preSets.emergencyCache = false;
} }
return this.request(method, data, preSets); return this.request(method, data, preSets);
} }
@ -512,8 +517,8 @@ export class CoreSite {
* This method is smart which means that it will try to map the method to a compatibility one if need be, usually this * This method is smart which means that it will try to map the method to a compatibility one if need be, usually this
* means that it will fallback on the 'local_mobile_' prefixed function if it is available and the non-prefixed is not. * means that it will fallback on the 'local_mobile_' prefixed function if it is available and the non-prefixed is not.
*/ */
request(method: string, data: any, preSets: CoreSiteWSPreSets, retrying?: boolean) : Promise<any> { request(method: string, data: any, preSets: CoreSiteWSPreSets, retrying?: boolean): Promise<any> {
let initialToken = this.token; const initialToken = this.token;
data = data || {}; data = data || {};
// Check if the method is available, use a prefixed version if possible. // Check if the method is available, use a prefixed version if possible.
@ -525,11 +530,12 @@ export class CoreSite {
method = compatibilityMethod; method = compatibilityMethod;
} else { } else {
this.logger.error(`WS function '${method}' is not available, even in compatibility mode.`); this.logger.error(`WS function '${method}' is not available, even in compatibility mode.`);
return Promise.reject(this.wsProvider.createFakeWSError('core.wsfunctionnotavailable', true)); return Promise.reject(this.wsProvider.createFakeWSError('core.wsfunctionnotavailable', true));
} }
} }
let wsPreSets: CoreWSPreSets = { const wsPreSets: CoreWSPreSets = {
wsToken: this.token, wsToken: this.token,
siteUrl: this.siteUrl, siteUrl: this.siteUrl,
cleanUnicode: this.cleanUnicode, cleanUnicode: this.cleanUnicode,
@ -564,12 +570,11 @@ export class CoreSite {
this.saveToCache(method, data, response, preSets); this.saveToCache(method, data, response, preSets);
} }
// We pass back a clone of the original object, this may // We pass back a clone of the original object, this may prevent errors if in the callback the object is modified.
// prevent errors if in the callback the object is modified.
return this.utils.clone(response); return this.utils.clone(response);
}).catch((error) => { }).catch((error) => {
if (error.errorcode == 'invalidtoken' || if (error.errorcode == 'invalidtoken' ||
(error.errorcode == 'accessexception' && error.message.indexOf('Invalid token - token expired') > -1)) { (error.errorcode == 'accessexception' && error.message.indexOf('Invalid token - token expired') > -1)) {
if (initialToken !== this.token && !retrying) { if (initialToken !== this.token && !retrying) {
// Token has changed, retry with the new token. // Token has changed, retry with the new token.
return this.request(method, data, preSets, true); return this.request(method, data, preSets, true);
@ -586,41 +591,49 @@ export class CoreSite {
error.message = this.translate.instant('core.lostconnection'); error.message = this.translate.instant('core.lostconnection');
} else if (error.errorcode === 'userdeleted') { } else if (error.errorcode === 'userdeleted') {
// User deleted, trigger event. // User deleted, trigger event.
this.eventsProvider.trigger(CoreEventsProvider.USER_DELETED, {params: data}, this.id); this.eventsProvider.trigger(CoreEventsProvider.USER_DELETED, { params: data }, this.id);
error.message = this.translate.instant('core.userdeleted'); error.message = this.translate.instant('core.userdeleted');
return Promise.reject(error); return Promise.reject(error);
} else if (error.errorcode === 'forcepasswordchangenotice') { } else if (error.errorcode === 'forcepasswordchangenotice') {
// Password Change Forced, trigger event. // Password Change Forced, trigger event.
this.eventsProvider.trigger(CoreEventsProvider.PASSWORD_CHANGE_FORCED, {}, this.id); this.eventsProvider.trigger(CoreEventsProvider.PASSWORD_CHANGE_FORCED, {}, this.id);
error.message = this.translate.instant('core.forcepasswordchangenotice'); error.message = this.translate.instant('core.forcepasswordchangenotice');
return Promise.reject(error); return Promise.reject(error);
} else if (error.errorcode === 'usernotfullysetup') { } else if (error.errorcode === 'usernotfullysetup') {
// User not fully setup, trigger event. // User not fully setup, trigger event.
this.eventsProvider.trigger(CoreEventsProvider.USER_NOT_FULLY_SETUP, {}, this.id); this.eventsProvider.trigger(CoreEventsProvider.USER_NOT_FULLY_SETUP, {}, this.id);
error.message = this.translate.instant('core.usernotfullysetup'); error.message = this.translate.instant('core.usernotfullysetup');
return Promise.reject(error); return Promise.reject(error);
} else if (error.errorcode === 'sitepolicynotagreed') { } else if (error.errorcode === 'sitepolicynotagreed') {
// Site policy not agreed, trigger event. // Site policy not agreed, trigger event.
this.eventsProvider.trigger(CoreEventsProvider.SITE_POLICY_NOT_AGREED, {}, this.id); this.eventsProvider.trigger(CoreEventsProvider.SITE_POLICY_NOT_AGREED, {}, this.id);
error.message = this.translate.instant('core.sitepolicynotagreederror'); error.message = this.translate.instant('core.sitepolicynotagreederror');
return Promise.reject(error); return Promise.reject(error);
} else if (error.errorcode === 'dmlwriteexception' && this.textUtils.hasUnicodeData(data)) { } else if (error.errorcode === 'dmlwriteexception' && this.textUtils.hasUnicodeData(data)) {
if (!this.cleanUnicode) { if (!this.cleanUnicode) {
// Try again cleaning unicode. // Try again cleaning unicode.
this.cleanUnicode = true; this.cleanUnicode = true;
return this.request(method, data, preSets); return this.request(method, data, preSets);
} }
// This should not happen. // This should not happen.
error.message = this.translate.instant('core.unicodenotsupported'); error.message = this.translate.instant('core.unicodenotsupported');
return Promise.reject(error); return Promise.reject(error);
} else if (typeof preSets.emergencyCache !== 'undefined' && !preSets.emergencyCache) { } else if (typeof preSets.emergencyCache !== 'undefined' && !preSets.emergencyCache) {
this.logger.debug(`WS call '${method}' failed. Emergency cache is forbidden, rejecting.`); this.logger.debug(`WS call '${method}' failed. Emergency cache is forbidden, rejecting.`);
return Promise.reject(error); return Promise.reject(error);
} }
this.logger.debug(`WS call '${method}' failed. Trying to use the emergency cache.`); this.logger.debug(`WS call '${method}' failed. Trying to use the emergency cache.`);
preSets.omitExpires = true; preSets.omitExpires = true;
preSets.getFromCache = true; preSets.getFromCache = true;
return this.getFromCache(method, data, preSets, true).catch(() => { return this.getFromCache(method, data, preSets, true).catch(() => {
return Promise.reject(error); return Promise.reject(error);
}); });
@ -635,7 +648,7 @@ export class CoreSite {
* @param {boolean} [checkPrefix=true] When true also checks with the compatibility prefix. * @param {boolean} [checkPrefix=true] When true also checks with the compatibility prefix.
* @return {boolean} Whether the WS is available. * @return {boolean} Whether the WS is available.
*/ */
wsAvailable(method: string, checkPrefix = true) : boolean { wsAvailable(method: string, checkPrefix: boolean = true): boolean {
if (typeof this.infos == 'undefined') { if (typeof this.infos == 'undefined') {
return false; return false;
} }
@ -662,8 +675,8 @@ export class CoreSite {
* @param {any} data Arguments to pass to the method. * @param {any} data Arguments to pass to the method.
* @return {string} Cache ID. * @return {string} Cache ID.
*/ */
protected getCacheId(method: string, data: any) : string { protected getCacheId(method: string, data: any): string {
return <string>Md5.hashAsciiStr(method + ':' + this.utils.sortAndStringify(data)); return <string> Md5.hashAsciiStr(method + ':' + this.utils.sortAndStringify(data));
} }
/** /**
@ -675,32 +688,33 @@ export class CoreSite {
* @param {boolean} emergency Whether it's an "emergency" cache call (WS call failed). * @param {boolean} emergency Whether it's an "emergency" cache call (WS call failed).
* @return {Promise<any>} Promise resolved with the WS response. * @return {Promise<any>} Promise resolved with the WS response.
*/ */
protected getFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, emergency?: boolean) : Promise<any> { protected getFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, emergency?: boolean): Promise<any> {
let id = this.getCacheId(method, data), const id = this.getCacheId(method, data);
promise; let promise;
if (!this.db || !preSets.getFromCache) { if (!this.db || !preSets.getFromCache) {
return Promise.reject(null); return Promise.reject(null);
} }
if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) { if (preSets.getCacheUsingCacheKey || (emergency && preSets.getEmergencyCacheUsingCacheKey)) {
promise = this.db.getRecords(this.WS_CACHE_TABLE, {key: preSets.cacheKey}).then((entries) => { promise = this.db.getRecords(this.WS_CACHE_TABLE, { key: preSets.cacheKey }).then((entries) => {
if (!entries.length) { if (!entries.length) {
// Cache key not found, get by params sent. // Cache key not found, get by params sent.
return this.db.getRecord(this.WS_CACHE_TABLE, {id: id}); return this.db.getRecord(this.WS_CACHE_TABLE, { id: id });
} else if (entries.length > 1) { } else if (entries.length > 1) {
// More than one entry found. Search the one with same ID as this call. // More than one entry found. Search the one with same ID as this call.
for (let i = 0, len = entries.length; i < len; i++) { for (let i = 0, len = entries.length; i < len; i++) {
let entry = entries[i]; const entry = entries[i];
if (entry.id == id) { if (entry.id == id) {
return entry; return entry;
} }
} }
} }
return entries[0]; return entries[0];
}); });
} else { } else {
promise = this.db.getRecord(this.WS_CACHE_TABLE, {id: id}); promise = this.db.getRecord(this.WS_CACHE_TABLE, { id: id });
} }
return promise.then((entry) => { return promise.then((entry) => {
@ -711,6 +725,7 @@ export class CoreSite {
if (!preSets.omitExpires) { if (!preSets.omitExpires) {
if (now > entry.expirationTime) { if (now > entry.expirationTime) {
this.logger.debug('Cached element found, but it is expired'); this.logger.debug('Cached element found, but it is expired');
return Promise.reject(null); return Promise.reject(null);
} }
} }
@ -718,6 +733,7 @@ export class CoreSite {
if (typeof entry != 'undefined' && typeof entry.data != 'undefined') { if (typeof entry != 'undefined' && typeof entry.data != 'undefined') {
const expires = (entry.expirationTime - now) / 1000; const expires = (entry.expirationTime - now) / 1000;
this.logger.info(`Cached element found, id: ${id} expires in ${expires} seconds`); this.logger.info(`Cached element found, id: ${id} expires in ${expires} seconds`);
return JSON.parse(entry.data); return JSON.parse(entry.data);
} }
@ -734,14 +750,14 @@ export class CoreSite {
* @param {CoreSiteWSPreSets} preSets Extra options. * @param {CoreSiteWSPreSets} preSets Extra options.
* @return {Promise<any>} Promise resolved when the response is saved. * @return {Promise<any>} Promise resolved when the response is saved.
*/ */
protected saveToCache(method: string, data: any, response: any, preSets: CoreSiteWSPreSets) : Promise<any> { protected saveToCache(method: string, data: any, response: any, preSets: CoreSiteWSPreSets): Promise<any> {
let id = this.getCacheId(method, data), const id = this.getCacheId(method, data),
cacheExpirationTime = CoreConfigConstants.cache_expiration_time,
promise,
entry: any = { entry: any = {
id: id, id: id,
data: JSON.stringify(response) data: JSON.stringify(response)
} };
let cacheExpirationTime = CoreConfigConstants.cache_expiration_time,
promise;
if (!this.db) { if (!this.db) {
return Promise.reject(null); return Promise.reject(null);
@ -761,7 +777,8 @@ export class CoreSite {
if (preSets.cacheKey) { if (preSets.cacheKey) {
entry.key = preSets.cacheKey; entry.key = preSets.cacheKey;
} }
return this.db.insertOrUpdateRecord(this.WS_CACHE_TABLE, entry, {id: id});
return this.db.insertOrUpdateRecord(this.WS_CACHE_TABLE, entry, { id: id });
}); });
} }
} }
@ -775,16 +792,16 @@ export class CoreSite {
* @param {boolean} [allCacheKey] True to delete all entries with the cache key, false to delete only by ID. * @param {boolean} [allCacheKey] True to delete all entries with the cache key, false to delete only by ID.
* @return {Promise<any>} Promise resolved when the entries are deleted. * @return {Promise<any>} Promise resolved when the entries are deleted.
*/ */
protected deleteFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, allCacheKey?: boolean) : Promise<any> { protected deleteFromCache(method: string, data: any, preSets: CoreSiteWSPreSets, allCacheKey?: boolean): Promise<any> {
const id = this.getCacheId(method, data); const id = this.getCacheId(method, data);
if (!this.db) { if (!this.db) {
return Promise.reject(null); return Promise.reject(null);
} else { } else {
if (allCacheKey) { if (allCacheKey) {
return this.db.deleteRecords(this.WS_CACHE_TABLE, {key: preSets.cacheKey}); return this.db.deleteRecords(this.WS_CACHE_TABLE, { key: preSets.cacheKey });
} else { } else {
return this.db.deleteRecords(this.WS_CACHE_TABLE, {id: id}); return this.db.deleteRecords(this.WS_CACHE_TABLE, { id: id });
} }
} }
} }
@ -797,7 +814,7 @@ export class CoreSite {
* @param {Function} [onProgress] Function to call on progress. * @param {Function} [onProgress] Function to call on progress.
* @return {Promise<any>} Promise resolved when uploaded. * @return {Promise<any>} Promise resolved when uploaded.
*/ */
uploadFile(filePath: string, options: CoreWSFileUploadOptions, onProgress?: (event: ProgressEvent) => any) : Promise<any> { uploadFile(filePath: string, options: CoreWSFileUploadOptions, onProgress?: (event: ProgressEvent) => any): Promise<any> {
if (!options.fileArea) { if (!options.fileArea) {
options.fileArea = 'draft'; options.fileArea = 'draft';
} }
@ -813,13 +830,14 @@ export class CoreSite {
* *
* @return {Promise<any>} Promise resolved when the cache entries are invalidated. * @return {Promise<any>} Promise resolved when the cache entries are invalidated.
*/ */
invalidateWsCache() : Promise<any> { invalidateWsCache(): Promise<any> {
if (!this.db) { if (!this.db) {
return Promise.reject(null); return Promise.reject(null);
} }
this.logger.debug('Invalidate all the cache for site: ' + this.id); this.logger.debug('Invalidate all the cache for site: ' + this.id);
return this.db.updateRecords(this.WS_CACHE_TABLE, {expirationTime: 0});
return this.db.updateRecords(this.WS_CACHE_TABLE, { expirationTime: 0 });
} }
/** /**
@ -828,7 +846,7 @@ export class CoreSite {
* @param {string} key Key to search. * @param {string} key Key to search.
* @return {Promise<any>} Promise resolved when the cache entries are invalidated. * @return {Promise<any>} Promise resolved when the cache entries are invalidated.
*/ */
invalidateWsCacheForKey(key: string) : Promise<any> { invalidateWsCacheForKey(key: string): Promise<any> {
if (!this.db) { if (!this.db) {
return Promise.reject(null); return Promise.reject(null);
} }
@ -837,7 +855,8 @@ export class CoreSite {
} }
this.logger.debug('Invalidate cache for key: ' + key); this.logger.debug('Invalidate cache for key: ' + key);
return this.db.updateRecords(this.WS_CACHE_TABLE, {expirationTime: 0}, {key: key});
return this.db.updateRecords(this.WS_CACHE_TABLE, { expirationTime: 0 }, { key: key });
} }
/** /**
@ -846,7 +865,7 @@ export class CoreSite {
* @param {string[]} keys Keys to search. * @param {string[]} keys Keys to search.
* @return {Promise<any>} Promise resolved when the cache entries are invalidated. * @return {Promise<any>} Promise resolved when the cache entries are invalidated.
*/ */
invalidateMultipleWsCacheForKey(keys: string[]) : Promise<any> { invalidateMultipleWsCacheForKey(keys: string[]): Promise<any> {
if (!this.db) { if (!this.db) {
return Promise.reject(null); return Promise.reject(null);
} }
@ -854,7 +873,7 @@ export class CoreSite {
return Promise.resolve(); return Promise.resolve();
} }
let promises = []; const promises = [];
this.logger.debug('Invalidating multiple cache keys'); this.logger.debug('Invalidating multiple cache keys');
keys.forEach((key) => { keys.forEach((key) => {
@ -870,7 +889,7 @@ export class CoreSite {
* @param {string} key Key to search. * @param {string} key Key to search.
* @return {Promise} Promise resolved when the cache entries are invalidated. * @return {Promise} Promise resolved when the cache entries are invalidated.
*/ */
invalidateWsCacheForKeyStartingWith(key: string) : Promise<any> { invalidateWsCacheForKeyStartingWith(key: string): Promise<any> {
if (!this.db) { if (!this.db) {
return Promise.reject(null); return Promise.reject(null);
} }
@ -879,8 +898,10 @@ export class CoreSite {
} }
this.logger.debug('Invalidate cache for key starting with: ' + key); this.logger.debug('Invalidate cache for key starting with: ' + key);
let sql = 'UPDATE ' + this.WS_CACHE_TABLE + ' SET expirationTime=0 WHERE key LIKE ?';
return this.db.execute(sql, [key + "%"]); const sql = 'UPDATE ' + this.WS_CACHE_TABLE + ' SET expirationTime=0 WHERE key LIKE ?';
return this.db.execute(sql, [key + '%']);
} }
/** /**
@ -890,7 +911,7 @@ export class CoreSite {
* @param {string} url The url to be fixed. * @param {string} url The url to be fixed.
* @return {string} Fixed URL. * @return {string} Fixed URL.
*/ */
fixPluginfileURL(url: string) : string { fixPluginfileURL(url: string): string {
return this.urlUtils.fixPluginfileURL(url, this.token); return this.urlUtils.fixPluginfileURL(url, this.token);
} }
@ -899,7 +920,7 @@ export class CoreSite {
* *
* @return {Promise<any>} Promise to be resolved when the DB is deleted. * @return {Promise<any>} Promise to be resolved when the DB is deleted.
*/ */
deleteDB() : Promise<any> { deleteDB(): Promise<any> {
return this.dbProvider.deleteDB('Site-' + this.id); return this.dbProvider.deleteDB('Site-' + this.id);
} }
@ -908,9 +929,10 @@ export class CoreSite {
* *
* @return {Promise<any>} Promise to be resolved when the DB is deleted. * @return {Promise<any>} Promise to be resolved when the DB is deleted.
*/ */
deleteFolder() : Promise<any> { deleteFolder(): Promise<any> {
if (this.fileProvider.isAvailable()) { if (this.fileProvider.isAvailable()) {
const siteFolder = this.fileProvider.getSiteFolder(this.id); const siteFolder = this.fileProvider.getSiteFolder(this.id);
return this.fileProvider.removeDir(siteFolder).catch(() => { return this.fileProvider.removeDir(siteFolder).catch(() => {
// Ignore any errors, $mmFS.removeDir fails if folder doesn't exists. // Ignore any errors, $mmFS.removeDir fails if folder doesn't exists.
}); });
@ -924,9 +946,10 @@ export class CoreSite {
* *
* @return {Promise<number>} Promise resolved with the site space usage (size). * @return {Promise<number>} Promise resolved with the site space usage (size).
*/ */
getSpaceUsage() : Promise<number> { getSpaceUsage(): Promise<number> {
if (this.fileProvider.isAvailable()) { if (this.fileProvider.isAvailable()) {
const siteFolderPath = this.fileProvider.getSiteFolder(this.id); const siteFolderPath = this.fileProvider.getSiteFolder(this.id);
return this.fileProvider.getDirectorySize(siteFolderPath).catch(() => { return this.fileProvider.getDirectorySize(siteFolderPath).catch(() => {
return 0; return 0;
}); });
@ -941,8 +964,9 @@ export class CoreSite {
* @param {string} [page] Docs page to go to. * @param {string} [page] Docs page to go to.
* @return {Promise<string>} Promise resolved with the Moodle docs URL. * @return {Promise<string>} Promise resolved with the Moodle docs URL.
*/ */
getDocsUrl(page?: string) : Promise<string> { getDocsUrl(page?: string): Promise<string> {
const release = this.infos.release ? this.infos.release : undefined; const release = this.infos.release ? this.infos.release : undefined;
return this.urlUtils.getDocsUrl(release, page); return this.urlUtils.getDocsUrl(release, page);
} }
@ -952,27 +976,29 @@ export class CoreSite {
* @param {boolean} [retrying] True if we're retrying the check. * @param {boolean} [retrying] True if we're retrying the check.
* @return {Promise<LocalMobileResponse>} Promise resolved when the check is done. * @return {Promise<LocalMobileResponse>} Promise resolved when the check is done.
*/ */
checkLocalMobilePlugin(retrying?: boolean) : Promise<LocalMobileResponse> { checkLocalMobilePlugin(retrying?: boolean): Promise<LocalMobileResponse> {
const checkUrl = this.siteUrl + '/local/mobile/check.php', const checkUrl = this.siteUrl + '/local/mobile/check.php',
service = CoreConfigConstants.wsextservice; service = CoreConfigConstants.wsextservice;
if (!service) { if (!service) {
// External service not defined. // External service not defined.
return Promise.resolve({code: 0}); return Promise.resolve({ code: 0 });
} }
let observable = this.http.post(checkUrl, {service: service}).timeout(CoreConstants.WS_TIMEOUT); const observable = this.http.post(checkUrl, { service: service }).timeout(CoreConstants.WS_TIMEOUT);
return this.utils.observableToPromise(observable).then((data: any) => { return this.utils.observableToPromise(observable).then((data: any) => {
if (typeof data != 'undefined' && data.errorcode === 'requirecorrectaccess') { if (typeof data != 'undefined' && data.errorcode === 'requirecorrectaccess') {
if (!retrying) { if (!retrying) {
this.siteUrl = this.urlUtils.addOrRemoveWWW(this.siteUrl); this.siteUrl = this.urlUtils.addOrRemoveWWW(this.siteUrl);
return this.checkLocalMobilePlugin(true); return this.checkLocalMobilePlugin(true);
} else { } else {
return Promise.reject(data.error); return Promise.reject(data.error);
} }
} else if (typeof data == 'undefined' || typeof data.code == 'undefined') { } else if (typeof data == 'undefined' || typeof data.code == 'undefined') {
// local_mobile returned something we didn't expect. Let's assume it's not installed. // The local_mobile returned something we didn't expect. Let's assume it's not installed.
return {code: 0, warning: 'core.login.localmobileunexpectedresponse'}; return { code: 0, warning: 'core.login.localmobileunexpectedresponse' };
} }
const code = parseInt(data.code, 10); const code = parseInt(data.code, 10);
@ -986,7 +1012,7 @@ export class CoreSite {
return Promise.reject(this.translate.instant('core.login.webservicesnotenabled')); return Promise.reject(this.translate.instant('core.login.webservicesnotenabled'));
case 3: case 3:
// Extended service not enabled, but the official is enabled. // Extended service not enabled, but the official is enabled.
return {code: 0}; return { code: 0 };
case 4: case 4:
// Neither extended or official services enabled. // Neither extended or official services enabled.
return Promise.reject(this.translate.instant('core.login.mobileservicesnotenabled')); return Promise.reject(this.translate.instant('core.login.mobileservicesnotenabled'));
@ -994,10 +1020,10 @@ export class CoreSite {
return Promise.reject(this.translate.instant('core.unexpectederror')); return Promise.reject(this.translate.instant('core.unexpectederror'));
} }
} else { } else {
return {code: code, service: service, coresupported: !!data.coresupported}; return { code: code, service: service, coresupported: !!data.coresupported };
} }
}, () => { }, () => {
return {code: 0}; return { code: 0 };
}); });
} }
@ -1006,7 +1032,7 @@ export class CoreSite {
* *
* @return {boolean} Whether the App is able to use local_mobile plugin for this site. * @return {boolean} Whether the App is able to use local_mobile plugin for this site.
*/ */
checkIfAppUsesLocalMobile() : boolean { checkIfAppUsesLocalMobile(): boolean {
let appUsesLocalMobile = false; let appUsesLocalMobile = false;
if (!this.infos || !this.infos.functions) { if (!this.infos || !this.infos.functions) {
@ -1027,7 +1053,7 @@ export class CoreSite {
* *
* @return {Promise<any>} Promise resolved it local_mobile was added, rejected otherwise. * @return {Promise<any>} Promise resolved it local_mobile was added, rejected otherwise.
*/ */
checkIfLocalMobileInstalledAndNotUsed() : Promise<any> { checkIfLocalMobileInstalledAndNotUsed(): Promise<any> {
const appUsesLocalMobile = this.checkIfAppUsesLocalMobile(); const appUsesLocalMobile = this.checkIfAppUsesLocalMobile();
if (appUsesLocalMobile) { if (appUsesLocalMobile) {
@ -1035,11 +1061,12 @@ export class CoreSite {
return Promise.reject(null); return Promise.reject(null);
} }
return this.checkLocalMobilePlugin().then((data: LocalMobileResponse) : any => { return this.checkLocalMobilePlugin().then((data: LocalMobileResponse): any => {
if (typeof data.service == 'undefined') { if (typeof data.service == 'undefined') {
// local_mobile NOT installed. Reject. // The local_mobile NOT installed. Reject.
return Promise.reject(null); return Promise.reject(null);
} }
return data; return data;
}); });
} }
@ -1050,13 +1077,14 @@ export class CoreSite {
* @param {string} url URL to check. * @param {string} url URL to check.
* @return {boolean} Whether the URL belongs to this site. * @return {boolean} Whether the URL belongs to this site.
*/ */
containsUrl(url: string) : boolean { containsUrl(url: string): boolean {
if (!url) { if (!url) {
return false; return false;
} }
const siteUrl = this.urlUtils.removeProtocolAndWWW(this.siteUrl); const siteUrl = this.urlUtils.removeProtocolAndWWW(this.siteUrl);
url = this.urlUtils.removeProtocolAndWWW(url); url = this.urlUtils.removeProtocolAndWWW(url);
return url.indexOf(siteUrl) == 0; return url.indexOf(siteUrl) == 0;
} }
@ -1065,12 +1093,13 @@ export class CoreSite {
* *
* @return {Promise<any>} Promise resolved with public config. Rejected with an object if error, see CoreWSProvider.callAjax. * @return {Promise<any>} Promise resolved with public config. Rejected with an object if error, see CoreWSProvider.callAjax.
*/ */
getPublicConfig() : Promise<any> { getPublicConfig(): Promise<any> {
return this.wsProvider.callAjax('tool_mobile_get_public_config', {}, {siteUrl: this.siteUrl}).then((config) => { return this.wsProvider.callAjax('tool_mobile_get_public_config', {}, { siteUrl: this.siteUrl }).then((config) => {
// Use the wwwroot returned by the server. // Use the wwwroot returned by the server.
if (config.httpswwwroot) { if (config.httpswwwroot) {
this.siteUrl = config.httpswwwroot; this.siteUrl = config.httpswwwroot;
} }
return config; return config;
}); });
} }
@ -1082,7 +1111,7 @@ export class CoreSite {
* @param {string} [alertMessage] If defined, an alert will be shown before opening the browser. * @param {string} [alertMessage] If defined, an alert will be shown before opening the browser.
* @return {Promise<any>} Promise resolved when done, rejected otherwise. * @return {Promise<any>} Promise resolved when done, rejected otherwise.
*/ */
openInBrowserWithAutoLogin(url: string, alertMessage?: string) : Promise<any> { openInBrowserWithAutoLogin(url: string, alertMessage?: string): Promise<any> {
return this.openWithAutoLogin(false, url, undefined, alertMessage); return this.openWithAutoLogin(false, url, undefined, alertMessage);
} }
@ -1093,7 +1122,7 @@ export class CoreSite {
* @param {string} [alertMessage] If defined, an alert will be shown before opening the browser. * @param {string} [alertMessage] If defined, an alert will be shown before opening the browser.
* @return {Promise<any>} Promise resolved when done, rejected otherwise. * @return {Promise<any>} Promise resolved when done, rejected otherwise.
*/ */
openInBrowserWithAutoLoginIfSameSite(url: string, alertMessage?: string) : Promise<any> { openInBrowserWithAutoLoginIfSameSite(url: string, alertMessage?: string): Promise<any> {
return this.openWithAutoLoginIfSameSite(false, url, undefined, alertMessage); return this.openWithAutoLoginIfSameSite(false, url, undefined, alertMessage);
} }
@ -1105,7 +1134,7 @@ export class CoreSite {
* @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser. * @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser.
* @return {Promise<InAppBrowserObject>} Promise resolved when done. * @return {Promise<InAppBrowserObject>} Promise resolved when done.
*/ */
openInAppWithAutoLogin(url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> { openInAppWithAutoLogin(url: string, options?: any, alertMessage?: string): Promise<InAppBrowserObject> {
return this.openWithAutoLogin(true, url, options, alertMessage); return this.openWithAutoLogin(true, url, options, alertMessage);
} }
@ -1117,7 +1146,7 @@ export class CoreSite {
* @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser. * @param {string} [alertMessage] If defined, an alert will be shown before opening the inappbrowser.
* @return {Promise<InAppBrowserObject>} Promise resolved when done. * @return {Promise<InAppBrowserObject>} Promise resolved when done.
*/ */
openInAppWithAutoLoginIfSameSite(url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> { openInAppWithAutoLoginIfSameSite(url: string, options?: any, alertMessage?: string): Promise<InAppBrowserObject> {
return this.openWithAutoLoginIfSameSite(true, url, options, alertMessage); return this.openWithAutoLoginIfSameSite(true, url, options, alertMessage);
} }
@ -1130,16 +1159,16 @@ export class CoreSite {
* @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser. * @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser.
* @return {Promise<InAppBrowserObject>} Promise resolved when done. Resolve param is returned only if inApp=true. * @return {Promise<InAppBrowserObject>} Promise resolved when done. Resolve param is returned only if inApp=true.
*/ */
openWithAutoLogin(inApp: boolean, url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> { openWithAutoLogin(inApp: boolean, url: string, options?: any, alertMessage?: string): Promise<InAppBrowserObject> {
// Convenience function to open the URL. // Convenience function to open the URL.
let open = (url) => { const open = (url): Promise<any> => {
return new Promise<InAppBrowserObject>((resolve, reject) => { return new Promise<InAppBrowserObject>((resolve, reject): void => {
if (modal) { if (modal) {
modal.dismiss(); modal.dismiss();
} }
if (alertMessage) { if (alertMessage) {
let alert = this.domUtils.showAlert(this.translate.instant('core.notice'), alertMessage, undefined, 3000); const alert = this.domUtils.showAlert(this.translate.instant('core.notice'), alertMessage, undefined, 3000);
alert.onDidDismiss(() => { alert.onDidDismiss(() => {
if (inApp) { if (inApp) {
resolve(this.utils.openInApp(url, options)); resolve(this.utils.openInApp(url, options));
@ -1157,8 +1186,8 @@ export class CoreSite {
}); });
}; };
if (!this.privateToken || !this.wsAvailable('tool_mobile_get_autologin_key') || if (!this.privateToken || !this.wsAvailable('tool_mobile_get_autologin_key') ||
(this.lastAutoLogin && this.timeUtils.timestamp() - this.lastAutoLogin < 6 * CoreConstants.SECONDS_MINUTE)) { (this.lastAutoLogin && this.timeUtils.timestamp() - this.lastAutoLogin < CoreConstants.SECONDS_MINUTE * 6)) {
// No private token, WS not available or last auto-login was less than 6 minutes ago. // No private token, WS not available or last auto-login was less than 6 minutes ago.
// Open the final URL without auto-login. // Open the final URL without auto-login.
return Promise.resolve(open(url)); return Promise.resolve(open(url));
@ -1172,7 +1201,7 @@ export class CoreSite {
// Use write to not use cache. // Use write to not use cache.
return this.write('tool_mobile_get_autologin_key', params).then((data) => { return this.write('tool_mobile_get_autologin_key', params).then((data) => {
if (!data.autologinurl || !data.key) { if (!data.autologinurl || !data.key) {
// Not valid data, open the final URL without auto-login. // Not valid data, open the final URL without auto-login.
return open(url); return open(url);
} }
@ -1195,7 +1224,7 @@ export class CoreSite {
* @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser. * @param {string} [alertMessage] If defined, an alert will be shown before opening the browser/inappbrowser.
* @return {Promise<InAppBrowserObject>} Promise resolved when done. Resolve param is returned only if inApp=true. * @return {Promise<InAppBrowserObject>} Promise resolved when done. Resolve param is returned only if inApp=true.
*/ */
openWithAutoLoginIfSameSite(inApp: boolean, url: string, options?: any, alertMessage?: string) : Promise<InAppBrowserObject> { openWithAutoLoginIfSameSite(inApp: boolean, url: string, options?: any, alertMessage?: string): Promise<InAppBrowserObject> {
if (this.containsUrl(url)) { if (this.containsUrl(url)) {
return this.openWithAutoLogin(inApp, url, options, alertMessage); return this.openWithAutoLogin(inApp, url, options, alertMessage);
} else { } else {
@ -1204,6 +1233,7 @@ export class CoreSite {
} else { } else {
this.utils.openInBrowser(url); this.utils.openInBrowser(url);
} }
return Promise.resolve(null); return Promise.resolve(null);
} }
} }
@ -1216,10 +1246,10 @@ export class CoreSite {
* @param {boolean} [ignoreCache] True if it should ignore cached data. * @param {boolean} [ignoreCache] True if it should ignore cached data.
* @return {Promise<any>} Promise resolved with site config. * @return {Promise<any>} Promise resolved with site config.
*/ */
getConfig(name?: string, ignoreCache?: boolean) { getConfig(name?: string, ignoreCache?: boolean): Promise<any> {
let preSets: CoreSiteWSPreSets = { const preSets: CoreSiteWSPreSets = {
cacheKey: this.getConfigCacheKey() cacheKey: this.getConfigCacheKey()
} };
if (ignoreCache) { if (ignoreCache) {
preSets.getFromCache = false; preSets.getFromCache = false;
@ -1229,18 +1259,20 @@ export class CoreSite {
return this.read('tool_mobile_get_config', {}, preSets).then((config) => { return this.read('tool_mobile_get_config', {}, preSets).then((config) => {
if (name) { if (name) {
// Return the requested setting. // Return the requested setting.
for (let x in config.settings) { for (const x in config.settings) {
if (config.settings[x].name == name) { if (config.settings[x].name == name) {
return config.settings[x].value; return config.settings[x].value;
} }
} }
return Promise.reject(null); return Promise.reject(null);
} else { } else {
// Return all settings in the same array. // Return all settings in the same array.
let settings = {}; const settings = {};
config.settings.forEach((setting) => { config.settings.forEach((setting) => {
settings[setting.name] = setting.value; settings[setting.name] = setting.value;
}); });
return settings; return settings;
} }
}); });
@ -1251,7 +1283,7 @@ export class CoreSite {
* *
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateConfig() : Promise<any> { invalidateConfig(): Promise<any> {
return this.invalidateWsCacheForKey(this.getConfigCacheKey()); return this.invalidateWsCacheForKey(this.getConfigCacheKey());
} }
@ -1260,7 +1292,7 @@ export class CoreSite {
* *
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getConfigCacheKey() : string { protected getConfigCacheKey(): string {
return 'tool_mobile_get_config'; return 'tool_mobile_get_config';
} }
@ -1270,7 +1302,7 @@ export class CoreSite {
* @param {string} [name] Name of the setting to get. If not set, all settings will be returned. * @param {string} [name] Name of the setting to get. If not set, all settings will be returned.
* @return {any} Site config or a specific setting. * @return {any} Site config or a specific setting.
*/ */
getStoredConfig(name?: string) : any { getStoredConfig(name?: string): any {
if (!this.config) { if (!this.config) {
return; return;
} }
@ -1288,13 +1320,14 @@ export class CoreSite {
* @param {string} name Name of the feature to check. * @param {string} name Name of the feature to check.
* @return {boolean} Whether it's disabled. * @return {boolean} Whether it's disabled.
*/ */
isFeatureDisabled(name: string) : boolean { isFeatureDisabled(name: string): boolean {
const disabledFeatures = this.getStoredConfig('tool_mobile_disabledfeatures'); const disabledFeatures = this.getStoredConfig('tool_mobile_disabledfeatures');
if (!disabledFeatures) { if (!disabledFeatures) {
return false; return false;
} }
const regEx = new RegExp('(,|^)' + this.textUtils.escapeForRegex(name) + '(,|$)', 'g'); const regEx = new RegExp('(,|^)' + this.textUtils.escapeForRegex(name) + '(,|$)', 'g');
return !!disabledFeatures.match(regEx); return !!disabledFeatures.match(regEx);
} }
@ -1319,7 +1352,7 @@ export class CoreSite {
* This function only accepts versions from 2.4.0 and above. If any of the versions supplied isn't found, it will assume * This function only accepts versions from 2.4.0 and above. If any of the versions supplied isn't found, it will assume
* it's the last released major version. * it's the last released major version.
*/ */
isVersionGreaterEqualThan(versions: string | string[]) : boolean { isVersionGreaterEqualThan(versions: string | string[]): boolean {
const siteVersion = parseInt(this.getInfo().version, 10); const siteVersion = parseInt(this.getInfo().version, 10);
if (Array.isArray(versions)) { if (Array.isArray(versions)) {
@ -1328,7 +1361,7 @@ export class CoreSite {
} }
for (let i = 0; i < versions.length; i++) { for (let i = 0; i < versions.length; i++) {
let versionNumber = this.getVersionNumber(versions[i]); const versionNumber = this.getVersionNumber(versions[i]);
if (i == versions.length - 1) { if (i == versions.length - 1) {
// It's the last version, check only if site version is greater than this one. // It's the last version, check only if site version is greater than this one.
return siteVersion >= versionNumber; return siteVersion >= versionNumber;
@ -1354,8 +1387,8 @@ export class CoreSite {
* @param {string} version Release version to convert to version number. * @param {string} version Release version to convert to version number.
* @return {number} Version number, 0 if invalid. * @return {number} Version number, 0 if invalid.
*/ */
protected getVersionNumber(version: string) : number { protected getVersionNumber(version: string): number {
let data = this.getMajorAndMinor(version); const data = this.getMajorAndMinor(version);
if (!data) { if (!data) {
// Invalid version. // Invalid version.
@ -1376,17 +1409,17 @@ export class CoreSite {
* @param {string} version Release version (e.g. '3.1.0'). * @param {string} version Release version (e.g. '3.1.0').
* @return {object} Object with major and minor. Returns false if invalid version. * @return {object} Object with major and minor. Returns false if invalid version.
*/ */
protected getMajorAndMinor(version) : any { protected getMajorAndMinor(version: string): any {
const match = version.match(/(\d)+(?:\.(\d)+)?(?:\.(\d)+)?/); const match = version.match(/(\d)+(?:\.(\d)+)?(?:\.(\d)+)?/);
if (!match || !match[1]) { if (!match || !match[1]) {
// Invalid version. // Invalid version.
return false; return false;
} }
return { return {
major: match[1] + '.' + (match[2] || '0'), major: match[1] + '.' + (match[2] || '0'),
minor: parseInt(match[3] || 0, 10) minor: parseInt(match[3], 10) || 0
} };
} }
/** /**
@ -1395,10 +1428,10 @@ export class CoreSite {
* @param {string} version Release version (e.g. '3.1.0'). * @param {string} version Release version (e.g. '3.1.0').
* @return {number} Next major version number. * @return {number} Next major version number.
*/ */
protected getNextMajorVersionNumber(version: string) : number { protected getNextMajorVersionNumber(version: string): number {
let data = this.getMajorAndMinor(version), const data = this.getMajorAndMinor(version),
position,
releases = Object.keys(this.moodleReleases); releases = Object.keys(this.moodleReleases);
let position;
if (!data) { if (!data) {
// Invalid version. // Invalid version.
@ -1407,7 +1440,7 @@ export class CoreSite {
position = releases.indexOf(data.major); position = releases.indexOf(data.major);
if (position == -1 || position == releases.length -1) { if (position == -1 || position == releases.length - 1) {
// Major version not found or it's the last one. Use the last one. // Major version not found or it's the last one. Use the last one.
return this.moodleReleases[releases[position]]; return this.moodleReleases[releases[position]];
} }

View File

@ -63,14 +63,14 @@ export class SQLiteDB {
* @return SQL query. * @return SQL query.
*/ */
buildCreateTableSql(name: string, columns: any[], primaryKeys?: string[], uniqueKeys?: string[][], foreignKeys?: any[], buildCreateTableSql(name: string, columns: any[], primaryKeys?: string[], uniqueKeys?: string[][], foreignKeys?: any[],
tableCheck?: string) : string { tableCheck?: string): string {
let sql = `CREATE TABLE IF NOT EXISTS ${name} (`, const columnsSql = [];
columnsSql = []; let sql = `CREATE TABLE IF NOT EXISTS ${name} (`;
// First define all the columns. // First define all the columns.
for (let index in columns) { for (const index in columns) {
let column = columns[index], const column = columns[index];
columnSql: string = column.name || ''; let columnSql: string = column.name || '';
if (column.type) { if (column.type) {
columnSql += ' ' + column.type; columnSql += ' ' + column.type;
@ -110,8 +110,8 @@ export class SQLiteDB {
} }
if (uniqueKeys && uniqueKeys.length) { if (uniqueKeys && uniqueKeys.length) {
for (let index in uniqueKeys) { for (const index in uniqueKeys) {
let setOfKeys = uniqueKeys[index]; const setOfKeys = uniqueKeys[index];
if (setOfKeys && setOfKeys.length) { if (setOfKeys && setOfKeys.length) {
sql += `, UNIQUE (${setOfKeys.join(', ')})`; sql += `, UNIQUE (${setOfKeys.join(', ')})`;
} }
@ -122,9 +122,8 @@ export class SQLiteDB {
sql += `, CHECK (${tableCheck})`; sql += `, CHECK (${tableCheck})`;
} }
for (const index in foreignKeys) {
for (let index in foreignKeys) { const foreignKey = foreignKeys[index];
let foreignKey = foreignKeys[index];
if (!foreignKey.columns || !!foreignKey.columns.length) { if (!foreignKey.columns || !!foreignKey.columns.length) {
return; return;
@ -146,8 +145,10 @@ export class SQLiteDB {
/** /**
* Close the database. * Close the database.
*
* @return {Promise<any>} Promise resolved when done.
*/ */
close() { close(): Promise<any> {
return this.ready().then(() => { return this.ready().then(() => {
return this.db.close(); return this.db.close();
}); });
@ -160,8 +161,9 @@ export class SQLiteDB {
* @param {object} [conditions] The conditions to build the where clause. Must not contain numeric indexes. * @param {object} [conditions] The conditions to build the where clause. Must not contain numeric indexes.
* @return {Promise<number>} Promise resolved with the count of records returned from the specified criteria. * @return {Promise<number>} Promise resolved with the count of records returned from the specified criteria.
*/ */
countRecords(table: string, conditions?: object) : Promise<number> { countRecords(table: string, conditions?: object): Promise<number> {
let selectAndParams = this.whereClause(conditions); const selectAndParams = this.whereClause(conditions);
return this.countRecordsSelect(table, selectAndParams[0], selectAndParams[1]); return this.countRecordsSelect(table, selectAndParams[0], selectAndParams[1]);
} }
@ -174,10 +176,11 @@ export class SQLiteDB {
* @param {string} [countItem] The count string to be used in the SQL call. Default is COUNT('x'). * @param {string} [countItem] The count string to be used in the SQL call. Default is COUNT('x').
* @return {Promise<number>} Promise resolved with the count of records returned from the specified criteria. * @return {Promise<number>} Promise resolved with the count of records returned from the specified criteria.
*/ */
countRecordsSelect(table: string, select='', params?: any, countItem="COUNT('x')") : Promise<number> { countRecordsSelect(table: string, select: string = '', params?: any, countItem: string = 'COUNT(\'x\')'): Promise<number> {
if (select) { if (select) {
select = 'WHERE ' + select; select = 'WHERE ' + select;
} }
return this.countRecordsSql(`SELECT ${countItem} FROM ${table} ${select}`, params); return this.countRecordsSql(`SELECT ${countItem} FROM ${table} ${select}`, params);
} }
@ -190,11 +193,12 @@ export class SQLiteDB {
* @param {any} [params] An array of sql parameters. * @param {any} [params] An array of sql parameters.
* @return {Promise<number>} Promise resolved with the count. * @return {Promise<number>} Promise resolved with the count.
*/ */
countRecordsSql(sql: string, params?: any) : Promise<number> { countRecordsSql(sql: string, params?: any): Promise<number> {
return this.getFieldSql(sql, params).then((count) => { return this.getFieldSql(sql, params).then((count) => {
if (typeof count != 'number' || count < 0) { if (typeof count != 'number' || count < 0) {
return 0; return 0;
} }
return count; return count;
}); });
} }
@ -223,8 +227,9 @@ export class SQLiteDB {
* @return {Promise<any>} Promise resolved when success. * @return {Promise<any>} Promise resolved when success.
*/ */
createTable(name: string, columns: any[], primaryKeys?: string[], uniqueKeys?: string[][], foreignKeys?: any[], createTable(name: string, columns: any[], primaryKeys?: string[], uniqueKeys?: string[][], foreignKeys?: any[],
tableCheck?: string) : Promise<any> { tableCheck?: string): Promise<any> {
let sql = this.buildCreateTableSql(name, columns, primaryKeys, uniqueKeys, foreignKeys, tableCheck); const sql = this.buildCreateTableSql(name, columns, primaryKeys, uniqueKeys, foreignKeys, tableCheck);
return this.execute(sql); return this.execute(sql);
} }
@ -234,9 +239,9 @@ export class SQLiteDB {
* @param {any} table Table schema. * @param {any} table Table schema.
* @return {Promise<any>} Promise resolved when success. * @return {Promise<any>} Promise resolved when success.
*/ */
createTableFromSchema(table: any) : Promise<any> { createTableFromSchema(table: any): Promise<any> {
return this.createTable(table.name, table.columns, table.primaryKeys, table.uniqueKeys, return this.createTable(table.name, table.columns, table.primaryKeys, table.uniqueKeys,
table.foreignKeys, table.tableCheck); table.foreignKeys, table.tableCheck);
} }
/** /**
@ -245,11 +250,12 @@ export class SQLiteDB {
* @param {any[]} tables List of table schema. * @param {any[]} tables List of table schema.
* @return {Promise<any>} Promise resolved when success. * @return {Promise<any>} Promise resolved when success.
*/ */
createTablesFromSchema(tables: any[]) : Promise<any> { createTablesFromSchema(tables: any[]): Promise<any> {
let promises = []; const promises = [];
tables.forEach((table) => { tables.forEach((table) => {
promises.push(this.createTableFromSchema(table)); promises.push(this.createTableFromSchema(table));
}); });
return Promise.all(promises); return Promise.all(promises);
} }
@ -261,13 +267,14 @@ export class SQLiteDB {
* @param {object} [conditions] The conditions to build the where clause. Must not contain numeric indexes. * @param {object} [conditions] The conditions to build the where clause. Must not contain numeric indexes.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
deleteRecords(table: string, conditions?: object) : Promise<any> { deleteRecords(table: string, conditions?: object): Promise<any> {
if (conditions === null || typeof conditions == 'undefined') { if (conditions === null || typeof conditions == 'undefined') {
// No conditions, delete the whole table. // No conditions, delete the whole table.
return this.execute(`DELETE FROM TABLE ${table}`); return this.execute(`DELETE FROM TABLE ${table}`);
} }
let selectAndParams = this.whereClause(conditions); const selectAndParams = this.whereClause(conditions);
return this.deleteRecordsSelect(table, selectAndParams[0], selectAndParams[1]); return this.deleteRecordsSelect(table, selectAndParams[0], selectAndParams[1]);
} }
@ -279,8 +286,9 @@ export class SQLiteDB {
* @param {any[]} values The values field might take. * @param {any[]} values The values field might take.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
deleteRecordsList(table: string, field: string, values: any[]) : Promise<any> { deleteRecordsList(table: string, field: string, values: any[]): Promise<any> {
let selectAndParams = this.whereClauseList(field, values); const selectAndParams = this.whereClauseList(field, values);
return this.deleteRecordsSelect(table, selectAndParams[0], selectAndParams[1]); return this.deleteRecordsSelect(table, selectAndParams[0], selectAndParams[1]);
} }
@ -292,7 +300,7 @@ export class SQLiteDB {
* @param {any[]} [params] Array of sql parameters. * @param {any[]} [params] Array of sql parameters.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
deleteRecordsSelect(table: string, select='', params?: any[]) : Promise<any> { deleteRecordsSelect(table: string, select: string = '', params?: any[]): Promise<any> {
if (select) { if (select) {
select = 'WHERE ' + select; select = 'WHERE ' + select;
} }
@ -309,7 +317,7 @@ export class SQLiteDB {
* @param {any[]} params Query parameters. * @param {any[]} params Query parameters.
* @return {Promise<any>} Promise resolved with the result. * @return {Promise<any>} Promise resolved with the result.
*/ */
execute(sql: string, params?: any[]) : Promise<any> { execute(sql: string, params?: any[]): Promise<any> {
return this.ready().then(() => { return this.ready().then(() => {
return this.db.executeSql(sql, params); return this.db.executeSql(sql, params);
}); });
@ -323,7 +331,7 @@ export class SQLiteDB {
* @param {any[]} sqlStatements SQL statements to execute. * @param {any[]} sqlStatements SQL statements to execute.
* @return {Promise<any>} Promise resolved with the result. * @return {Promise<any>} Promise resolved with the result.
*/ */
executeBatch(sqlStatements: any[]) : Promise<any> { executeBatch(sqlStatements: any[]): Promise<any> {
return this.ready().then(() => { return this.ready().then(() => {
return this.db.sqlBatch(sqlStatements); return this.db.sqlBatch(sqlStatements);
}); });
@ -334,10 +342,10 @@ export class SQLiteDB {
* *
* @param {object} data Data to insert. * @param {object} data Data to insert.
*/ */
protected formatDataToInsert(data: object) : void { protected formatDataToInsert(data: object): void {
// Remove undefined entries and convert null to "NULL". // Remove undefined entries and convert null to "NULL".
for (let name in data) { for (const name in data) {
let value = data[name]; const value = data[name];
if (typeof value == 'undefined') { if (typeof value == 'undefined') {
delete data[name]; delete data[name];
} }
@ -350,7 +358,7 @@ export class SQLiteDB {
* @param {string} table The table to query. * @param {string} table The table to query.
* @return {Promise<any>} Promise resolved with the records. * @return {Promise<any>} Promise resolved with the records.
*/ */
getAllRecords(table: string) : Promise<any> { getAllRecords(table: string): Promise<any> {
return this.getRecords(table); return this.getRecords(table);
} }
@ -362,8 +370,9 @@ export class SQLiteDB {
* @param {object} [conditions] The conditions to build the where clause. Must not contain numeric indexes. * @param {object} [conditions] The conditions to build the where clause. Must not contain numeric indexes.
* @return {Promise<any>} Promise resolved with the field's value. * @return {Promise<any>} Promise resolved with the field's value.
*/ */
getField(table: string, field: string, conditions?: object) : Promise<any> { getField(table: string, field: string, conditions?: object): Promise<any> {
let selectAndParams = this.whereClause(conditions); const selectAndParams = this.whereClause(conditions);
return this.getFieldSelect(table, field, selectAndParams[0], selectAndParams[1]); return this.getFieldSelect(table, field, selectAndParams[0], selectAndParams[1]);
} }
@ -376,7 +385,7 @@ export class SQLiteDB {
* @param {any[]} [params] Array of sql parameters. * @param {any[]} [params] Array of sql parameters.
* @return {Promise<any>} Promise resolved with the field's value. * @return {Promise<any>} Promise resolved with the field's value.
*/ */
getFieldSelect(table: string, field: string, select='', params?: any[]) : Promise<any> { getFieldSelect(table: string, field: string, select: string = '', params?: any[]): Promise<any> {
if (select) { if (select) {
select = 'WHERE ' + select; select = 'WHERE ' + select;
} }
@ -391,7 +400,7 @@ export class SQLiteDB {
* @param {any[]} [params] An array of sql parameters. * @param {any[]} [params] An array of sql parameters.
* @return {Promise<any>} Promise resolved with the field's value. * @return {Promise<any>} Promise resolved with the field's value.
*/ */
getFieldSql(sql: string, params?: any[]) : Promise<any> { getFieldSql(sql: string, params?: any[]): Promise<any> {
return this.getRecordSql(sql, params).then((record) => { return this.getRecordSql(sql, params).then((record) => {
if (!record) { if (!record) {
return Promise.reject(null); return Promise.reject(null);
@ -411,7 +420,7 @@ export class SQLiteDB {
* meaning return empty. Other values will become part of the returned SQL fragment. * meaning return empty. Other values will become part of the returned SQL fragment.
* @return {any[]} A list containing the constructed sql fragment and an array of parameters. * @return {any[]} A list containing the constructed sql fragment and an array of parameters.
*/ */
getInOrEqual(items: any, equal=true, onEmptyItems?: any) : any[] { getInOrEqual(items: any, equal: boolean = true, onEmptyItems?: any): any[] {
let sql, let sql,
params; params;
@ -428,6 +437,7 @@ export class SQLiteDB {
if (Array.isArray(items) && !items.length) { if (Array.isArray(items) && !items.length) {
if (onEmptyItems === null) { // Special case, NULL value. if (onEmptyItems === null) { // Special case, NULL value.
sql = equal ? ' IS NULL' : ' IS NOT NULL'; sql = equal ? ' IS NULL' : ' IS NOT NULL';
return [sql, []]; return [sql, []];
} else { } else {
items = [onEmptyItems]; // Rest of cases, prepare items for processing. items = [onEmptyItems]; // Rest of cases, prepare items for processing.
@ -438,7 +448,7 @@ export class SQLiteDB {
sql = equal ? '= ?' : '<> ?'; sql = equal ? '= ?' : '<> ?';
params = Array.isArray(items) ? items : [items]; params = Array.isArray(items) ? items : [items];
} else { } else {
sql = (equal ? '' : 'NOT ') + 'IN (' + ',?'.repeat(items.length).substr(1) + ')'; sql = (equal ? '' : 'NOT ') + 'IN (' + ',?'.repeat(items.length).substr(1) + ')';
params = items; params = items;
} }
@ -450,7 +460,7 @@ export class SQLiteDB {
* *
* @return {string} Database name. * @return {string} Database name.
*/ */
getName() : string { getName(): string {
return this.name; return this.name;
} }
@ -462,8 +472,9 @@ export class SQLiteDB {
* @param {string} [fields='*'] A comma separated list of fields to return. * @param {string} [fields='*'] A comma separated list of fields to return.
* @return {Promise<any>} Promise resolved with the record, rejected if not found. * @return {Promise<any>} Promise resolved with the record, rejected if not found.
*/ */
getRecord(table: string, conditions?: object, fields='*') : Promise<any> { getRecord(table: string, conditions?: object, fields: string = '*'): Promise<any> {
let selectAndParams = this.whereClause(conditions); const selectAndParams = this.whereClause(conditions);
return this.getRecordSelect(table, selectAndParams[0], selectAndParams[1], fields); return this.getRecordSelect(table, selectAndParams[0], selectAndParams[1], fields);
} }
@ -476,7 +487,7 @@ export class SQLiteDB {
* @param {string} [fields='*'] A comma separated list of fields to return. * @param {string} [fields='*'] A comma separated list of fields to return.
* @return {Promise<any>} Promise resolved with the record, rejected if not found. * @return {Promise<any>} Promise resolved with the record, rejected if not found.
*/ */
getRecordSelect(table: string, select='', params=[], fields='*') : Promise<any> { getRecordSelect(table: string, select: string = '', params: any[] = [], fields: string = '*'): Promise<any> {
if (select) { if (select) {
select = ' WHERE ' + select; select = ' WHERE ' + select;
} }
@ -494,7 +505,7 @@ export class SQLiteDB {
* @param {any[]} [params] List of sql parameters * @param {any[]} [params] List of sql parameters
* @return {Promise<any>} Promise resolved with the records. * @return {Promise<any>} Promise resolved with the records.
*/ */
getRecordSql(sql: string, params?: any[]) : Promise<any> { getRecordSql(sql: string, params?: any[]): Promise<any> {
return this.getRecordsSql(sql, params, 0, 1).then((result) => { return this.getRecordsSql(sql, params, 0, 1).then((result) => {
if (!result || !result.length) { if (!result || !result.length) {
// Not found, reject. // Not found, reject.
@ -517,8 +528,10 @@ export class SQLiteDB {
* @param {number} [limitNum=0] Return a subset comprising this many records in total. * @param {number} [limitNum=0] Return a subset comprising this many records in total.
* @return {Promise<any>} Promise resolved with the records. * @return {Promise<any>} Promise resolved with the records.
*/ */
getRecords(table: string, conditions?: object, sort='', fields='*', limitFrom=0, limitNum=0) : Promise<any> { getRecords(table: string, conditions?: object, sort: string = '', fields: string = '*', limitFrom: number = 0,
let selectAndParams = this.whereClause(conditions); limitNum: number = 0): Promise<any> {
const selectAndParams = this.whereClause(conditions);
return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum); return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum);
} }
@ -534,8 +547,10 @@ export class SQLiteDB {
* @param {number} [limitNum=0] Return a subset comprising this many records in total. * @param {number} [limitNum=0] Return a subset comprising this many records in total.
* @return {Promise<any>} Promise resolved with the records. * @return {Promise<any>} Promise resolved with the records.
*/ */
getRecordsList(table: string, field: string, values: any[], sort='', fields='*', limitFrom=0, limitNum=0) : Promise<any> { getRecordsList(table: string, field: string, values: any[], sort: string = '', fields: string = '*', limitFrom: number = 0,
let selectAndParams = this.whereClauseList(field, values); limitNum: number = 0): Promise<any> {
const selectAndParams = this.whereClauseList(field, values);
return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum); return this.getRecordsSelect(table, selectAndParams[0], selectAndParams[1], sort, fields, limitFrom, limitNum);
} }
@ -551,7 +566,8 @@ export class SQLiteDB {
* @param {number} [limitNum=0] Return a subset comprising this many records in total. * @param {number} [limitNum=0] Return a subset comprising this many records in total.
* @return {Promise<any>} Promise resolved with the records. * @return {Promise<any>} Promise resolved with the records.
*/ */
getRecordsSelect(table: string, select='', params=[], sort='', fields='*', limitFrom=0, limitNum=0) : Promise<any> { getRecordsSelect(table: string, select: string = '', params: any[] = [], sort: string = '', fields: string = '*',
limitFrom: number = 0, limitNum: number = 0): Promise<any> {
if (select) { if (select) {
select = ' WHERE ' + select; select = ' WHERE ' + select;
} }
@ -559,7 +575,8 @@ export class SQLiteDB {
sort = ' ORDER BY ' + sort; sort = ' ORDER BY ' + sort;
} }
let sql = `SELECT ${fields} FROM ${table} ${select} ${sort}`; const sql = `SELECT ${fields} FROM ${table} ${select} ${sort}`;
return this.getRecordsSql(sql, params, limitFrom, limitNum); return this.getRecordsSql(sql, params, limitFrom, limitNum);
} }
@ -572,8 +589,8 @@ export class SQLiteDB {
* @param {number} [limitNum] Return a subset comprising this many records. * @param {number} [limitNum] Return a subset comprising this many records.
* @return {Promise<any>} Promise resolved with the records. * @return {Promise<any>} Promise resolved with the records.
*/ */
getRecordsSql(sql: string, params?: any[], limitFrom?: number, limitNum?: number) : Promise<any> { getRecordsSql(sql: string, params?: any[], limitFrom?: number, limitNum?: number): Promise<any> {
let limits = this.normaliseLimitFromNum(limitFrom, limitNum); const limits = this.normaliseLimitFromNum(limitFrom, limitNum);
if (limits[0] || limits[1]) { if (limits[0] || limits[1]) {
if (limits[1] < 1) { if (limits[1] < 1) {
@ -584,10 +601,11 @@ export class SQLiteDB {
return this.execute(sql, params).then((result) => { return this.execute(sql, params).then((result) => {
// Retrieve the records. // Retrieve the records.
let records = []; const records = [];
for (let i = 0; i < result.rows.length; i++) { for (let i = 0; i < result.rows.length; i++) {
records.push(result.rows.item(i)); records.push(result.rows.item(i));
} }
return records; return records;
}); });
} }
@ -599,23 +617,23 @@ export class SQLiteDB {
* @param {object} data A data object with values for one or more fields in the record. * @param {object} data A data object with values for one or more fields in the record.
* @return {any[]} Array with the SQL query and the params. * @return {any[]} Array with the SQL query and the params.
*/ */
protected getSqlInsertQuery(table: string, data: object) : any[] { protected getSqlInsertQuery(table: string, data: object): any[] {
this.formatDataToInsert(data); this.formatDataToInsert(data);
let keys = Object.keys(data), const keys = Object.keys(data),
fields = keys.join(','), fields = keys.join(','),
questionMarks = ',?'.repeat(keys.length).substr(1); questionMarks = ',?'.repeat(keys.length).substr(1);
return [ return [
`INSERT INTO ${table} (${fields}) VALUES (${questionMarks})`, `INSERT INTO ${table} (${fields}) VALUES (${questionMarks})`,
keys.map(key => data[key]) keys.map((key) => data[key])
]; ];
} }
/** /**
* Initialize the database. * Initialize the database.
*/ */
init() : void { init(): void {
this.promise = this.platform.ready().then(() => { this.promise = this.platform.ready().then(() => {
return this.sqlite.create({ return this.sqlite.create({
name: this.name, name: this.name,
@ -634,8 +652,8 @@ export class SQLiteDB {
* @param {object} conditions The conditions to check if the record already exists. * @param {object} conditions The conditions to check if the record already exists.
* @return {Promise<any>} Promise resolved with done. * @return {Promise<any>} Promise resolved with done.
*/ */
insertOrUpdateRecord(table: string, data: object, conditions: object) : Promise<any> { insertOrUpdateRecord(table: string, data: object, conditions: object): Promise<any> {
return this.getRecord(table, conditions || data).then(() => { return this.getRecord(table, conditions || data).then(() => {
// It exists, update it. // It exists, update it.
return this.updateRecords(table, data, conditions); return this.updateRecords(table, data, conditions);
}).catch(() => { }).catch(() => {
@ -651,8 +669,8 @@ export class SQLiteDB {
* @param {object} data A data object with values for one or more fields in the record. * @param {object} data A data object with values for one or more fields in the record.
* @return {Promise<number>} Promise resolved with new rowId. Please notice this rowId is internal from SQLite. * @return {Promise<number>} Promise resolved with new rowId. Please notice this rowId is internal from SQLite.
*/ */
insertRecord(table: string, data: object) : Promise<number> { insertRecord(table: string, data: object): Promise<number> {
let sqlAndParams = this.getSqlInsertQuery(table, data); const sqlAndParams = this.getSqlInsertQuery(table, data);
return this.execute(sqlAndParams[0], sqlAndParams[1]).then((result) => { return this.execute(sqlAndParams[0], sqlAndParams[1]).then((result) => {
return result.insertId; return result.insertId;
@ -666,12 +684,12 @@ export class SQLiteDB {
* @param {object[]} dataObjects List of objects to be inserted. * @param {object[]} dataObjects List of objects to be inserted.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
insertRecords(table: string, dataObjects: object[]) : Promise<any> { insertRecords(table: string, dataObjects: object[]): Promise<any> {
if (!Array.isArray(dataObjects)) { if (!Array.isArray(dataObjects)) {
return Promise.reject(null); return Promise.reject(null);
} }
let statements = []; const statements = [];
dataObjects.forEach((dataObject) => { dataObjects.forEach((dataObject) => {
statements.push(this.getSqlInsertQuery(table, dataObject)); statements.push(this.getSqlInsertQuery(table, dataObject));
@ -689,7 +707,7 @@ export class SQLiteDB {
* @param {any} limitNum How many results to return. * @param {any} limitNum How many results to return.
* @return {number[]} Normalised limit params in array: [limitFrom, limitNum]. * @return {number[]} Normalised limit params in array: [limitFrom, limitNum].
*/ */
normaliseLimitFromNum(limitFrom: any, limitNum: any) : number[] { normaliseLimitFromNum(limitFrom: any, limitNum: any): number[] {
// We explicilty treat these cases as 0. // We explicilty treat these cases as 0.
if (typeof limitFrom == 'undefined' || limitFrom === null || limitFrom === '' || limitFrom === -1) { if (typeof limitFrom == 'undefined' || limitFrom === null || limitFrom === '' || limitFrom === -1) {
limitFrom = 0; limitFrom = 0;
@ -699,17 +717,19 @@ export class SQLiteDB {
} }
limitFrom = parseInt(limitFrom, 10); limitFrom = parseInt(limitFrom, 10);
limitNum = parseInt(limitNum, 10); limitNum = parseInt(limitNum, 10);
limitFrom = Math.max(0, limitFrom); limitFrom = Math.max(0, limitFrom);
limitNum = Math.max(0, limitNum); limitNum = Math.max(0, limitNum);
return [limitFrom, limitNum]; return [limitFrom, limitNum];
} }
/** /**
* Open the database. Only needed if it was closed before, a database is automatically opened when created. * Open the database. Only needed if it was closed before, a database is automatically opened when created.
*
* @return {Promise<void>} Promise resolved when open.
*/ */
open() { open(): Promise<any> {
return this.ready().then(() => { return this.ready().then(() => {
return this.db.open(); return this.db.open();
}); });
@ -720,7 +740,7 @@ export class SQLiteDB {
* *
* @return {Promise<void>} Promise resolved when ready. * @return {Promise<void>} Promise resolved when ready.
*/ */
ready() : Promise<void> { ready(): Promise<void> {
return this.promise; return this.promise;
} }
@ -731,7 +751,7 @@ export class SQLiteDB {
* @param {object} [conditions] The conditions to build the where clause. Must not contain numeric indexes. * @param {object} [conditions] The conditions to build the where clause. Must not contain numeric indexes.
* @return {Promise<void>} Promise resolved if exists, rejected otherwise. * @return {Promise<void>} Promise resolved if exists, rejected otherwise.
*/ */
recordExists(table: string, conditions?: object) : Promise<void> { recordExists(table: string, conditions?: object): Promise<void> {
return this.getRecord(table, conditions).then((record) => { return this.getRecord(table, conditions).then((record) => {
if (!record) { if (!record) {
return Promise.reject(null); return Promise.reject(null);
@ -747,7 +767,7 @@ export class SQLiteDB {
* @param {any[]} [params] An array of sql parameters. * @param {any[]} [params] An array of sql parameters.
* @return {Promise<any>} Promise resolved if exists, rejected otherwise. * @return {Promise<any>} Promise resolved if exists, rejected otherwise.
*/ */
recordExistsSelect(table: string, select='', params=[]) : Promise<any> { recordExistsSelect(table: string, select: string = '', params: any[] = []): Promise<any> {
return this.getRecordSelect(table, select, params).then((record) => { return this.getRecordSelect(table, select, params).then((record) => {
if (!record) { if (!record) {
return Promise.reject(null); return Promise.reject(null);
@ -762,7 +782,7 @@ export class SQLiteDB {
* @param {any[]} [params] An array of sql parameters. * @param {any[]} [params] An array of sql parameters.
* @return {Promise<any>} Promise resolved if exists, rejected otherwise. * @return {Promise<any>} Promise resolved if exists, rejected otherwise.
*/ */
recordExistsSql(sql: string, params?: any[]) : Promise<any> { recordExistsSql(sql: string, params?: any[]): Promise<any> {
return this.getRecordSql(sql, params).then((record) => { return this.getRecordSql(sql, params).then((record) => {
if (!record) { if (!record) {
return Promise.reject(null); return Promise.reject(null);
@ -778,27 +798,27 @@ export class SQLiteDB {
* @param {any} [conditions] The conditions to build the where clause. Must not contain numeric indexes. * @param {any} [conditions] The conditions to build the where clause. Must not contain numeric indexes.
* @return {Promise<any>} Promise resolved when updated. * @return {Promise<any>} Promise resolved when updated.
*/ */
updateRecords(table: string, data: any, conditions?: any) : Promise<any> { updateRecords(table: string, data: any, conditions?: any): Promise<any> {
if (!data || !Object.keys(data).length) { if (!data || !Object.keys(data).length) {
// No fields to update, consider it's done. // No fields to update, consider it's done.
return Promise.resolve(); return Promise.resolve();
} }
let whereAndParams = this.whereClause(conditions), const whereAndParams = this.whereClause(conditions),
sets = [], sets = [];
sql, let sql,
params; params;
this.formatDataToInsert(data); this.formatDataToInsert(data);
for (let key in data) { for (const key in data) {
sets.push(`${key} = ?`); sets.push(`${key} = ?`);
} }
sql = `UPDATE ${table} SET ${sets.join(', ')} WHERE ${whereAndParams[0]}`; sql = `UPDATE ${table} SET ${sets.join(', ')} WHERE ${whereAndParams[0]}`;
// Create the list of params using the "data" object and the params for the where clause. // Create the list of params using the "data" object and the params for the where clause.
params = Object.keys(data).map(key => data[key]).concat(whereAndParams[1]); params = Object.keys(data).map((key) => data[key]).concat(whereAndParams[1]);
return this.execute(sql, params); return this.execute(sql, params);
} }
@ -812,17 +832,17 @@ export class SQLiteDB {
* @param {any[]} [whereParams] Params for the where clause. * @param {any[]} [whereParams] Params for the where clause.
* @return {Promise<any>} Promise resolved when updated. * @return {Promise<any>} Promise resolved when updated.
*/ */
updateRecordsWhere(table: string, data: any, where?: string, whereParams?: any[]) : Promise<any> { updateRecordsWhere(table: string, data: any, where?: string, whereParams?: any[]): Promise<any> {
if (!data || !Object.keys(data).length) { if (!data || !Object.keys(data).length) {
// No fields to update, consider it's done. // No fields to update, consider it's done.
return Promise.resolve(); return Promise.resolve();
} }
let sets = [], const sets = [];
sql, let sql,
params; params;
for (let key in data) { for (const key in data) {
sets.push(`${key} = ?`); sets.push(`${key} = ?`);
} }
@ -832,7 +852,7 @@ export class SQLiteDB {
} }
// Create the list of params using the "data" object and the params for the where clause. // Create the list of params using the "data" object and the params for the where clause.
params = Object.keys(data).map(key => data[key]); params = Object.keys(data).map((key) => data[key]);
if (where && whereParams) { if (where && whereParams) {
params = params.concat(whereParams[1]); params = params.concat(whereParams[1]);
} }
@ -846,16 +866,16 @@ export class SQLiteDB {
* @param {object} [conditions] The conditions to build the where clause. Must not contain numeric indexes. * @param {object} [conditions] The conditions to build the where clause. Must not contain numeric indexes.
* @return {any[]} An array list containing sql 'where' part and 'params'. * @return {any[]} An array list containing sql 'where' part and 'params'.
*/ */
whereClause(conditions={}) : any[] { whereClause(conditions: any = {}): any[] {
if (!conditions || !Object.keys(conditions).length) { if (!conditions || !Object.keys(conditions).length) {
return ['', []]; return ['', []];
} }
let where = [], const where = [],
params = []; params = [];
for (let key in conditions) { for (const key in conditions) {
let value = conditions[key]; const value = conditions[key];
if (typeof value == 'undefined' || value === null) { if (typeof value == 'undefined' || value === null) {
where.push(key + ' IS NULL'); where.push(key + ' IS NULL');
@ -875,13 +895,13 @@ export class SQLiteDB {
* @param {any[]} values The values field might take. * @param {any[]} values The values field might take.
* @return {any[]} An array containing sql 'where' part and 'params'. * @return {any[]} An array containing sql 'where' part and 'params'.
*/ */
whereClauseList(field: string, values: any[]) : any[] { whereClauseList(field: string, values: any[]): any[] {
if (!values || !values.length) { if (!values || !values.length) {
return ["1 = 2", []]; // Fake condition, won't return rows ever. return ['1 = 2', []]; // Fake condition, won't return rows ever.
} }
let params = [], const params = [];
select = ''; let select = '';
values.forEach((value) => { values.forEach((value) => {
if (typeof value == 'boolean') { if (typeof value == 'boolean') {
@ -903,7 +923,7 @@ export class SQLiteDB {
if (params.length == 1) { if (params.length == 1) {
select = select + field + ' = ?'; select = select + field + ' = ?';
} else { } else {
let questionMarks = ',?'.repeat(params.length).substr(1); const questionMarks = ',?'.repeat(params.length).substr(1);
select = select + field + ' IN (' + questionMarks + ')'; select = select + field + ' IN (' + questionMarks + ')';
} }
} }

View File

@ -32,12 +32,12 @@ import { Component, Input, OnChanges, OnDestroy, Output, EventEmitter, SimpleCha
}) })
export class CoreChronoComponent implements OnChanges, OnDestroy { export class CoreChronoComponent implements OnChanges, OnDestroy {
@Input() running: boolean; // Set it to true to start the chrono. Set it to false to stop it. @Input() running: boolean; // Set it to true to start the chrono. Set it to false to stop it.
@Input() startTime?: number = 0; // Number of milliseconds to put in the chrono before starting. @Input() startTime? = 0; // Number of milliseconds to put in the chrono before starting.
@Input() endTime?: number; // Number of milliseconds to stop the chrono. @Input() endTime?: number; // Number of milliseconds to stop the chrono.
@Input() reset?: boolean; // Set it to true to reset the chrono. @Input() reset?: boolean; // Set it to true to reset the chrono.
@Output() onEnd?: EventEmitter<void>; // Will emit an event when the endTime is reached. @Output() onEnd?: EventEmitter<void>; // Will emit an event when the endTime is reached.
time: number = 0; time = 0;
protected interval; protected interval;
constructor(private cdr: ChangeDetectorRef) { constructor(private cdr: ChangeDetectorRef) {
@ -47,14 +47,14 @@ export class CoreChronoComponent implements OnChanges, OnDestroy {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.time = this.startTime || 0; this.time = this.startTime || 0;
} }
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnChanges(changes: {[name: string]: SimpleChange}) { ngOnChanges(changes: { [name: string]: SimpleChange }): void {
if (changes && changes.running) { if (changes && changes.running) {
if (changes.running.currentValue) { if (changes.running.currentValue) {
this.start(); this.start();
@ -70,7 +70,7 @@ export class CoreChronoComponent implements OnChanges, OnDestroy {
/** /**
* Reset the chrono, stopping it and setting it to startTime. * Reset the chrono, stopping it and setting it to startTime.
*/ */
protected resetChrono() : void { protected resetChrono(): void {
this.stop(); this.stop();
this.time = this.startTime || 0; this.time = this.startTime || 0;
} }
@ -78,7 +78,7 @@ export class CoreChronoComponent implements OnChanges, OnDestroy {
/** /**
* Start the chrono if it isn't running. * Start the chrono if it isn't running.
*/ */
protected start() : void { protected start(): void {
if (this.interval) { if (this.interval) {
// Already setup. // Already setup.
return; return;
@ -105,12 +105,12 @@ export class CoreChronoComponent implements OnChanges, OnDestroy {
/** /**
* Stop the chrono, leaving the same time it has. * Stop the chrono, leaving the same time it has.
*/ */
protected stop() : void { protected stop(): void {
clearInterval(this.interval); clearInterval(this.interval);
delete this.interval; delete this.interval;
} }
ngOnDestroy() { ngOnDestroy(): void {
this.stop(); this.stop();
} }
} }

View File

@ -15,7 +15,6 @@
import { Component, Input, Output, OnInit, OnDestroy, EventEmitter, OnChanges, SimpleChange } from '@angular/core'; import { Component, Input, Output, OnInit, OnDestroy, EventEmitter, OnChanges, SimpleChange } from '@angular/core';
import { CoreContextMenuComponent } from './context-menu'; import { CoreContextMenuComponent } from './context-menu';
/** /**
* This directive adds a item to the Context Menu popover. * This directive adds a item to the Context Menu popover.
* *
@ -36,15 +35,16 @@ import { CoreContextMenuComponent } from './context-menu';
export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChanges { export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChanges {
@Input() content: string; // Content of the item. @Input() content: string; // Content of the item.
@Input() iconDescription?: string; // Name of the icon to be shown on the left side of the item. @Input() iconDescription?: string; // Name of the icon to be shown on the left side of the item.
@Input() iconAction?: string; // Name of the icon to be shown on the right side of the item. It represents the action to do on @Input() iconAction?: string; // Name of the icon to show on the right side of the item. Represents the action to do on click.
// click. If is "spinner" an spinner will be shown. If no icon or spinner is selected, no action // If is "spinner" an spinner will be shown.
// or link will work. If href but no iconAction is provided arrow-right will be used. // If no icon or spinner is selected, no action or link will work.
// If href but no iconAction is provided arrow-right will be used.
@Input() ariaDescription?: string; // Aria label to add to iconDescription. @Input() ariaDescription?: string; // Aria label to add to iconDescription.
@Input() ariaAction?: string; // Aria label to add to iconAction. If not set, it will be equal to content. @Input() ariaAction?: string; // Aria label to add to iconAction. If not set, it will be equal to content.
@Input() href?: string; // Link to go if no action provided. @Input() href?: string; // Link to go if no action provided.
@Input() captureLink?: boolean|string; // Whether the link needs to be captured by the app. @Input() captureLink?: boolean | string; // Whether the link needs to be captured by the app.
@Input() autoLogin?: string; // Whether the link needs to be opened using auto-login. @Input() autoLogin?: string; // Whether the link needs to be opened using auto-login.
@Input() closeOnClick?: boolean|string = true; // Whether to close the popover when the item is clicked. @Input() closeOnClick?: boolean | string = true; // Whether to close the popover when the item is clicked.
@Input() priority?: number; // Used to sort items. The highest priority, the highest position. @Input() priority?: number; // Used to sort items. The highest priority, the highest position.
@Input() badge?: string; // A badge to show in the item. @Input() badge?: string; // A badge to show in the item.
@Input() badgeClass?: number; // A class to set in the badge. @Input() badgeClass?: number; // A class to set in the badge.
@ -61,7 +61,7 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
// Initialize values. // Initialize values.
this.priority = this.priority || 1; this.priority = this.priority || 1;
this.closeOnClick = this.getBooleanValue(this.closeOnClick, true); this.closeOnClick = this.getBooleanValue(this.closeOnClick, true);
@ -88,17 +88,18 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange
* @param {boolean} defaultValue Value to use if undefined. * @param {boolean} defaultValue Value to use if undefined.
* @return {boolean} Boolean value. * @return {boolean} Boolean value.
*/ */
protected getBooleanValue(value: any, defaultValue: boolean) : boolean { protected getBooleanValue(value: any, defaultValue: boolean): boolean {
if (typeof value == 'undefined') { if (typeof value == 'undefined') {
return defaultValue; return defaultValue;
} }
return value && value !== 'false'; return value && value !== 'false';
} }
/** /**
* Component destroyed. * Component destroyed.
*/ */
ngOnDestroy() { ngOnDestroy(): void {
this.destroyed = true; this.destroyed = true;
this.ctxtMenu.removeItem(this); this.ctxtMenu.removeItem(this);
} }
@ -106,7 +107,7 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange
/** /**
* Detect changes on input properties. * Detect changes on input properties.
*/ */
ngOnChanges(changes: {[name: string]: SimpleChange}) { ngOnChanges(changes: { [name: string]: SimpleChange }): void {
if (changes.hidden && !changes.hidden.firstChange) { if (changes.hidden && !changes.hidden.firstChange) {
this.ctxtMenu.itemsChanged(); this.ctxtMenu.itemsChanged();
} }

View File

@ -38,7 +38,7 @@ export class CoreContextMenuPopoverComponent {
/** /**
* Close the popover. * Close the popover.
*/ */
closeMenu() : void { closeMenu(): void {
this.viewCtrl.dismiss(); this.viewCtrl.dismiss();
} }
@ -49,13 +49,14 @@ export class CoreContextMenuPopoverComponent {
* @param {CoreContextMenuItemComponent} item Item clicked. * @param {CoreContextMenuItemComponent} item Item clicked.
* @return {boolean} Return true if success, false if error. * @return {boolean} Return true if success, false if error.
*/ */
itemClicked(event: Event, item: CoreContextMenuItemComponent) : boolean { itemClicked(event: Event, item: CoreContextMenuItemComponent): boolean {
if (item.action.observers.length > 0) { if (item.action.observers.length > 0) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
if (!item.iconAction) { if (!item.iconAction) {
this.logger.warn('Items with action must have an icon action to work', item); this.logger.warn('Items with action must have an icon action to work', item);
return false; return false;
} else if (item.iconAction == 'spinner') { } else if (item.iconAction == 'spinner') {
return false; return false;

View File

@ -43,15 +43,15 @@ export class CoreContextMenuComponent implements OnInit {
this.hideMenu = !this.items.some((item) => { this.hideMenu = !this.items.some((item) => {
return !item.hidden; return !item.hidden;
}); });
}) });
} }
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.icon = this.icon || 'more'; this.icon = this.icon || 'more';
this.ariaLabel = this.title || this.translate.instant('core.info'); this.ariaLabel = this.title || this.translate.instant('core.info');
} }
/** /**
@ -59,7 +59,7 @@ export class CoreContextMenuComponent implements OnInit {
* *
* @param {CoreContextMenuItemComponent} item The item to add. * @param {CoreContextMenuItemComponent} item The item to add.
*/ */
addItem(item: CoreContextMenuItemComponent) : void { addItem(item: CoreContextMenuItemComponent): void {
this.items.push(item); this.items.push(item);
this.itemsChanged(); this.itemsChanged();
} }
@ -67,7 +67,7 @@ export class CoreContextMenuComponent implements OnInit {
/** /**
* Function called when the items change. * Function called when the items change.
*/ */
itemsChanged() { itemsChanged(): void {
this.itemsChangedStream.next(); this.itemsChangedStream.next();
} }
@ -76,8 +76,8 @@ export class CoreContextMenuComponent implements OnInit {
* *
* @param {CoreContextMenuItemComponent} item The item to remove. * @param {CoreContextMenuItemComponent} item The item to remove.
*/ */
removeItem(item: CoreContextMenuItemComponent) : void { removeItem(item: CoreContextMenuItemComponent): void {
let index = this.items.indexOf(item); const index = this.items.indexOf(item);
if (index >= 0) { if (index >= 0) {
this.items.splice(index, 1); this.items.splice(index, 1);
} }
@ -89,8 +89,8 @@ export class CoreContextMenuComponent implements OnInit {
* *
* @param {MouseEvent} event Event. * @param {MouseEvent} event Event.
*/ */
showContextMenu(event: MouseEvent) : void { showContextMenu(event: MouseEvent): void {
let popover = this.popoverCtrl.create(CoreContextMenuPopoverComponent, {title: this.title, items: this.items}); const popover = this.popoverCtrl.create(CoreContextMenuPopoverComponent, { title: this.title, items: this.items });
popover.present({ popover.present({
ev: event ev: event
}); });

View File

@ -38,8 +38,9 @@ export class CoreCoursePickerMenuPopoverComponent {
* @param {any} course Course object clicked. * @param {any} course Course object clicked.
* @return {boolean} Return true if success, false if error. * @return {boolean} Return true if success, false if error.
*/ */
coursePicked(event: Event, course: any) : boolean { coursePicked(event: Event, course: any): boolean {
this.viewCtrl.dismiss(course); this.viewCtrl.dismiss(course);
return true; return true;
} }
} }

View File

@ -29,5 +29,7 @@ export class CoreEmptyBoxComponent {
@Input() icon?: string; // Name of the icon to use. @Input() icon?: string; // Name of the icon to use.
@Input() image?: string; // Image source. If an icon is provided, image won't be used. @Input() image?: string; // Image source. If an icon is provided, image won't be used.
constructor() {} constructor() {
// Nothing to do.
}
} }

View File

@ -35,12 +35,12 @@ import { CoreConstants } from '../../core/constants';
export class CoreFileComponent implements OnInit, OnDestroy { export class CoreFileComponent implements OnInit, OnDestroy {
@Input() file: any; // The file. Must have a property 'filename' and a 'fileurl' or 'url' @Input() file: any; // The file. Must have a property 'filename' and a 'fileurl' or 'url'
@Input() component?: string; // Component the file belongs to. @Input() component?: string; // Component the file belongs to.
@Input() componentId?: string|number; // Component ID. @Input() componentId?: string | number; // Component ID.
@Input() timemodified?: number; // If set, the value will be used to check if the file is outdated. @Input() timemodified?: number; // If set, the value will be used to check if the file is outdated.
@Input() canDelete?: boolean|string; // Whether file can be deleted. @Input() canDelete?: boolean | string; // Whether file can be deleted.
@Input() alwaysDownload?: boolean|string; // Whether it should always display the refresh button when the file is downloaded. @Input() alwaysDownload?: boolean | string; // Whether it should always display the refresh button when the file is downloaded.
// Use it for files that you cannot determine if they're outdated or not. // Use it for files that you cannot determine if they're outdated or not.
@Input() canDownload?: boolean|string = true; // Whether file can be downloaded. @Input() canDownload?: boolean | string = true; // Whether file can be downloaded.
@Output() onDelete?: EventEmitter<string>; // Will notify when the delete button is clicked. @Output() onDelete?: EventEmitter<string>; // Will notify when the delete button is clicked.
isDownloaded: boolean; isDownloaded: boolean;
@ -64,7 +64,7 @@ export class CoreFileComponent implements OnInit, OnDestroy {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.canDelete = this.utils.isTrueOrOne(this.canDelete); this.canDelete = this.utils.isTrueOrOne(this.canDelete);
this.alwaysDownload = this.utils.isTrueOrOne(this.alwaysDownload); this.alwaysDownload = this.utils.isTrueOrOne(this.alwaysDownload);
this.canDownload = this.utils.isTrueOrOne(this.canDownload); this.canDownload = this.utils.isTrueOrOne(this.canDownload);
@ -98,14 +98,14 @@ export class CoreFileComponent implements OnInit, OnDestroy {
* *
* @return {Promise<void>} Promise resolved when state has been calculated. * @return {Promise<void>} Promise resolved when state has been calculated.
*/ */
protected calculateState() : Promise<void> { protected calculateState(): Promise<void> {
return this.filepoolProvider.getFileStateByUrl(this.siteId, this.fileUrl, this.timemodified).then((state) => { return this.filepoolProvider.getFileStateByUrl(this.siteId, this.fileUrl, this.timemodified).then((state) => {
let canDownload = this.sitesProvider.getCurrentSite().canDownloadFiles(); const canDownload = this.sitesProvider.getCurrentSite().canDownloadFiles();
this.isDownloaded = state === CoreConstants.DOWNLOADED || state === CoreConstants.OUTDATED; this.isDownloaded = state === CoreConstants.DOWNLOADED || state === CoreConstants.OUTDATED;
this.isDownloading = canDownload && state === CoreConstants.DOWNLOADING; this.isDownloading = canDownload && state === CoreConstants.DOWNLOADING;
this.showDownload = canDownload && (state === CoreConstants.NOT_DOWNLOADED || state === CoreConstants.OUTDATED || this.showDownload = canDownload && (state === CoreConstants.NOT_DOWNLOADED || state === CoreConstants.OUTDATED ||
(this.alwaysDownload && state === CoreConstants.DOWNLOADED)); (this.alwaysDownload && state === CoreConstants.DOWNLOADED));
}); });
} }
@ -114,25 +114,27 @@ export class CoreFileComponent implements OnInit, OnDestroy {
* *
* @return {Promise<string>} Promise resolved when file is downloaded. * @return {Promise<string>} Promise resolved when file is downloaded.
*/ */
protected downloadFile() : Promise<string> { protected downloadFile(): Promise<string> {
if (!this.sitesProvider.getCurrentSite().canDownloadFiles()) { if (!this.sitesProvider.getCurrentSite().canDownloadFiles()) {
this.domUtils.showErrorModal('core.cannotdownloadfiles', true); this.domUtils.showErrorModal('core.cannotdownloadfiles', true);
return Promise.reject(null); return Promise.reject(null);
} }
this.isDownloading = true; this.isDownloading = true;
return this.filepoolProvider.downloadUrl(this.siteId, this.fileUrl, false, this.component, this.componentId,
this.timemodified, undefined, undefined, this.file).catch(() => {
// Call calculateState to make sure we have the right state. return this.filepoolProvider.downloadUrl(this.siteId, this.fileUrl, false, this.component, this.componentId,
return this.calculateState().then(() => { this.timemodified, undefined, undefined, this.file).catch(() => {
if (this.isDownloaded) {
return this.filepoolProvider.getInternalUrlByUrl(this.siteId, this.fileUrl); // Call calculateState to make sure we have the right state.
} else { return this.calculateState().then(() => {
return Promise.reject(null); if (this.isDownloaded) {
} return this.filepoolProvider.getInternalUrlByUrl(this.siteId, this.fileUrl);
} else {
return Promise.reject(null);
}
});
}); });
});
} }
/** /**
@ -140,33 +142,35 @@ export class CoreFileComponent implements OnInit, OnDestroy {
* *
* @return {Promise<string>} Promise resolved when file is opened. * @return {Promise<string>} Promise resolved when file is opened.
*/ */
protected openFile() : Promise<any> { protected openFile(): Promise<any> {
let fixedUrl = this.sitesProvider.getCurrentSite().fixPluginfileURL(this.fileUrl), const fixedUrl = this.sitesProvider.getCurrentSite().fixPluginfileURL(this.fileUrl);
promise; let promise;
if (this.fileProvider.isAvailable()) { if (this.fileProvider.isAvailable()) {
promise = Promise.resolve().then(() => { promise = Promise.resolve().then(() => {
// The file system is available. // The file system is available.
let isWifi = !this.appProvider.isNetworkAccessLimited(), const isWifi = !this.appProvider.isNetworkAccessLimited(),
isOnline = this.appProvider.isOnline(); isOnline = this.appProvider.isOnline();
if (this.isDownloaded && !this.showDownload) { if (this.isDownloaded && !this.showDownload) {
// File is downloaded, get the local file URL. // File is downloaded, get the local file URL.
return this.filepoolProvider.getUrlByUrl(this.siteId, this.fileUrl, return this.filepoolProvider.getUrlByUrl(this.siteId, this.fileUrl,
this.component, this.componentId, this.timemodified, false, false, this.file); this.component, this.componentId, this.timemodified, false, false, this.file);
} else { } else {
if (!isOnline && !this.isDownloaded) { if (!isOnline && !this.isDownloaded) {
// Not downloaded and user is offline, reject. // Not downloaded and user is offline, reject.
return Promise.reject(this.translate.instant('core.networkerrormsg')); return Promise.reject(this.translate.instant('core.networkerrormsg'));
} }
let isDownloading = this.isDownloading; const isDownloading = this.isDownloading;
this.isDownloading = true; // This check could take a while, show spinner. this.isDownloading = true; // This check could take a while, show spinner.
return this.filepoolProvider.shouldDownloadBeforeOpen(fixedUrl, this.fileSize).then(() => { return this.filepoolProvider.shouldDownloadBeforeOpen(fixedUrl, this.fileSize).then(() => {
if (isDownloading) { if (isDownloading) {
// It's already downloading, stop. // It's already downloading, stop.
return; return;
} }
// Download and then return the local URL. // Download and then return the local URL.
return this.downloadFile(); return this.downloadFile();
}, () => { }, () => {
@ -181,7 +185,7 @@ export class CoreFileComponent implements OnInit, OnDestroy {
} else { } else {
// Outdated but offline, so we return the local URL. // Outdated but offline, so we return the local URL.
return this.filepoolProvider.getUrlByUrl(this.siteId, this.fileUrl, return this.filepoolProvider.getUrlByUrl(this.siteId, this.fileUrl,
this.component, this.componentId, this.timemodified, false, false, this.file); this.component, this.componentId, this.timemodified, false, false, this.file);
} }
}); });
} }
@ -231,7 +235,7 @@ export class CoreFileComponent implements OnInit, OnDestroy {
* @param {Event} e Click event. * @param {Event} e Click event.
* @param {boolean} openAfterDownload Whether the file should be opened after download. * @param {boolean} openAfterDownload Whether the file should be opened after download.
*/ */
download(e: Event, openAfterDownload: boolean) : void { download(e: Event, openAfterDownload: boolean): void {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -243,6 +247,7 @@ export class CoreFileComponent implements OnInit, OnDestroy {
if (!this.appProvider.isOnline() && (!openAfterDownload || (openAfterDownload && !this.isDownloaded))) { if (!this.appProvider.isOnline() && (!openAfterDownload || (openAfterDownload && !this.isDownloaded))) {
this.domUtils.showErrorModal('core.networkerrormsg', true); this.domUtils.showErrorModal('core.networkerrormsg', true);
return; return;
} }
@ -253,27 +258,27 @@ export class CoreFileComponent implements OnInit, OnDestroy {
}); });
} else { } else {
// File doesn't need to be opened (it's a prefetch). Show confirm modal if file size is defined and it's big. // File doesn't need to be opened (it's a prefetch). Show confirm modal if file size is defined and it's big.
promise = this.fileSize ? this.domUtils.confirmDownloadSize({size: this.fileSize, total: true}) : Promise.resolve(); promise = this.fileSize ? this.domUtils.confirmDownloadSize({ size: this.fileSize, total: true }) : Promise.resolve();
promise.then(() => { promise.then(() => {
// User confirmed, add the file to queue. // User confirmed, add the file to queue.
this.filepoolProvider.invalidateFileByUrl(this.siteId, this.fileUrl).finally(() => { this.filepoolProvider.invalidateFileByUrl(this.siteId, this.fileUrl).finally(() => {
this.isDownloading = true; this.isDownloading = true;
this.filepoolProvider.addToQueueByUrl(this.siteId, this.fileUrl, this.component, this.filepoolProvider.addToQueueByUrl(this.siteId, this.fileUrl, this.component,
this.componentId, this.timemodified, undefined, undefined, 0, this.file).catch((error) => { this.componentId, this.timemodified, undefined, undefined, 0, this.file).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true); this.domUtils.showErrorModalDefault(error, 'core.errordownloading', true);
this.calculateState(); this.calculateState();
}); });
}); });
}); });
} }
}; }
/** /**
* Delete the file. * Delete the file.
* *
* @param {Event} e Click event. * @param {Event} e Click event.
*/ */
deleteFile(e: Event) : void { deleteFile(e: Event): void {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -285,7 +290,7 @@ export class CoreFileComponent implements OnInit, OnDestroy {
/** /**
* Component destroyed. * Component destroyed.
*/ */
ngOnDestroy() { ngOnDestroy(): void {
this.observer && this.observer.off(); this.observer && this.observer.off();
} }
} }

View File

@ -51,8 +51,8 @@ export class CoreIframeComponent implements OnInit {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
let iframe: HTMLIFrameElement = this.iframe && this.iframe.nativeElement; const iframe: HTMLIFrameElement = this.iframe && this.iframe.nativeElement;
this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.src); this.safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.src);
this.iframeWidth = this.domUtils.formatPixelsSize(this.iframeWidth) || '100%'; this.iframeWidth = this.domUtils.formatPixelsSize(this.iframeWidth) || '100%';
@ -85,7 +85,7 @@ export class CoreIframeComponent implements OnInit {
* @param {any} element Element to treat. * @param {any} element Element to treat.
* @return {{ window: Window, document: Document }} Window and Document. * @return {{ window: Window, document: Document }} Window and Document.
*/ */
protected getContentWindowAndDocument(element: any) : { window: Window, document: Document } { protected getContentWindowAndDocument(element: any): { window: Window, document: Document } {
let contentWindow: Window = element.contentWindow, let contentWindow: Window = element.contentWindow,
contentDocument: Document = element.contentDocument || (contentWindow && contentWindow.document); contentDocument: Document = element.contentDocument || (contentWindow && contentWindow.document);
@ -106,7 +106,7 @@ export class CoreIframeComponent implements OnInit {
} }
} }
return {window: contentWindow, document: contentDocument}; return { window: contentWindow, document: contentDocument };
} }
/** /**
@ -115,7 +115,7 @@ export class CoreIframeComponent implements OnInit {
* *
* @param {any} element Element to treat. * @param {any} element Element to treat.
*/ */
protected treatFrame(element: any) : void { protected treatFrame(element: any): void {
if (element) { if (element) {
let winAndDoc = this.getContentWindowAndDocument(element); let winAndDoc = this.getContentWindowAndDocument(element);
// Redefine window.open in this element and sub frames, it might have been loaded already. // Redefine window.open in this element and sub frames, it might have been loaded already.
@ -139,10 +139,10 @@ export class CoreIframeComponent implements OnInit {
* @param {Window} contentWindow The window of the element contents. * @param {Window} contentWindow The window of the element contents.
* @param {Document} contentDocument The document of the element contents. * @param {Document} contentDocument The document of the element contents.
*/ */
protected redefineWindowOpen(element: any, contentWindow: Window, contentDocument: Document) : void { protected redefineWindowOpen(element: any, contentWindow: Window, contentDocument: Document): void {
if (contentWindow) { if (contentWindow) {
// Intercept window.open. // Intercept window.open.
contentWindow.open = (url: string) : Window => { contentWindow.open = (url: string): Window => {
const scheme = this.urlUtils.getUrlScheme(url); const scheme = this.urlUtils.getUrlScheme(url);
if (!scheme) { if (!scheme) {
// It's a relative URL, use the frame src to create the full URL. // It's a relative URL, use the frame src to create the full URL.
@ -153,10 +153,12 @@ export class CoreIframeComponent implements OnInit {
url = this.textUtils.concatenatePaths(dirAndFile.directory, url); url = this.textUtils.concatenatePaths(dirAndFile.directory, url);
} else { } else {
this.logger.warn('Cannot get iframe dir path to open relative url', url, element); this.logger.warn('Cannot get iframe dir path to open relative url', url, element);
return new Window(); // Return new Window object. return new Window(); // Return new Window object.
} }
} else { } else {
this.logger.warn('Cannot get iframe src to open relative url', url, element); this.logger.warn('Cannot get iframe src to open relative url', url, element);
return new Window(); // Return new Window object. return new Window(); // Return new Window object.
} }
} }
@ -198,7 +200,7 @@ export class CoreIframeComponent implements OnInit {
* @param {any} element Element to treat. * @param {any} element Element to treat.
* @param {Document} contentDocument The document of the element contents. * @param {Document} contentDocument The document of the element contents.
*/ */
protected treatLinks(element: any, contentDocument: Document) : void { protected treatLinks(element: any, contentDocument: Document): void {
if (!contentDocument) { if (!contentDocument) {
return; return;
} }

View File

@ -47,12 +47,12 @@ export class CoreInputErrorsComponent implements OnInit {
@Input() errorMessages?: any; @Input() errorMessages?: any;
errorKeys: any[]; errorKeys: any[];
constructor(private translate: TranslateService) {} constructor(private translate: TranslateService) { }
/** /**
* Component is being initialized. * Component is being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.initErrorMessages(); this.initErrorMessages();
this.errorKeys = Object.keys(this.errorMessages); this.errorKeys = Object.keys(this.errorMessages);
@ -61,11 +61,11 @@ export class CoreInputErrorsComponent implements OnInit {
/** /**
* Initialize some common errors if they aren't set. * Initialize some common errors if they aren't set.
*/ */
protected initErrorMessages() { protected initErrorMessages(): void {
this.errorMessages = this.errorMessages || {}; this.errorMessages = this.errorMessages || {};
this.errorMessages.required = this.errorMessages.required || this.translate.instant('core.required'); this.errorMessages.required = this.errorMessages.required || this.translate.instant('core.required');
this.errorMessages.email = this.errorMessages.email || this.translate.instant('core.login.invalidemail'); this.errorMessages.email = this.errorMessages.email || this.translate.instant('core.login.invalidemail');
this.errorMessages.date = this.errorMessages.date || this.translate.instant('core.login.invaliddate'); this.errorMessages.date = this.errorMessages.date || this.translate.instant('core.login.invaliddate');
this.errorMessages.datetime = this.errorMessages.datetime || this.translate.instant('core.login.invaliddate'); this.errorMessages.datetime = this.errorMessages.datetime || this.translate.instant('core.login.invaliddate');
this.errorMessages.datetimelocal = this.errorMessages.datetimelocal || this.translate.instant('core.login.invaliddate'); this.errorMessages.datetimelocal = this.errorMessages.datetimelocal || this.translate.instant('core.login.invaliddate');
@ -73,24 +73,6 @@ export class CoreInputErrorsComponent implements OnInit {
this.errorMessages.url = this.errorMessages.url || this.translate.instant('core.login.invalidurl'); this.errorMessages.url = this.errorMessages.url || this.translate.instant('core.login.invalidurl');
// @todo: Check how to handle min/max errors once we have a test case to use. Also, review previous errors. // @todo: Check how to handle min/max errors once we have a test case to use. Also, review previous errors.
// ['min', 'max'].forEach((type) => {
// // Initialize min/max errors if needed.
// if (!this.errorMessages[type]) {
// if (input && typeof input[type] != 'undefined' && input[type] !== '') {
// var value = input[type];
// if (input.type == 'date' || input.type == 'datetime' || input.type == 'datetime-local') {
// var date = moment(value);
// if (date.isValid()) {
// value = moment(value).format($translate.instant('core.dfdaymonthyear'));
// }
// }
// scope.errorMessages[type] = $translate.instant('core.login.invalidvalue' + type, {$a: value});
// } else {
// scope.errorMessages[type] = $translate.instant('core.login.profileinvaliddata');
// }
// }
// });
} }
} }

View File

@ -43,12 +43,12 @@ export class CoreLoadingComponent implements OnInit {
@Input() hideUntil: boolean; // Determine when should the contents be shown. @Input() hideUntil: boolean; // Determine when should the contents be shown.
@Input() message?: string; // Message to show while loading. @Input() message?: string; // Message to show while loading.
constructor(private translate: TranslateService) {} constructor(private translate: TranslateService) { }
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
if (!this.message) { if (!this.message) {
// Default loading message. // Default loading message.
this.message = this.translate.instant('core.loading'); this.message = this.translate.instant('core.loading');

View File

@ -33,8 +33,8 @@ import * as moment from 'moment';
}) })
export class CoreLocalFileComponent implements OnInit { export class CoreLocalFileComponent implements OnInit {
@Input() file: any; // A fileEntry retrieved using CoreFileProvider.getFile or similar. @Input() file: any; // A fileEntry retrieved using CoreFileProvider.getFile or similar.
@Input() manage?: boolean|string; // Whether the user can manage the file (edit and delete). @Input() manage?: boolean | string; // Whether the user can manage the file (edit and delete).
@Input() overrideClick?: boolean|string; // Whether the default item click should be overridden. @Input() overrideClick?: boolean | string; // Whether the default item click should be overridden.
@Output() onDelete?: EventEmitter<void>; // Will notify when the file is deleted. @Output() onDelete?: EventEmitter<void>; // Will notify when the file is deleted.
@Output() onRename?: EventEmitter<any>; // Will notify when the file is renamed. Receives the FileEntry as the param. @Output() onRename?: EventEmitter<any>; // Will notify when the file is renamed. Receives the FileEntry as the param.
@Output() onClick?: EventEmitter<void>; // Will notify when the file is clicked. Only if overrideClick is true. @Output() onClick?: EventEmitter<void>; // Will notify when the file is clicked. Only if overrideClick is true.
@ -44,7 +44,7 @@ export class CoreLocalFileComponent implements OnInit {
fileExtension: string; fileExtension: string;
size: string; size: string;
timemodified: string; timemodified: string;
newFileName: string = ''; newFileName = '';
editMode: boolean; editMode: boolean;
relativePath: string; relativePath: string;
@ -59,7 +59,7 @@ export class CoreLocalFileComponent implements OnInit {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.manage = this.utils.isTrueOrOne(this.manage); this.manage = this.utils.isTrueOrOne(this.manage);
// Let's calculate the relative path for the file. // Let's calculate the relative path for the file.
@ -83,11 +83,8 @@ export class CoreLocalFileComponent implements OnInit {
/** /**
* Load the basic data for the file. * Load the basic data for the file.
*
* @param {[type]} scope [description]
* @param {[type]} file [description]
*/ */
protected loadFileBasicData() { protected loadFileBasicData(): void {
this.fileName = this.file.name; this.fileName = this.file.name;
this.fileIcon = this.mimeUtils.getFileIcon(this.file.name); this.fileIcon = this.mimeUtils.getFileIcon(this.file.name);
this.fileExtension = this.mimeUtils.getFileExtension(this.file.name); this.fileExtension = this.mimeUtils.getFileExtension(this.file.name);
@ -98,7 +95,7 @@ export class CoreLocalFileComponent implements OnInit {
* *
* @param {Event} e Click event. * @param {Event} e Click event.
*/ */
fileClicked(e: Event) : void { fileClicked(e: Event): void {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -107,14 +104,14 @@ export class CoreLocalFileComponent implements OnInit {
} else { } else {
this.utils.openFile(this.file.toURL()); this.utils.openFile(this.file.toURL());
} }
}; }
/** /**
* Activate the edit mode. * Activate the edit mode.
* *
* @param {Event} e Click event. * @param {Event} e Click event.
*/ */
activateEdit(e: Event) : void { activateEdit(e: Event): void {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
this.editMode = true; this.editMode = true;
@ -124,21 +121,22 @@ export class CoreLocalFileComponent implements OnInit {
// $timeout(function() { // $timeout(function() {
// $mmUtil.focusElement(element[0].querySelector('input')); // $mmUtil.focusElement(element[0].querySelector('input'));
// }); // });
}; }
/** /**
* Rename the file. * Rename the file.
* *
* @param {string} newName New name. * @param {string} newName New name.
*/ */
changeName(newName: string) : void { changeName(newName: string): void {
if (newName == this.file.name) { if (newName == this.file.name) {
// Name hasn't changed, stop. // Name hasn't changed, stop.
this.editMode = false; this.editMode = false;
return; return;
} }
let modal = this.domUtils.showModalLoading(), const modal = this.domUtils.showModalLoading(),
fileAndDir = this.fileProvider.getFileAndDirectoryFromPath(this.relativePath), fileAndDir = this.fileProvider.getFileAndDirectoryFromPath(this.relativePath),
newPath = this.textUtils.concatenatePaths(fileAndDir.directory, newName); newPath = this.textUtils.concatenatePaths(fileAndDir.directory, newName);
@ -152,27 +150,27 @@ export class CoreLocalFileComponent implements OnInit {
this.editMode = false; this.editMode = false;
this.file = fileEntry; this.file = fileEntry;
this.loadFileBasicData(); this.loadFileBasicData();
this.onRename.emit({file: this.file}); this.onRename.emit({ file: this.file });
}).catch(() => { }).catch(() => {
this.domUtils.showErrorModal('core.errorrenamefile', true); this.domUtils.showErrorModal('core.errorrenamefile', true);
}); });
}).finally(() => { }).finally(() => {
modal.dismiss(); modal.dismiss();
}); });
}; }
/** /**
* Delete the file. * Delete the file.
* *
* @param {Event} e Click event. * @param {Event} e Click event.
*/ */
deleteFile(e: Event) : void { deleteFile(e: Event): void {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
// Ask confirmation. // Ask confirmation.
this.domUtils.showConfirm(this.translate.instant('core.confirmdeletefile')).then(() => { this.domUtils.showConfirm(this.translate.instant('core.confirmdeletefile')).then(() => {
let modal = this.domUtils.showModalLoading(); const modal = this.domUtils.showModalLoading();
this.fileProvider.removeFile(this.relativePath).then(() => { this.fileProvider.removeFile(this.relativePath).then(() => {
this.onDelete.emit(); this.onDelete.emit();
}).catch(() => { }).catch(() => {

View File

@ -32,7 +32,7 @@ import { CoreUtilsProvider } from '../../providers/utils/utils';
templateUrl: 'mark-required.html' templateUrl: 'mark-required.html'
}) })
export class CoreMarkRequiredComponent implements OnInit, AfterViewInit { export class CoreMarkRequiredComponent implements OnInit, AfterViewInit {
@Input('core-mark-required') coreMarkRequired: boolean|string = true; @Input('core-mark-required') coreMarkRequired: boolean | string = true;
protected element: HTMLElement; protected element: HTMLElement;
requiredLabel: string; requiredLabel: string;
@ -45,14 +45,14 @@ export class CoreMarkRequiredComponent implements OnInit, AfterViewInit {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.coreMarkRequired = this.utils.isTrueOrOne(this.coreMarkRequired); this.coreMarkRequired = this.utils.isTrueOrOne(this.coreMarkRequired);
} }
/** /**
* Called after the view is initialized. * Called after the view is initialized.
*/ */
ngAfterViewInit() : void { ngAfterViewInit(): void {
if (this.coreMarkRequired) { if (this.coreMarkRequired) {
// Add the "required" to the aria-label. // Add the "required" to the aria-label.
const ariaLabel = this.element.getAttribute('aria-label') || this.textUtils.cleanTags(this.element.innerHTML, true); const ariaLabel = this.element.getAttribute('aria-label') || this.textUtils.cleanTags(this.element.innerHTML, true);

View File

@ -27,17 +27,17 @@ import { DomSanitizer, SafeStyle } from '@angular/platform-browser';
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class CoreProgressBarComponent implements OnChanges { export class CoreProgressBarComponent implements OnChanges {
@Input() progress: number|string; // Percentage from 0 to 100. @Input() progress: number | string; // Percentage from 0 to 100.
@Input() text?: string; // Percentage in text to be shown at the right. If not defined, progress will be used. @Input() text?: string; // Percentage in text to be shown at the right. If not defined, progress will be used.
width: SafeStyle; width: SafeStyle;
protected textSupplied = false; protected textSupplied = false;
constructor(private sanitizer: DomSanitizer) {} constructor(private sanitizer: DomSanitizer) { }
/** /**
* Detect changes on input properties. * Detect changes on input properties.
*/ */
ngOnChanges(changes: {[name: string]: SimpleChange}) { ngOnChanges(changes: { [name: string]: SimpleChange }): void {
if (changes.text && typeof changes.text.currentValue != 'undefined') { if (changes.text && typeof changes.text.currentValue != 'undefined') {
// User provided a custom text, don't use default. // User provided a custom text, don't use default.
this.textSupplied = true; this.textSupplied = true;

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core'; import { Component, Input, Output, EventEmitter, ViewChild, ElementRef } from '@angular/core';
import { TextInput } from 'ionic-angular'; import { TextInput } from 'ionic-angular';
import { CoreDomUtilsProvider } from '../../providers/utils/dom'; import { CoreDomUtilsProvider } from '../../providers/utils/dom';
@ -22,7 +21,7 @@ import { Keyboard } from '@ionic-native/keyboard';
/** /**
* Directive to display a rich text editor if enabled. * Directive to display a rich text editor if enabled.
* *
* If enabled, this directive will show a rich text editor. Otherwise it'll show a regular textarea. * If enabled, this directive will show a rich text editor. Otherwise it'll show a regular textarea.
* *
* This directive requires an OBJECT model. The text written in the editor or textarea will be stored inside * This directive requires an OBJECT model. The text written in the editor or textarea will be stored inside
* a "text" property in that object. This is to ensure 2-way data-binding, since using a string as a model * a "text" property in that object. This is to ensure 2-way data-binding, since using a string as a model
@ -41,15 +40,15 @@ export class CoreRichTextEditorComponent {
// Based on: https://github.com/judgewest2000/Ionic3RichText/ // Based on: https://github.com/judgewest2000/Ionic3RichText/
// @todo: Resize, images, anchor button, fullscreen... // @todo: Resize, images, anchor button, fullscreen...
@Input() placeholder?: string = ""; // Placeholder to set in textarea. @Input() placeholder? = ''; // Placeholder to set in textarea.
@Input() control: FormControl; // Form control. @Input() control: FormControl; // Form control.
@Output() public contentChanged: EventEmitter<string>; @Output() contentChanged: EventEmitter<string>;
@ViewChild('editor') editor: ElementRef; // WYSIWYG editor. @ViewChild('editor') editor: ElementRef; // WYSIWYG editor.
@ViewChild('textarea') textarea: TextInput; // Textarea editor. @ViewChild('textarea') textarea: TextInput; // Textarea editor.
@ViewChild('decorate') decorate: ElementRef; // Buttons. @ViewChild('decorate') decorate: ElementRef; // Buttons.
rteEnabled: boolean = false; rteEnabled = false;
uniqueId = `rte{Math.floor(Math.random() * 1000000)}`; uniqueId = `rte{Math.floor(Math.random() * 1000000)}`;
editorElement: HTMLDivElement; editorElement: HTMLDivElement;
@ -60,7 +59,7 @@ export class CoreRichTextEditorComponent {
/** /**
* Init editor * Init editor
*/ */
ngAfterContentInit() { ngAfterContentInit(): void {
this.domUtils.isRichTextEditorEnabled().then((enabled) => { this.domUtils.isRichTextEditorEnabled().then((enabled) => {
this.rteEnabled = !!enabled; this.rteEnabled = !!enabled;
}); });
@ -77,14 +76,14 @@ export class CoreRichTextEditorComponent {
this.editorElement.oninput = this.onChange.bind(this); this.editorElement.oninput = this.onChange.bind(this);
// Setup button actions. // Setup button actions.
let buttons = (this.decorate.nativeElement as HTMLDivElement).getElementsByTagName('button'); const buttons = (this.decorate.nativeElement as HTMLDivElement).getElementsByTagName('button');
for (let i = 0; i < buttons.length; i++) { for (let i = 0; i < buttons.length; i++) {
let button = buttons[i], const button = buttons[i];
command = button.getAttribute('data-command'); let command = button.getAttribute('data-command');
if (command) { if (command) {
if (command.includes('|')) { if (command.includes('|')) {
let parameter = command.split('|')[1]; const parameter = command.split('|')[1];
command = command.split('|')[0]; command = command.split('|')[0];
button.addEventListener('click', ($event) => { button.addEventListener('click', ($event) => {
@ -101,8 +100,10 @@ export class CoreRichTextEditorComponent {
/** /**
* On change function to sync with form data. * On change function to sync with form data.
*
* @param {Event} $event The event.
*/ */
onChange($event) { onChange($event: Event): void {
if (this.rteEnabled) { if (this.rteEnabled) {
if (this.isNullOrWhiteSpace(this.editorElement.innerText)) { if (this.isNullOrWhiteSpace(this.editorElement.innerText)) {
this.clearText(); this.clearText();
@ -121,8 +122,10 @@ export class CoreRichTextEditorComponent {
/** /**
* Toggle from rte editor to textarea syncing values. * Toggle from rte editor to textarea syncing values.
*
* @param {Event} $event The event.
*/ */
toggleEditor($event) { toggleEditor($event: Event): void {
$event.preventDefault(); $event.preventDefault();
$event.stopPropagation(); $event.stopPropagation();
@ -139,10 +142,12 @@ export class CoreRichTextEditorComponent {
setTimeout(() => { setTimeout(() => {
if (this.rteEnabled) { if (this.rteEnabled) {
this.editorElement.focus(); this.editorElement.focus();
let range = document.createRange();
const range = document.createRange();
range.selectNodeContents(this.editorElement); range.selectNodeContents(this.editorElement);
range.collapse(false); range.collapse(false);
let sel = window.getSelection();
const sel = window.getSelection();
sel.removeAllRanges(); sel.removeAllRanges();
sel.addRange(range); sel.addRange(range);
} else { } else {
@ -158,10 +163,11 @@ export class CoreRichTextEditorComponent {
* Check if text is empty. * Check if text is empty.
* @param {string} value text * @param {string} value text
*/ */
private isNullOrWhiteSpace(value: string) { protected isNullOrWhiteSpace(value: string): boolean {
if (value == null || typeof value == "undefined") { if (value == null || typeof value == 'undefined') {
return true; return true;
} }
value = value.replace(/[\n\r]/g, ''); value = value.replace(/[\n\r]/g, '');
value = value.split(' ').join(''); value = value.split(' ').join('');
@ -171,7 +177,7 @@ export class CoreRichTextEditorComponent {
/** /**
* Clear the text. * Clear the text.
*/ */
clearText() { clearText(): void {
this.editorElement.innerHTML = '<p></p>'; this.editorElement.innerHTML = '<p></p>';
this.textarea.value = ''; this.textarea.value = '';
this.control.setValue(null); this.control.setValue(null);
@ -185,9 +191,9 @@ export class CoreRichTextEditorComponent {
* @param {string} command Command to execute. * @param {string} command Command to execute.
* @param {any} [parameters] Parameters of the command. * @param {any} [parameters] Parameters of the command.
*/ */
private buttonAction($event: any, command: string, parameters: any = null) { protected buttonAction($event: any, command: string, parameters: any = null): void {
$event.preventDefault(); $event.preventDefault();
$event.stopPropagation(); $event.stopPropagation();
document.execCommand(command, false, parameters); document.execCommand(command, false, parameters);
} }
} }

View File

@ -31,20 +31,20 @@ import { CoreUtilsProvider } from '../../providers/utils/utils';
templateUrl: 'search-box.html' templateUrl: 'search-box.html'
}) })
export class CoreSearchBoxComponent implements OnInit { export class CoreSearchBoxComponent implements OnInit {
@Input() initialValue?: string = ''; // Initial value for search text. @Input() initialValue? = ''; // Initial value for search text.
@Input() searchLabel?: string ; // Label to be used on action button. @Input() searchLabel?: string; // Label to be used on action button.
@Input() placeholder?: string; // Placeholder text for search text input. @Input() placeholder?: string; // Placeholder text for search text input.
@Input() autocorrect?: string = 'on'; // Enables/disable Autocorrection on search text input. @Input() autocorrect? = 'on'; // Enables/disable Autocorrection on search text input.
@Input() spellcheck?: string|boolean = true; // Enables/disable Spellchecker on search text input. @Input() spellcheck?: string | boolean = true; // Enables/disable Spellchecker on search text input.
@Input() autoFocus?: string|boolean; // Enables/disable Autofocus when entering view. @Input() autoFocus?: string | boolean; // Enables/disable Autofocus when entering view.
@Input() lengthCheck?: number = 3; // Check value length before submit. If 0, any string will be submitted. @Input() lengthCheck? = 3; // Check value length before submit. If 0, any string will be submitted.
@Output() onSubmit: EventEmitter<string>; // Send data when submitting the search form. @Output() onSubmit: EventEmitter<string>; // Send data when submitting the search form.
constructor(private translate: TranslateService, private utils: CoreUtilsProvider) { constructor(private translate: TranslateService, private utils: CoreUtilsProvider) {
this.onSubmit = new EventEmitter(); this.onSubmit = new EventEmitter();
} }
ngOnInit() { ngOnInit(): void {
this.searchLabel = this.searchLabel || this.translate.instant('core.search'); this.searchLabel = this.searchLabel || this.translate.instant('core.search');
this.placeholder = this.placeholder || this.translate.instant('core.search'); this.placeholder = this.placeholder || this.translate.instant('core.search');
this.spellcheck = this.utils.isTrueOrOne(this.spellcheck); this.spellcheck = this.utils.isTrueOrOne(this.spellcheck);
@ -55,7 +55,7 @@ export class CoreSearchBoxComponent implements OnInit {
* *
* @param {string} value Entered value. * @param {string} value Entered value.
*/ */
submitForm(value: string) { submitForm(value: string): void {
if (value.length < this.lengthCheck) { if (value.length < this.lengthCheck) {
// The view should handle this case, but we check it here too just in case. // The view should handle this case, but we check it here too just in case.
return; return;
@ -63,5 +63,4 @@ export class CoreSearchBoxComponent implements OnInit {
this.onSubmit.emit(value); this.onSubmit.emit(value);
} }
} }

View File

@ -35,12 +35,12 @@ import { CoreUtilsProvider } from '../../providers/utils/utils';
}) })
export class CoreShowPasswordComponent implements OnInit, AfterViewInit { export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
@Input() name: string; // Name of the input affected. @Input() name: string; // Name of the input affected.
@Input() initialShown?: boolean|string; // Whether the password should be shown at start. @Input() initialShown?: boolean | string; // Whether the password should be shown at start.
shown: boolean; // Whether the password is shown. shown: boolean; // Whether the password is shown.
label: string; // Label for the button to show/hide. label: string; // Label for the button to show/hide.
iconName: string; // Name of the icon of the button to show/hide. iconName: string; // Name of the icon of the button to show/hide.
selector: string = ''; // Selector to identify the input. selector = ''; // Selector to identify the input.
protected input: HTMLInputElement; // Input affected. protected input: HTMLInputElement; // Input affected.
protected element: HTMLElement; // Current element. protected element: HTMLElement; // Current element.
@ -52,7 +52,7 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.shown = this.utils.isTrueOrOne(this.initialShown); this.shown = this.utils.isTrueOrOne(this.initialShown);
this.selector = 'input[name="' + this.name + '"]'; this.selector = 'input[name="' + this.name + '"]';
this.setData(); this.setData();
@ -61,14 +61,14 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
/** /**
* View has been initialized. * View has been initialized.
*/ */
ngAfterViewInit() { ngAfterViewInit(): void {
this.searchInput(); this.searchInput();
} }
/** /**
* Search the input to show/hide. * Search the input to show/hide.
*/ */
protected searchInput() { protected searchInput(): void {
// Search the input. // Search the input.
this.input = <HTMLInputElement> this.element.querySelector(this.selector); this.input = <HTMLInputElement> this.element.querySelector(this.selector);
@ -89,7 +89,7 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
/** /**
* Set label, icon name and input type. * Set label, icon name and input type.
*/ */
protected setData() { protected setData(): void {
this.label = this.shown ? 'core.hide' : 'core.show'; this.label = this.shown ? 'core.hide' : 'core.show';
this.iconName = this.shown ? 'eye-off' : 'eye'; this.iconName = this.shown ? 'eye-off' : 'eye';
if (this.input) { if (this.input) {
@ -100,7 +100,7 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
/** /**
* Toggle show/hide password. * Toggle show/hide password.
*/ */
toggle() : void { toggle(): void {
this.shown = !this.shown; this.shown = !this.shown;
this.setData(); this.setData();
} }

View File

@ -36,16 +36,16 @@ export class CoreSitePickerComponent implements OnInit {
sites: any[]; sites: any[];
constructor(private translate: TranslateService, private sitesProvider: CoreSitesProvider, constructor(private translate: TranslateService, private sitesProvider: CoreSitesProvider,
private textUtils: CoreTextUtilsProvider) { private textUtils: CoreTextUtilsProvider) {
this.siteSelected = new EventEmitter(); this.siteSelected = new EventEmitter();
} }
ngOnInit() { ngOnInit(): void {
this.selectedSite = this.initialSite || this.sitesProvider.getCurrentSiteId(); this.selectedSite = this.initialSite || this.sitesProvider.getCurrentSiteId();
// Load the sites. // Load the sites.
this.sitesProvider.getSites().then((sites) => { this.sitesProvider.getSites().then((sites) => {
let promises = []; const promises = [];
sites.forEach((site: any) => { sites.forEach((site: any) => {
// Format the site name. // Format the site name.
@ -53,7 +53,7 @@ export class CoreSitePickerComponent implements OnInit {
return site.siteName; return site.siteName;
}).then((formatted) => { }).then((formatted) => {
site.fullNameAndSiteName = this.translate.instant('core.fullnameandsitename', site.fullNameAndSiteName = this.translate.instant('core.fullnameandsitename',
{fullname: site.fullName, sitename: formatted}); { fullname: site.fullName, sitename: formatted });
})); }));
}); });

View File

@ -17,13 +17,15 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { IonicPage } from 'ionic-angular'; import { IonicPage } from 'ionic-angular';
@IonicPage({segment: "core-placeholder"}) @IonicPage({ segment: 'core-placeholder' })
@Component({ @Component({
selector: 'core-placeholder', selector: 'core-placeholder',
templateUrl: 'placeholder.html', templateUrl: 'placeholder.html',
}) })
export class CoreSplitViewPlaceholderPage { export class CoreSplitViewPlaceholderPage {
constructor() { } constructor() {
// Nothing to do.
}
} }

View File

@ -44,9 +44,9 @@ export class CoreSplitViewComponent implements OnInit {
// @todo Mix both panels header buttons // @todo Mix both panels header buttons
@ViewChild('detailNav') detailNav: Nav; @ViewChild('detailNav') detailNav: Nav;
@Input() when?: string | boolean = "md"; // @Input() when?: string | boolean = 'md';
protected isEnabled: boolean = false; protected isEnabled = false;
protected masterPageName: string = ""; protected masterPageName = '';
protected loadDetailPage: any = false; protected loadDetailPage: any = false;
protected element: HTMLElement; // Current element. protected element: HTMLElement; // Current element.
@ -60,7 +60,7 @@ export class CoreSplitViewComponent implements OnInit {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
// Get the master page name and set an empty page as a placeholder. // Get the master page name and set an empty page as a placeholder.
this.masterPageName = this.masterNav.getActive().component.name; this.masterPageName = this.masterNav.getActive().component.name;
this.emptyDetails(); this.emptyDetails();
@ -81,7 +81,7 @@ export class CoreSplitViewComponent implements OnInit {
* @param {any} page The component class or deeplink name you want to push onto the navigation stack. * @param {any} page The component class or deeplink name you want to push onto the navigation stack.
* @param {any} params Any NavParams you want to pass along to the next view. * @param {any} params Any NavParams you want to pass along to the next view.
*/ */
push(page: any, params?: any, element?: HTMLElement) { push(page: any, params?: any, element?: HTMLElement): void {
if (this.isEnabled) { if (this.isEnabled) {
this.detailNav.setRoot(page, params); this.detailNav.setRoot(page, params);
} else { } else {
@ -96,7 +96,7 @@ export class CoreSplitViewComponent implements OnInit {
/** /**
* Set the details panel to default info. * Set the details panel to default info.
*/ */
emptyDetails() { emptyDetails(): void {
this.loadDetailPage = false; this.loadDetailPage = false;
this.detailNav.setRoot('CoreSplitViewPlaceholderPage'); this.detailNav.setRoot('CoreSplitViewPlaceholderPage');
} }
@ -106,7 +106,7 @@ export class CoreSplitViewComponent implements OnInit {
* *
* @param {Boolean} isOn If it fits both panels at the same time. * @param {Boolean} isOn If it fits both panels at the same time.
*/ */
onSplitPaneChanged(isOn) { onSplitPaneChanged(isOn: boolean): void {
this.isEnabled = isOn; this.isEnabled = isOn;
if (this.masterNav && this.detailNav) { if (this.masterNav && this.detailNav) {
(isOn) ? this.activateSplitView() : this.deactivateSplitView(); (isOn) ? this.activateSplitView() : this.deactivateSplitView();
@ -116,14 +116,14 @@ export class CoreSplitViewComponent implements OnInit {
/** /**
* Enable the split view, show both panels and do some magical navigation. * Enable the split view, show both panels and do some magical navigation.
*/ */
activateSplitView() { activateSplitView(): void {
let currentView = this.masterNav.getActive(), const currentView = this.masterNav.getActive(),
currentPageName = currentView.component.name; currentPageName = currentView.component.name;
if (currentPageName != this.masterPageName) { if (currentPageName != this.masterPageName) {
// CurrentView is a 'Detail' page remove it from the 'master' nav stack. // CurrentView is a 'Detail' page remove it from the 'master' nav stack.
this.masterNav.pop(); this.masterNav.pop();
// and add it to the 'detail' nav stack. // And add it to the 'detail' nav stack.
this.detailNav.setRoot(currentView.component, currentView.data); this.detailNav.setRoot(currentView.component, currentView.data);
} else if (this.loadDetailPage) { } else if (this.loadDetailPage) {
// MasterPage is shown, load the last detail page if found. // MasterPage is shown, load the last detail page if found.
@ -135,12 +135,12 @@ export class CoreSplitViewComponent implements OnInit {
/** /**
* Disabled the split view, show only one panel and do some magical navigation. * Disabled the split view, show only one panel and do some magical navigation.
*/ */
deactivateSplitView() { deactivateSplitView(): void {
let detailView = this.detailNav.getActive(), const detailView = this.detailNav.getActive(),
currentPageName = detailView.component.name; currentPageName = detailView.component.name;
if (currentPageName != 'CoreSplitViewPlaceholderPage') { if (currentPageName != 'CoreSplitViewPlaceholderPage') {
// Current detail view is a 'Detail' page so, not the placeholder page, push it on 'master' nav stack. // Current detail view is a 'Detail' page so, not the placeholder page, push it on 'master' nav stack.
this.masterNav.push(detailView.component, detailView.data); this.masterNav.push(detailView.component, detailView.data);
} }
} }
} }

View File

@ -44,12 +44,12 @@ export class CoreTabComponent implements OnInit, OnDestroy {
@Input() icon?: string; // The tab icon. @Input() icon?: string; // The tab icon.
@Input() badge?: string; // A badge to add in the tab. @Input() badge?: string; // A badge to add in the tab.
@Input() badgeStyle?: string; // The badge color. @Input() badgeStyle?: string; // The badge color.
@Input() enabled?: boolean = true; // Whether the tab is enabled. @Input() enabled? = true; // Whether the tab is enabled.
@Input() show?: boolean = true; // Whether the tab should be shown. @Input() show? = true; // Whether the tab should be shown.
@Input() id?: string; // An ID to identify the tab. @Input() id?: string; // An ID to identify the tab.
@Output() ionSelect: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>(); @Output() ionSelect: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>();
@ContentChild(TemplateRef) template: TemplateRef<any> // Template defined by the content. @ContentChild(TemplateRef) template: TemplateRef<any>; // Template defined by the content.
@ContentChild(Content) scroll: Content; @ContentChild(Content) scroll: Content;
element: HTMLElement; // The core-tab element. element: HTMLElement; // The core-tab element.
@ -62,21 +62,21 @@ export class CoreTabComponent implements OnInit, OnDestroy {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.tabs.addTab(this); this.tabs.addTab(this);
} }
/** /**
* Component destroyed. * Component destroyed.
*/ */
ngOnDestroy() { ngOnDestroy(): void {
this.tabs.removeTab(this); this.tabs.removeTab(this);
} }
/** /**
* Select tab. * Select tab.
*/ */
selectTab() { selectTab(): void {
this.element.classList.add('selected'); this.element.classList.add('selected');
this.loaded = true; this.loaded = true;
@ -85,9 +85,9 @@ export class CoreTabComponent implements OnInit, OnDestroy {
// Setup tab scrolling. // Setup tab scrolling.
setTimeout(() => { setTimeout(() => {
if (this.scroll) { if (this.scroll) {
this.scroll.getScrollElement().onscroll = (e) => { this.scroll.getScrollElement().onscroll = (e): void => {
this.tabs.showHideTabs(e); this.tabs.showHideTabs(e);
} };
} }
}, 1); }, 1);
} }
@ -95,7 +95,7 @@ export class CoreTabComponent implements OnInit, OnDestroy {
/** /**
* Unselect tab. * Unselect tab.
*/ */
unselectTab() { unselectTab(): void {
this.element.classList.remove('selected'); this.element.classList.remove('selected');
} }
} }

View File

@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component, Input, Output, EventEmitter, OnInit, OnChanges, AfterViewInit, ViewChild, ElementRef, import {
SimpleChange } from '@angular/core'; Component, Input, Output, EventEmitter, OnInit, OnChanges, AfterViewInit, ViewChild, ElementRef,
SimpleChange
} from '@angular/core';
import { CoreTabComponent } from './tab'; import { CoreTabComponent } from './tab';
import { Content } from 'ionic-angular'; import { Content } from 'ionic-angular';
@ -40,7 +42,7 @@ import { Content } from 'ionic-angular';
templateUrl: 'tabs.html' templateUrl: 'tabs.html'
}) })
export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges { export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
@Input() selectedIndex?: number = 0; // Index of the tab to select. @Input() selectedIndex = 0; // Index of the tab to select.
@Input() hideUntil: boolean; // Determine when should the contents be shown. @Input() hideUntil: boolean; // Determine when should the contents be shown.
@Output() ionChange: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>(); // Emitted when the tab changes. @Output() ionChange: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>(); // Emitted when the tab changes.
@ViewChild('originalTabs') originalTabsRef: ElementRef; @ViewChild('originalTabs') originalTabsRef: ElementRef;
@ -70,7 +72,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.originalTabsContainer = this.originalTabsRef.nativeElement; this.originalTabsContainer = this.originalTabsRef.nativeElement;
this.topTabsElement = this.topTabs.nativeElement; this.topTabsElement = this.topTabs.nativeElement;
} }
@ -78,7 +80,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
/** /**
* View has been initialized. * View has been initialized.
*/ */
ngAfterViewInit() { ngAfterViewInit(): void {
this.afterViewInitTriggered = true; this.afterViewInitTriggered = true;
if (!this.initialized && this.hideUntil) { if (!this.initialized && this.hideUntil) {
// Tabs should be shown, initialize them. // Tabs should be shown, initialize them.
@ -89,7 +91,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
/** /**
* Detect changes on input properties. * Detect changes on input properties.
*/ */
ngOnChanges(changes: {[name: string]: SimpleChange}) { ngOnChanges(changes: { [name: string]: SimpleChange }): void {
// We need to wait for ngAfterViewInit because we need core-tab components to be executed. // We need to wait for ngAfterViewInit because we need core-tab components to be executed.
if (!this.initialized && this.hideUntil && this.afterViewInitTriggered) { if (!this.initialized && this.hideUntil && this.afterViewInitTriggered) {
// Tabs should be shown, initialize them. // Tabs should be shown, initialize them.
@ -105,7 +107,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
* *
* @param {CoreTabComponent} tab The tab to add. * @param {CoreTabComponent} tab The tab to add.
*/ */
addTab(tab: CoreTabComponent) : void { addTab(tab: CoreTabComponent): void {
// Check if tab is already in the list. // Check if tab is already in the list.
if (this.getIndex(tab) == -1) { if (this.getIndex(tab) == -1) {
this.tabs.push(tab); this.tabs.push(tab);
@ -119,13 +121,14 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
* @param {any} tab [description] * @param {any} tab [description]
* @return {number} [description] * @return {number} [description]
*/ */
getIndex(tab: any) : number { getIndex(tab: any): number {
for (let i = 0; i < this.tabs.length; i++) { for (let i = 0; i < this.tabs.length; i++) {
let t = this.tabs[i]; const t = this.tabs[i];
if (t === tab || (typeof t.id != 'undefined' && t.id === tab.id)) { if (t === tab || (typeof t.id != 'undefined' && t.id === tab.id)) {
return i; return i;
} }
} }
return -1; return -1;
} }
@ -134,14 +137,14 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
* *
* @return {CoreTabComponent} Selected tab. * @return {CoreTabComponent} Selected tab.
*/ */
getSelected() : CoreTabComponent { getSelected(): CoreTabComponent {
return this.tabs[this.selected]; return this.tabs[this.selected];
} }
/** /**
* Initialize the tabs, determining the first tab to be shown. * Initialize the tabs, determining the first tab to be shown.
*/ */
protected initializeTabs() : void { protected initializeTabs(): void {
let selectedIndex = this.selectedIndex || 0, let selectedIndex = this.selectedIndex || 0,
selectedTab = this.tabs[selectedIndex]; selectedTab = this.tabs[selectedIndex];
@ -150,8 +153,10 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
selectedTab = this.tabs.find((tab, index) => { selectedTab = this.tabs.find((tab, index) => {
if (tab.enabled && tab.show) { if (tab.enabled && tab.show) {
selectedIndex = index; selectedIndex = index;
return true; return true;
} }
return false; return false;
}); });
} }
@ -162,7 +167,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
// Setup tab scrolling. // Setup tab scrolling.
this.tabBarHeight = this.topTabsElement.offsetHeight; this.tabBarHeight = this.topTabsElement.offsetHeight;
this.originalTabsContainer.style.paddingBottom = this.tabBarHeight + 'px'; this.originalTabsContainer.style.paddingBottom = this.tabBarHeight + 'px';
if (this.scroll) { if (this.scroll) {
this.scroll.classList.add('no-scroll'); this.scroll.classList.add('no-scroll');
} }
@ -175,7 +180,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
* *
* @param {any} e Scroll event. * @param {any} e Scroll event.
*/ */
showHideTabs(e: any) : void { showHideTabs(e: any): void {
if (e.target.scrollTop < this.tabBarHeight) { if (e.target.scrollTop < this.tabBarHeight) {
if (!this.tabsShown) { if (!this.tabsShown) {
this.tabBarElement.classList.remove('tabs-hidden'); this.tabBarElement.classList.remove('tabs-hidden');
@ -194,7 +199,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
* *
* @param {CoreTabComponent} tab The tab to remove. * @param {CoreTabComponent} tab The tab to remove.
*/ */
removeTab(tab: CoreTabComponent) : void { removeTab(tab: CoreTabComponent): void {
const index = this.getIndex(tab); const index = this.getIndex(tab);
this.tabs.splice(index, 1); this.tabs.splice(index, 1);
} }
@ -204,7 +209,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
* *
* @param {number} index The index of the tab to select. * @param {number} index The index of the tab to select.
*/ */
selectTab(index: number) : void { selectTab(index: number): void {
if (index == this.selected) { if (index == this.selected) {
// Already selected. // Already selected.
return; return;
@ -236,13 +241,13 @@ export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
/** /**
* Sort the tabs, keeping the same order as in the original list. * Sort the tabs, keeping the same order as in the original list.
*/ */
protected sortTabs() { protected sortTabs(): void {
if (this.originalTabsContainer) { if (this.originalTabsContainer) {
let newTabs = [], const newTabs = [];
newSelected; let newSelected;
this.tabs.forEach((tab, index) => { this.tabs.forEach((tab, index) => {
let originalIndex = Array.prototype.indexOf.call(this.originalTabsContainer.children, tab.element); const originalIndex = Array.prototype.indexOf.call(this.originalTabsContainer.children, tab.element);
if (originalIndex != -1) { if (originalIndex != -1) {
newTabs[originalIndex] = tab; newTabs[originalIndex] = tab;
if (this.selected == index) { if (this.selected == index) {

View File

@ -16,34 +16,34 @@
* Static class to contain all the core constants. * Static class to contain all the core constants.
*/ */
export class CoreConstants { export class CoreConstants {
public static SECONDS_YEAR = 31536000; static SECONDS_YEAR = 31536000;
public static SECONDS_WEEK = 604800; static SECONDS_WEEK = 604800;
public static SECONDS_DAY = 86400; static SECONDS_DAY = 86400;
public static SECONDS_HOUR = 3600; static SECONDS_HOUR = 3600;
public static SECONDS_MINUTE = 60; static SECONDS_MINUTE = 60;
public static WIFI_DOWNLOAD_THRESHOLD = 104857600; // 100MB. static WIFI_DOWNLOAD_THRESHOLD = 104857600; // 100MB.
public static DOWNLOAD_THRESHOLD = 10485760; // 10MB. static DOWNLOAD_THRESHOLD = 10485760; // 10MB.
public static DONT_SHOW_ERROR = 'CoreDontShowError'; static DONT_SHOW_ERROR = 'CoreDontShowError';
public static NO_SITE_ID = 'NoSite'; static NO_SITE_ID = 'NoSite';
// Settings constants. // Settings constants.
public static SETTINGS_RICH_TEXT_EDITOR = 'CoreSettingsRichTextEditor'; static SETTINGS_RICH_TEXT_EDITOR = 'CoreSettingsRichTextEditor';
public static SETTINGS_NOTIFICATION_SOUND = 'CoreSettingsNotificationSound'; static SETTINGS_NOTIFICATION_SOUND = 'CoreSettingsNotificationSound';
public static SETTINGS_SYNC_ONLY_ON_WIFI = 'CoreSettingsSyncOnlyOnWifi'; static SETTINGS_SYNC_ONLY_ON_WIFI = 'CoreSettingsSyncOnlyOnWifi';
// WS constants. // WS constants.
public static WS_TIMEOUT = 30000; static WS_TIMEOUT = 30000;
public static WS_PREFIX = 'local_mobile_'; static WS_PREFIX = 'local_mobile_';
// Login constants. // Login constants.
public static LOGIN_SSO_CODE = 2; // SSO in browser window is required. static LOGIN_SSO_CODE = 2; // SSO in browser window is required.
public static LOGIN_SSO_INAPP_CODE = 3; // SSO in embedded browser is required. static LOGIN_SSO_INAPP_CODE = 3; // SSO in embedded browser is required.
public static LOGIN_LAUNCH_DATA = 'CoreLoginLaunchData'; static LOGIN_LAUNCH_DATA = 'CoreLoginLaunchData';
// Download status constants. // Download status constants.
public static DOWNLOADED = 'downloaded'; static DOWNLOADED = 'downloaded';
public static DOWNLOADING = 'downloading'; static DOWNLOADING = 'downloading';
public static NOT_DOWNLOADED = 'notdownloaded'; static NOT_DOWNLOADED = 'notdownloaded';
public static OUTDATED = 'outdated'; static OUTDATED = 'outdated';
public static NOT_DOWNLOADABLE = 'notdownloadable'; static NOT_DOWNLOADABLE = 'notdownloadable';
} }

View File

@ -54,7 +54,9 @@ export class CoreContentLinksHandlerBase implements CoreContentLinksHandler {
*/ */
pattern?: RegExp; pattern?: RegExp;
constructor() {} constructor() {
// Nothing to do.
}
/** /**
* Get the list of actions for a link (url). * Get the list of actions for a link (url).
@ -65,8 +67,8 @@ export class CoreContentLinksHandlerBase implements CoreContentLinksHandler {
* @param {number} [courseId] Course ID related to the URL. Optional but recommended. * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions. * @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
*/ */
getActions(siteIds: string[], url: string, params: any, courseId?: number) : getActions(siteIds: string[], url: string, params: any, courseId?: number):
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return []; return [];
} }
@ -76,7 +78,7 @@ export class CoreContentLinksHandlerBase implements CoreContentLinksHandler {
* @param {string} url The URL to check. * @param {string} url The URL to check.
* @return {boolean} Whether the URL is handled by this handler * @return {boolean} Whether the URL is handled by this handler
*/ */
handles(url: string) : boolean { handles(url: string): boolean {
return this.pattern && url.search(this.pattern) >= 0; return this.pattern && url.search(this.pattern) >= 0;
} }
@ -86,9 +88,9 @@ export class CoreContentLinksHandlerBase implements CoreContentLinksHandler {
* @param {string} url The URL to check. * @param {string} url The URL to check.
* @return {string} Site URL if it is handled, undefined otherwise. * @return {string} Site URL if it is handled, undefined otherwise.
*/ */
getSiteUrl(url: string) : string { getSiteUrl(url: string): string {
if (this.pattern) { if (this.pattern) {
var position = url.search(this.pattern); const position = url.search(this.pattern);
if (position > -1) { if (position > -1) {
return url.substr(0, position); return url.substr(0, position);
} }
@ -105,7 +107,7 @@ export class CoreContentLinksHandlerBase implements CoreContentLinksHandler {
* @param {number} [courseId] Course ID related to the URL. Optional but recommended. * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
* @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site. * @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site.
*/ */
isEnabled(siteId: string, url: string, params: any, courseId?: number) : boolean|Promise<boolean> { isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise<boolean> {
return true; return true;
} }
} }

View File

@ -60,12 +60,13 @@ export class CoreContentLinksModuleGradeHandler extends CoreContentLinksHandlerB
* @param {number} [courseId] Course ID related to the URL. Optional but recommended. * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions. * @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
*/ */
getActions(siteIds: string[], url: string, params: any, courseId?: number) : getActions(siteIds: string[], url: string, params: any, courseId?: number):
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
courseId = courseId || params.courseid || params.cid; courseId = courseId || params.courseid || params.cid;
return [{ return [{
action: (siteId, navCtrl?) : void => { action: (siteId, navCtrl?): void => {
// Check if userid is the site's current user. // Check if userid is the site's current user.
const modal = this.domUtils.showModalLoading(); const modal = this.domUtils.showModalLoading();
this.sitesProvider.getSite(siteId).then((site) => { this.sitesProvider.getSite(siteId).then((site) => {
@ -96,7 +97,7 @@ export class CoreContentLinksModuleGradeHandler extends CoreContentLinksHandlerB
* @param {NavController} [navCtrl] Nav Controller to use to navigate. * @param {NavController} [navCtrl] Nav Controller to use to navigate.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
protected goToReview(url: string, params: any, courseId: number, siteId: string, navCtrl?: NavController) : Promise<any> { protected goToReview(url: string, params: any, courseId: number, siteId: string, navCtrl?: NavController): Promise<any> {
// This function should be overridden. // This function should be overridden.
return Promise.resolve(); return Promise.resolve();
} }

View File

@ -50,12 +50,13 @@ export class CoreContentLinksModuleIndexHandler extends CoreContentLinksHandlerB
* @param {number} [courseId] Course ID related to the URL. Optional but recommended. * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions. * @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
*/ */
getActions(siteIds: string[], url: string, params: any, courseId?: number) : getActions(siteIds: string[], url: string, params: any, courseId?: number):
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
courseId = courseId || params.courseid || params.cid; courseId = courseId || params.courseid || params.cid;
return [{ return [{
action: (siteId, navCtrl?) => { action: (siteId, navCtrl?): void => {
this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId); this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId);
} }
}]; }];

View File

@ -22,7 +22,7 @@ import { CoreContentLinksHelperProvider } from '../../providers/helper';
/** /**
* Page to display the list of sites to choose one to perform a content link action. * Page to display the list of sites to choose one to perform a content link action.
*/ */
@IonicPage({segment: 'core-content-links-choose-site'}) @IonicPage({ segment: 'core-content-links-choose-site' })
@Component({ @Component({
selector: 'page-core-content-links-choose-site', selector: 'page-core-content-links-choose-site',
templateUrl: 'choose-site.html', templateUrl: 'choose-site.html',
@ -43,7 +43,7 @@ export class CoreContentLinksChooseSitePage implements OnInit {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
if (!this.url) { if (!this.url) {
return this.leaveView(); return this.leaveView();
} }
@ -70,7 +70,7 @@ export class CoreContentLinksChooseSitePage implements OnInit {
/** /**
* Cancel. * Cancel.
*/ */
cancel() : void { cancel(): void {
this.leaveView(); this.leaveView();
} }
@ -79,17 +79,16 @@ export class CoreContentLinksChooseSitePage implements OnInit {
* *
* @param {string} siteId Site ID. * @param {string} siteId Site ID.
*/ */
siteClicked(siteId: string) : void { siteClicked(siteId: string): void {
this.action.action(siteId, this.navCtrl); this.action.action(siteId, this.navCtrl);
} }
/** /**
* Cancel and leave the view. * Cancel and leave the view.
*/ */
protected leaveView() { protected leaveView(): void {
this.sitesProvider.logout().finally(() => { this.sitesProvider.logout().finally(() => {
this.navCtrl.setRoot('CoreLoginSitesPage'); this.navCtrl.setRoot('CoreLoginSitesPage');
}); });
} }
}
}

View File

@ -58,8 +58,8 @@ export interface CoreContentLinksHandler {
* @param {number} [courseId] Course ID related to the URL. Optional but recommended. * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions. * @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
*/ */
getActions(siteIds: string[], url: string, params: any, courseId?: number) : getActions(siteIds: string[], url: string, params: any, courseId?: number):
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>; CoreContentLinksAction[] | Promise<CoreContentLinksAction[]>;
/** /**
* Check if a URL is handled by this handler. * Check if a URL is handled by this handler.
@ -67,7 +67,7 @@ export interface CoreContentLinksHandler {
* @param {string} url The URL to check. * @param {string} url The URL to check.
* @return {boolean} Whether the URL is handled by this handler * @return {boolean} Whether the URL is handled by this handler
*/ */
handles(url: string) : boolean; handles(url: string): boolean;
/** /**
* If the URL is handled by this handler, return the site URL. * If the URL is handled by this handler, return the site URL.
@ -75,7 +75,7 @@ export interface CoreContentLinksHandler {
* @param {string} url The URL to check. * @param {string} url The URL to check.
* @return {string} Site URL if it is handled, undefined otherwise. * @return {string} Site URL if it is handled, undefined otherwise.
*/ */
getSiteUrl(url: string) : string; getSiteUrl(url: string): string;
/** /**
* Check if the handler is enabled for a certain site (site + user) and a URL. * Check if the handler is enabled for a certain site (site + user) and a URL.
@ -87,8 +87,8 @@ export interface CoreContentLinksHandler {
* @param {number} [courseId] Course ID related to the URL. Optional but recommended. * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
* @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site. * @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site.
*/ */
isEnabled?(siteId: string, url: string, params: any, courseId?: number) : boolean|Promise<boolean>; isEnabled?(siteId: string, url: string, params: any, courseId?: number): boolean | Promise<boolean>;
}; }
/** /**
* Action to perform when a link is clicked. * Action to perform when a link is clicked.
@ -118,8 +118,8 @@ export interface CoreContentLinksAction {
* @param {string} siteId The site ID. * @param {string} siteId The site ID.
* @param {NavController} [navCtrl] Nav Controller to use to navigate. * @param {NavController} [navCtrl] Nav Controller to use to navigate.
*/ */
action(siteId: string, navCtrl?: NavController) : void; action(siteId: string, navCtrl?: NavController): void;
}; }
/** /**
* Actions and priority for a handler and URL. * Actions and priority for a handler and URL.
@ -136,7 +136,7 @@ export interface CoreContentLinksHandlerActions {
* @type {CoreContentLinksAction[]} * @type {CoreContentLinksAction[]}
*/ */
actions: CoreContentLinksAction[]; actions: CoreContentLinksAction[];
}; }
/** /**
* Delegate to register handlers to handle links. * Delegate to register handlers to handle links.
@ -144,7 +144,7 @@ export interface CoreContentLinksHandlerActions {
@Injectable() @Injectable()
export class CoreContentLinksDelegate { export class CoreContentLinksDelegate {
protected logger; protected logger;
protected handlers: {[s: string]: CoreContentLinksHandler} = {}; // All registered handlers. protected handlers: { [s: string]: CoreContentLinksHandler } = {}; // All registered handlers.
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private urlUtils: CoreUrlUtilsProvider, constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private urlUtils: CoreUrlUtilsProvider,
private utils: CoreUtilsProvider) { private utils: CoreUtilsProvider) {
@ -159,7 +159,7 @@ export class CoreContentLinksDelegate {
* @param {string} [username] Username to use to filter sites. * @param {string} [username] Username to use to filter sites.
* @return {Promise<CoreContentLinksAction[]>} Promise resolved with the actions. * @return {Promise<CoreContentLinksAction[]>} Promise resolved with the actions.
*/ */
getActionsFor(url: string, courseId?: number, username?: string) : Promise<CoreContentLinksAction[]> { getActionsFor(url: string, courseId?: number, username?: string): Promise<CoreContentLinksAction[]> {
if (!url) { if (!url) {
return Promise.resolve([]); return Promise.resolve([]);
} }
@ -170,7 +170,7 @@ export class CoreContentLinksDelegate {
promises = [], promises = [],
params = this.urlUtils.extractUrlParams(url); params = this.urlUtils.extractUrlParams(url);
for (let name in this.handlers) { for (const name in this.handlers) {
const handler = this.handlers[name], const handler = this.handlers[name],
checkAll = handler.checkAllUsers, checkAll = handler.checkAllUsers,
isEnabledFn = this.isHandlerEnabled.bind(this, handler, url, params, courseId); isEnabledFn = this.isHandlerEnabled.bind(this, handler, url, params, courseId);
@ -192,8 +192,8 @@ export class CoreContentLinksDelegate {
// Set default values if any value isn't supplied. // Set default values if any value isn't supplied.
actions.forEach((action) => { actions.forEach((action) => {
action.message = action.message || 'core.view'; action.message = action.message || 'core.view';
action.icon = action.icon || 'eye'; action.icon = action.icon || 'eye';
action.sites = action.sites || siteIds; action.sites = action.sites || siteIds;
}); });
// Add them to the list. // Add them to the list.
@ -221,13 +221,13 @@ export class CoreContentLinksDelegate {
* @param {string} url URL to handle. * @param {string} url URL to handle.
* @return {string} Site URL if the URL is supported by any handler, undefined otherwise. * @return {string} Site URL if the URL is supported by any handler, undefined otherwise.
*/ */
getSiteUrl(url: string) : string { getSiteUrl(url: string): string {
if (!url) { if (!url) {
return; return;
} }
// Check if any handler supports this URL. // Check if any handler supports this URL.
for (let name in this.handlers) { for (const name in this.handlers) {
const handler = this.handlers[name], const handler = this.handlers[name],
siteUrl = handler.getSiteUrl(url); siteUrl = handler.getSiteUrl(url);
@ -264,7 +264,7 @@ export class CoreContentLinksDelegate {
} }
if (!handler.isEnabled) { if (!handler.isEnabled) {
// isEnabled function not provided, assume it's enabled. // Handler doesn't implement isEnabled, assume it's enabled.
return true; return true;
} }
@ -278,13 +278,15 @@ export class CoreContentLinksDelegate {
* @param {CoreContentLinksHandler} handler The handler to register. * @param {CoreContentLinksHandler} handler The handler to register.
* @return {boolean} True if registered successfully, false otherwise. * @return {boolean} True if registered successfully, false otherwise.
*/ */
registerHandler(handler: CoreContentLinksHandler) : boolean { registerHandler(handler: CoreContentLinksHandler): boolean {
if (typeof this.handlers[handler.name] !== 'undefined') { if (typeof this.handlers[handler.name] !== 'undefined') {
this.logger.log(`Addon '${handler.name}' already registered`); this.logger.log(`Addon '${handler.name}' already registered`);
return false; return false;
} }
this.logger.log(`Registered addon '${handler.name}'`); this.logger.log(`Registered addon '${handler.name}'`);
this.handlers[handler.name] = handler; this.handlers[handler.name] = handler;
return true; return true;
} }
@ -294,7 +296,7 @@ export class CoreContentLinksDelegate {
* @param {CoreContentLinksHandlerActions[]} actions Actions to sort. * @param {CoreContentLinksHandlerActions[]} actions Actions to sort.
* @return {CoreContentLinksAction[]} Sorted actions. * @return {CoreContentLinksAction[]} Sorted actions.
*/ */
protected sortActionsByPriority(actions: CoreContentLinksHandlerActions[]) : CoreContentLinksAction[] { protected sortActionsByPriority(actions: CoreContentLinksHandlerActions[]): CoreContentLinksAction[] {
let sorted: CoreContentLinksAction[] = []; let sorted: CoreContentLinksAction[] = [];
// Sort by priority. // Sort by priority.
@ -306,6 +308,7 @@ export class CoreContentLinksDelegate {
actions.forEach((entry) => { actions.forEach((entry) => {
sorted = sorted.concat(entry.actions); sorted = sorted.concat(entry.actions);
}); });
return sorted; return sorted;
} }
} }

View File

@ -50,7 +50,7 @@ export class CoreContentLinksHelperProvider {
* @param {CoreContentLinksAction[]} actions List of actions. * @param {CoreContentLinksAction[]} actions List of actions.
* @return {CoreContentLinksAction} First valid action. Returns undefined if no valid action found. * @return {CoreContentLinksAction} First valid action. Returns undefined if no valid action found.
*/ */
getFirstValidAction(actions: CoreContentLinksAction[]) : CoreContentLinksAction { getFirstValidAction(actions: CoreContentLinksAction[]): CoreContentLinksAction {
if (actions) { if (actions) {
for (let i = 0; i < actions.length; i++) { for (let i = 0; i < actions.length; i++) {
const action = actions[i]; const action = actions[i];
@ -70,7 +70,7 @@ export class CoreContentLinksHelperProvider {
* @param {any} [pageParams] Params to send to the page. * @param {any} [pageParams] Params to send to the page.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
*/ */
goInSite(navCtrl: NavController, pageName: string, pageParams: any, siteId?: string) : void { goInSite(navCtrl: NavController, pageName: string, pageParams: any, siteId?: string): void {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
if (navCtrl && siteId == this.sitesProvider.getCurrentSiteId()) { if (navCtrl && siteId == this.sitesProvider.getCurrentSiteId()) {
navCtrl.push(pageName, pageParams); navCtrl.push(pageName, pageParams);
@ -84,8 +84,8 @@ export class CoreContentLinksHelperProvider {
* *
* @param {string} url URL to treat. * @param {string} url URL to treat.
*/ */
goToChooseSite(url: string) : void { goToChooseSite(url: string): void {
this.appProvider.getRootNavController().setRoot('CoreContentLinksChooseSitePage', {url: url}); this.appProvider.getRootNavController().setRoot('CoreContentLinksChooseSitePage', { url: url });
} }
/** /**
@ -94,20 +94,20 @@ export class CoreContentLinksHelperProvider {
* @param {string} url URL to handle. * @param {string} url URL to handle.
* @return {boolean} True if the URL should be handled by this component, false otherwise. * @return {boolean} True if the URL should be handled by this component, false otherwise.
*/ */
handleCustomUrl(url: string) : boolean { handleCustomUrl(url: string): boolean {
const contentLinksScheme = CoreConfigConstants.customurlscheme + '://link'; const contentLinksScheme = CoreConfigConstants.customurlscheme + '://link';
if (url.indexOf(contentLinksScheme) == -1) { if (url.indexOf(contentLinksScheme) == -1) {
return false; return false;
} }
const modal = this.domUtils.showModalLoading();
let username;
url = decodeURIComponent(url); url = decodeURIComponent(url);
// App opened using custom URL scheme. // App opened using custom URL scheme.
this.logger.debug('Treating custom URL scheme: ' + url); this.logger.debug('Treating custom URL scheme: ' + url);
let modal = this.domUtils.showModalLoading(),
username;
// Delete the scheme from the URL. // Delete the scheme from the URL.
url = url.replace(contentLinksScheme + '=', ''); url = url.replace(contentLinksScheme + '=', '');
@ -124,6 +124,7 @@ export class CoreContentLinksHelperProvider {
}).then((siteIds) => { }).then((siteIds) => {
if (siteIds.length) { if (siteIds.length) {
modal.dismiss(); // Dismiss modal so it doesn't collide with confirms. modal.dismiss(); // Dismiss modal so it doesn't collide with confirms.
return this.handleLink(url, username).then((treated) => { return this.handleLink(url, username).then((treated) => {
if (!treated) { if (!treated) {
this.domUtils.showErrorModal('core.contentlinks.errornoactions', true); this.domUtils.showErrorModal('core.contentlinks.errornoactions', true);
@ -134,14 +135,14 @@ export class CoreContentLinksHelperProvider {
const siteUrl = this.contentLinksDelegate.getSiteUrl(url); const siteUrl = this.contentLinksDelegate.getSiteUrl(url);
if (!siteUrl) { if (!siteUrl) {
this.domUtils.showErrorModal('core.login.invalidsite', true); this.domUtils.showErrorModal('core.login.invalidsite', true);
return; return;
} }
// Check that site exists. // Check that site exists.
return this.sitesProvider.checkSite(siteUrl).then((result) => { return this.sitesProvider.checkSite(siteUrl).then((result) => {
// Site exists. We'll allow to add it. // Site exists. We'll allow to add it.
let promise, const ssoNeeded = this.loginHelper.isSSOLoginNeeded(result.code),
ssoNeeded = this.loginHelper.isSSOLoginNeeded(result.code),
hasRemoteAddonsLoaded = false, hasRemoteAddonsLoaded = false,
pageName = 'CoreLoginCredentialsPage', pageName = 'CoreLoginCredentialsPage',
pageParams = { pageParams = {
@ -150,6 +151,7 @@ export class CoreContentLinksHelperProvider {
urlToOpen: url, urlToOpen: url,
siteConfig: result.config siteConfig: result.config
}; };
let promise;
modal.dismiss(); // Dismiss modal so it doesn't collide with confirms. modal.dismiss(); // Dismiss modal so it doesn't collide with confirms.
@ -161,7 +163,7 @@ export class CoreContentLinksHelperProvider {
const confirmMsg = this.translate.instant('core.contentlinks.confirmurlothersite'); const confirmMsg = this.translate.instant('core.contentlinks.confirmurlothersite');
promise = this.domUtils.showConfirm(confirmMsg).then(() => { promise = this.domUtils.showConfirm(confirmMsg).then(() => {
if (!ssoNeeded) { if (!ssoNeeded) {
// hasRemoteAddonsLoaded = $mmAddonManager.hasRemoteAddonsLoaded(); @todo // @todo hasRemoteAddonsLoaded = $mmAddonManager.hasRemoteAddonsLoaded(); @todo
if (hasRemoteAddonsLoaded) { if (hasRemoteAddonsLoaded) {
// Store the redirect since logout will restart the app. // Store the redirect since logout will restart the app.
this.appProvider.storeRedirect(CoreConstants.NO_SITE_ID, pageName, pageParams); this.appProvider.storeRedirect(CoreConstants.NO_SITE_ID, pageName, pageParams);
@ -177,7 +179,7 @@ export class CoreContentLinksHelperProvider {
return promise.then(() => { return promise.then(() => {
if (ssoNeeded) { if (ssoNeeded) {
this.loginHelper.confirmAndOpenBrowserForSSOLogin( this.loginHelper.confirmAndOpenBrowserForSSOLogin(
result.siteUrl, result.code, result.service, result.config && result.config.launchurl); result.siteUrl, result.code, result.service, result.config && result.config.launchurl);
} else if (!hasRemoteAddonsLoaded) { } else if (!hasRemoteAddonsLoaded) {
this.appProvider.getRootNavController().setRoot(pageName, pageParams); this.appProvider.getRootNavController().setRoot(pageName, pageParams);
} }
@ -205,7 +207,7 @@ export class CoreContentLinksHelperProvider {
* @param {NavController} [navCtrl] Nav Controller to use to navigate. * @param {NavController} [navCtrl] Nav Controller to use to navigate.
* @return {Promise<boolean>} Promise resolved with a boolean: true if URL was treated, false otherwise. * @return {Promise<boolean>} Promise resolved with a boolean: true if URL was treated, false otherwise.
*/ */
handleLink(url: string, username?: string, navCtrl?: NavController) : Promise<boolean> { handleLink(url: string, username?: string, navCtrl?: NavController): Promise<boolean> {
// Check if the link should be treated by some component/addon. // Check if the link should be treated by some component/addon.
return this.contentLinksDelegate.getActionsFor(url, undefined, username).then((actions) => { return this.contentLinksDelegate.getActionsFor(url, undefined, username).then((actions) => {
const action = this.getFirstValidAction(actions); const action = this.getFirstValidAction(actions);
@ -230,8 +232,10 @@ export class CoreContentLinksHelperProvider {
} }
}); });
} }
return true; return true;
} }
return false; return false;
}).catch(() => { }).catch(() => {
return false; return false;

View File

@ -37,7 +37,7 @@ import { CoreConstants } from '../../constants';
* @return {Promise<string>} Promise resolved when the prefetch finishes. The string returned will be stored as "extra" data in the * @return {Promise<string>} Promise resolved when the prefetch finishes. The string returned will be stored as "extra" data in the
* filepool package. If you don't need to store extra data, don't return anything. * filepool package. If you don't need to store extra data, don't return anything.
*/ */
export type prefetchFunction = (module: any, courseId: number, single: boolean, siteId: string, ...args) => Promise<string>; export type prefetchFunction = (module: any, courseId: number, single: boolean, siteId: string, ...args: any[]) => Promise<string>;
/** /**
* Base prefetch handler to be registered in CoreCourseModulePrefetchDelegate. It is useful to minimize the amount of * Base prefetch handler to be registered in CoreCourseModulePrefetchDelegate. It is useful to minimize the amount of
@ -88,10 +88,10 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* List of download promises to prevent downloading the module twice at the same time. * List of download promises to prevent downloading the module twice at the same time.
* @type {{[s: string]: {[s: string]: Promise<any>}}} * @type {{[s: string]: {[s: string]: Promise<any>}}}
*/ */
protected downloadPromises: {[s: string]: {[s: string]: Promise<any>}} = {}; protected downloadPromises: { [s: string]: { [s: string]: Promise<any> } } = {};
// List of services that will be injected using injector. It's done like this so subclasses don't have to send all the // List of services that will be injected using injector.
// services to the parent in the constructor. // It's done like this so subclasses don't have to send all the services to the parent in the constructor.
protected translate: TranslateService; protected translate: TranslateService;
protected appProvider: CoreAppProvider; protected appProvider: CoreAppProvider;
protected courseProvider: CoreCourseProvider; protected courseProvider: CoreCourseProvider;
@ -118,7 +118,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {String} [siteId] Site ID. If not defined, current site. * @param {String} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise of the current download. * @return {Promise<any>} Promise of the current download.
*/ */
addOngoingDownload(id: number, promise: Promise<any>, siteId?: string) : Promise<any> { addOngoingDownload(id: number, promise: Promise<any>, siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
const uniqueId = this.getUniqueId(id); const uniqueId = this.getUniqueId(id);
@ -141,7 +141,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {number} courseId Course ID. * @param {number} courseId Course ID.
* @return {Promise<any>} Promise resolved when all content is downloaded. * @return {Promise<any>} Promise resolved when all content is downloaded.
*/ */
download(module: any, courseId: number) : Promise<any> { download(module: any, courseId: number): Promise<any> {
return this.downloadOrPrefetch(module, courseId, false); return this.downloadOrPrefetch(module, courseId, false);
} }
@ -156,7 +156,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* in the filepool root folder. * in the filepool root folder.
* @return {Promise<any>} Promise resolved when all content is downloaded. Data returned is not reliable. * @return {Promise<any>} Promise resolved when all content is downloaded. Data returned is not reliable.
*/ */
downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string) : Promise<any> { downloadOrPrefetch(module: any, courseId: number, prefetch?: boolean, dirPath?: string): Promise<any> {
if (!this.appProvider.isOnline()) { if (!this.appProvider.isOnline()) {
// Cannot download in offline. // Cannot download in offline.
return Promise.reject(this.translate.instant('core.networkerrormsg')); return Promise.reject(this.translate.instant('core.networkerrormsg'));
@ -169,21 +169,21 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
// Get the intro files. // Get the intro files.
return this.getIntroFiles(module, courseId); return this.getIntroFiles(module, courseId);
}).then((introFiles) => { }).then((introFiles) => {
let downloadFn = prefetch ? this.filepoolProvider.prefetchPackage.bind(this.filepoolProvider) : const downloadFn = prefetch ? this.filepoolProvider.prefetchPackage.bind(this.filepoolProvider) :
this.filepoolProvider.downloadPackage.bind(this.filepoolProvider), this.filepoolProvider.downloadPackage.bind(this.filepoolProvider),
contentFiles = this.getContentDownloadableFiles(module), contentFiles = this.getContentDownloadableFiles(module),
promises = []; promises = [];
if (dirPath) { if (dirPath) {
// Download intro files in filepool root folder. // Download intro files in filepool root folder.
promises.push(this.filepoolProvider.downloadOrPrefetchFiles(siteId, introFiles, prefetch, false, promises.push(this.filepoolProvider.downloadOrPrefetchFiles(siteId, introFiles, prefetch, false,
this.component, module.id)); this.component, module.id));
// Download content files inside dirPath. // Download content files inside dirPath.
promises.push(downloadFn(siteId, contentFiles, this.component, module.id, undefined, dirPath)); promises.push(downloadFn(siteId, contentFiles, this.component, module.id, undefined, dirPath));
} else { } else {
// No dirPath, download everything in filepool root folder. // No dirPath, download everything in filepool root folder.
let files = introFiles.concat(contentFiles); const files = introFiles.concat(contentFiles);
promises.push(downloadFn(siteId, files, this.component, module.id)); promises.push(downloadFn(siteId, files, this.component, module.id));
} }
@ -197,8 +197,8 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {any} module The module object returned by WS. * @param {any} module The module object returned by WS.
* @return {any[]} List of files. * @return {any[]} List of files.
*/ */
getContentDownloadableFiles(module: any) { getContentDownloadableFiles(module: any): any[] {
let files = []; const files = [];
if (module.contents && module.contents.length) { if (module.contents && module.contents.length) {
module.contents.forEach((content) => { module.contents.forEach((content) => {
@ -220,11 +220,11 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @return {Promise<{size: number, total: boolean}>} Promise resolved with the size and a boolean indicating if it was able * @return {Promise<{size: number, total: boolean}>} Promise resolved with the size and a boolean indicating if it was able
* to calculate the total size. * to calculate the total size.
*/ */
getDownloadSize(module: any, courseId: number, single?: boolean) : Promise<{size: number, total: boolean}> { getDownloadSize(module: any, courseId: number, single?: boolean): Promise<{ size: number, total: boolean }> {
return this.getFiles(module, courseId).then((files) => { return this.getFiles(module, courseId).then((files) => {
return this.utils.sumFileSizes(files); return this.utils.sumFileSizes(files);
}).catch(() => { }).catch(() => {
return {size: -1, total: false}; return { size: -1, total: false };
}); });
} }
@ -235,8 +235,9 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {number} courseId Course ID the module belongs to. * @param {number} courseId Course ID the module belongs to.
* @return {number|Promise<number>} Size, or promise resolved with the size. * @return {number|Promise<number>} Size, or promise resolved with the size.
*/ */
getDownloadedSize?(module: any, courseId: number) : number|Promise<number> { getDownloadedSize?(module: any, courseId: number): number | Promise<number> {
const siteId = this.sitesProvider.getCurrentSiteId(); const siteId = this.sitesProvider.getCurrentSiteId();
return this.filepoolProvider.getFilesSizeByComponent(siteId, this.component, module.id); return this.filepoolProvider.getFilesSizeByComponent(siteId, this.component, module.id);
} }
@ -248,7 +249,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {boolean} [single] True if we're downloading a single module, false if we're downloading a whole section. * @param {boolean} [single] True if we're downloading a single module, false if we're downloading a whole section.
* @return {Promise<any[]>} Promise resolved with the list of files. * @return {Promise<any[]>} Promise resolved with the list of files.
*/ */
getFiles(module: any, courseId: number, single?: boolean) : Promise<any[]> { getFiles(module: any, courseId: number, single?: boolean): Promise<any[]> {
// Load module contents if needed. // Load module contents if needed.
return this.loadContents(module, courseId).then(() => { return this.loadContents(module, courseId).then(() => {
return this.getIntroFiles(module, courseId).then((files) => { return this.getIntroFiles(module, courseId).then((files) => {
@ -264,7 +265,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {number} courseId Course ID. * @param {number} courseId Course ID.
* @return {Promise<any[]>} Promise resolved with list of intro files. * @return {Promise<any[]>} Promise resolved with list of intro files.
*/ */
getIntroFiles(module: any, courseId: number) : Promise<any[]> { getIntroFiles(module: any, courseId: number): Promise<any[]> {
return Promise.resolve(this.getIntroFilesFromInstance(module)); return Promise.resolve(this.getIntroFilesFromInstance(module));
} }
@ -275,7 +276,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {any} [instance] The instance to get the intro files (book, assign, ...). If not defined, module will be used. * @param {any} [instance] The instance to get the intro files (book, assign, ...). If not defined, module will be used.
* @return {any[]} List of intro files. * @return {any[]} List of intro files.
*/ */
getIntroFilesFromInstance(module: any, instance?: any) { getIntroFilesFromInstance(module: any, instance?: any): any[] {
if (instance) { if (instance) {
if (typeof instance.introfiles != 'undefined') { if (typeof instance.introfiles != 'undefined') {
return instance.introfiles; return instance.introfiles;
@ -298,13 +299,14 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise of the current download. * @return {Promise<any>} Promise of the current download.
*/ */
getOngoingDownload(id: number, siteId?: string) : Promise<any> { getOngoingDownload(id: number, siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
if (this.isDownloading(id, siteId)) { if (this.isDownloading(id, siteId)) {
// There's already a download ongoing, return the promise. // There's already a download ongoing, return the promise.
return this.downloadPromises[siteId][this.getUniqueId(id)]; return this.downloadPromises[siteId][this.getUniqueId(id)];
} }
return Promise.resolve(); return Promise.resolve();
} }
@ -314,7 +316,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {number} id Unique ID inside component. * @param {number} id Unique ID inside component.
* @return {string} Unique ID. * @return {string} Unique ID.
*/ */
getUniqueId(id: number) { getUniqueId(id: number): string {
return this.component + '#' + id; return this.component + '#' + id;
} }
@ -324,7 +326,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {number} moduleId The module ID. * @param {number} moduleId The module ID.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateContent(moduleId: number) : Promise<any> { invalidateContent(moduleId: number): Promise<any> {
const promises = [], const promises = [],
siteId = this.sitesProvider.getCurrentSiteId(); siteId = this.sitesProvider.getCurrentSiteId();
@ -342,7 +344,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {number} courseId Course ID the module belongs to. * @param {number} courseId Course ID the module belongs to.
* @return {Promise<any>} Promise resolved when invalidated. * @return {Promise<any>} Promise resolved when invalidated.
*/ */
invalidateModule(module: any, courseId: number) : Promise<any> { invalidateModule(module: any, courseId: number): Promise<any> {
return this.courseProvider.invalidateModule(module.id); return this.courseProvider.invalidateModule(module.id);
} }
@ -353,7 +355,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {number} courseId Course ID the module belongs to. * @param {number} courseId Course ID the module belongs to.
* @return {boolean|Promise<boolean>} Whether the module can be downloaded. The promise should never be rejected. * @return {boolean|Promise<boolean>} Whether the module can be downloaded. The promise should never be rejected.
*/ */
isDownloadable(module: any, courseId: number) : boolean|Promise<boolean> { isDownloadable(module: any, courseId: number): boolean | Promise<boolean> {
// By default, mark all instances as downloadable. // By default, mark all instances as downloadable.
return true; return true;
} }
@ -361,12 +363,13 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
/** /**
* Check if a there's an ongoing download for the given identifier. * Check if a there's an ongoing download for the given identifier.
* *
* @param {number} id Unique identifier per component. * @param {number} id Unique identifier per component.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Boolean} True if downloading, false otherwise. * @return {boolean} True if downloading, false otherwise.
*/ */
isDownloading(id: number, siteId?: string) : boolean { isDownloading(id: number, siteId?: string): boolean {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
return !!(this.downloadPromises[siteId] && this.downloadPromises[siteId][this.getUniqueId(id)]); return !!(this.downloadPromises[siteId] && this.downloadPromises[siteId][this.getUniqueId(id)]);
} }
@ -375,7 +378,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* *
* @return {boolean|Promise<boolean>} A boolean, or a promise resolved with a boolean, indicating if the handler is enabled. * @return {boolean|Promise<boolean>} A boolean, or a promise resolved with a boolean, indicating if the handler is enabled.
*/ */
isEnabled() : boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
return true; return true;
} }
@ -385,7 +388,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {any} file File to check. * @param {any} file File to check.
* @return {boolean} Whether the file is downloadable. * @return {boolean} Whether the file is downloadable.
*/ */
isFileDownloadable(file: any) : boolean { isFileDownloadable(file: any): boolean {
return file.type === 'file'; return file.type === 'file';
} }
@ -397,10 +400,11 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {boolean} [ignoreCache] True if it should ignore cached data (it will always fail in offline or server down). * @param {boolean} [ignoreCache] True if it should ignore cached data (it will always fail in offline or server down).
* @return {Promise} Promise resolved when loaded. * @return {Promise} Promise resolved when loaded.
*/ */
loadContents(module: any, courseId: number, ignoreCache?: boolean) : Promise<void> { loadContents(module: any, courseId: number, ignoreCache?: boolean): Promise<void> {
if (this.isResource) { if (this.isResource) {
return this.courseProvider.loadModuleContents(module, courseId, undefined, false, ignoreCache); return this.courseProvider.loadModuleContents(module, courseId, undefined, false, ignoreCache);
} }
return Promise.resolve(); return Promise.resolve();
} }
@ -432,8 +436,8 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the module has been downloaded. Data returned is not reliable. * @return {Promise<any>} Promise resolved when the module has been downloaded. Data returned is not reliable.
*/ */
prefetchPackage(module: any, courseId: number, single: boolean, downloadFn: prefetchFunction, siteId?: string, ...args) : prefetchPackage(module: any, courseId: number, single: boolean, downloadFn: prefetchFunction, siteId?: string, ...args: any[])
Promise<any> { : Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
if (!this.appProvider.isOnline()) { if (!this.appProvider.isOnline()) {
@ -469,8 +473,9 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {string} [extra] Extra data to store. * @param {string} [extra] Extra data to store.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
setDownloaded(id: number, siteId?: string, extra?: string) : Promise<any> { setDownloaded(id: number, siteId?: string, extra?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
return this.filepoolProvider.storePackageStatus(siteId, CoreConstants.DOWNLOADED, this.component, id, extra); return this.filepoolProvider.storePackageStatus(siteId, CoreConstants.DOWNLOADED, this.component, id, extra);
} }
@ -481,8 +486,9 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
setDownloading(id: number, siteId?: string) : Promise<any> { setDownloading(id: number, siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
return this.filepoolProvider.storePackageStatus(siteId, CoreConstants.DOWNLOADING, this.component, id); return this.filepoolProvider.storePackageStatus(siteId, CoreConstants.DOWNLOADING, this.component, id);
} }
@ -494,8 +500,9 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<never>} Rejected promise. * @return {Promise<never>} Rejected promise.
*/ */
setPreviousStatusAndReject(id: number, error?: any, siteId?: string) : Promise<never> { setPreviousStatusAndReject(id: number, error?: any, siteId?: string): Promise<never> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
return this.filepoolProvider.setPackagePreviousStatus(siteId, this.component, id).then(() => { return this.filepoolProvider.setPackagePreviousStatus(siteId, this.component, id).then(() => {
return Promise.reject(error); return Promise.reject(error);
}); });
@ -508,7 +515,7 @@ export class CoreCourseModulePrefetchHandlerBase implements CoreCourseModulePref
* @param {number} courseId Course ID the module belongs to. * @param {number} courseId Course ID the module belongs to.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
removeFiles(module: any, courseId: number) : Promise<any> { removeFiles(module: any, courseId: number): Promise<any> {
return this.filepoolProvider.removeFilesByComponent(this.sitesProvider.getCurrentSiteId(), this.component, module.id); return this.filepoolProvider.removeFilesByComponent(this.sitesProvider.getCurrentSiteId(), this.component, module.id);
} }
} }

View File

@ -12,8 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, ComponentFactoryResolver, ViewChild, ChangeDetectorRef, import {
SimpleChange, Output, EventEmitter } from '@angular/core'; Component, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, ComponentFactoryResolver, ViewChild, ChangeDetectorRef,
SimpleChange, Output, EventEmitter
} from '@angular/core';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { CoreEventsProvider } from '../../../../providers/events'; import { CoreEventsProvider } from '../../../../providers/events';
import { CoreLoggerProvider } from '../../../../providers/logger'; import { CoreLoggerProvider } from '../../../../providers/logger';
@ -54,23 +56,23 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
// The component hasn't been initialized yet. Store the container. // The component hasn't been initialized yet. Store the container.
this.componentContainers['courseFormat'] = el; this.componentContainers['courseFormat'] = el;
} }
}; }
@ViewChild('courseSummary', { read: ViewContainerRef }) set courseSummary(el: ViewContainerRef) { @ViewChild('courseSummary', { read: ViewContainerRef }) set courseSummary(el: ViewContainerRef) {
this.createComponent('courseSummary', this.cfDelegate.getCourseSummaryComponent(this.course), el); this.createComponent('courseSummary', this.cfDelegate.getCourseSummaryComponent(this.course), el);
}; }
@ViewChild('sectionSelector', { read: ViewContainerRef }) set sectionSelector(el: ViewContainerRef) { @ViewChild('sectionSelector', { read: ViewContainerRef }) set sectionSelector(el: ViewContainerRef) {
this.createComponent('sectionSelector', this.cfDelegate.getSectionSelectorComponent(this.course), el); this.createComponent('sectionSelector', this.cfDelegate.getSectionSelectorComponent(this.course), el);
}; }
@ViewChild('singleSection', { read: ViewContainerRef }) set singleSection(el: ViewContainerRef) { @ViewChild('singleSection', { read: ViewContainerRef }) set singleSection(el: ViewContainerRef) {
this.createComponent('singleSection', this.cfDelegate.getSingleSectionComponent(this.course), el); this.createComponent('singleSection', this.cfDelegate.getSingleSectionComponent(this.course), el);
}; }
@ViewChild('allSections', { read: ViewContainerRef }) set allSections(el: ViewContainerRef) { @ViewChild('allSections', { read: ViewContainerRef }) set allSections(el: ViewContainerRef) {
this.createComponent('allSections', this.cfDelegate.getAllSectionsComponent(this.course), el); this.createComponent('allSections', this.cfDelegate.getAllSectionsComponent(this.course), el);
}; }
// Instances and containers of all the components that the handler could define. // Instances and containers of all the components that the handler could define.
protected componentContainers: {[type: string]: ViewContainerRef} = {}; protected componentContainers: { [type: string]: ViewContainerRef } = {};
componentInstances: {[type: string]: any} = {}; componentInstances: { [type: string]: any } = {};
displaySectionSelector: boolean; displaySectionSelector: boolean;
selectedSection: any; selectedSection: any;
@ -94,10 +96,10 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
// Listen for section status changes. // Listen for section status changes.
this.sectionStatusObserver = eventsProvider.on(CoreEventsProvider.SECTION_STATUS_CHANGED, (data) => { this.sectionStatusObserver = eventsProvider.on(CoreEventsProvider.SECTION_STATUS_CHANGED, (data) => {
if (this.downloadEnabled && this.sections && this.sections.length && this.course && data.sectionId && if (this.downloadEnabled && this.sections && this.sections.length && this.course && data.sectionId &&
data.courseId == this.course.id) { data.courseId == this.course.id) {
// Check if the affected section is being downloaded. If so, we don't update section status // Check if the affected section is being downloaded.
// because it'll already be updated when the download finishes. // If so, we don't update section status because it'll already be updated when the download finishes.
let downloadId = this.courseHelper.getSectionDownloadId({id: data.sectionId}); const downloadId = this.courseHelper.getSectionDownloadId({ id: data.sectionId });
if (prefetchDelegate.isBeingDownloaded(downloadId)) { if (prefetchDelegate.isBeingDownloaded(downloadId)) {
return; return;
} }
@ -105,7 +107,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
// Get the affected section. // Get the affected section.
let section; let section;
for (let i = 0; i < this.sections.length; i++) { for (let i = 0; i < this.sections.length; i++) {
let s = this.sections[i]; const s = this.sections[i];
if (s.id === data.sectionId) { if (s.id === data.sectionId) {
section = s; section = s;
break; break;
@ -131,24 +133,24 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.displaySectionSelector = this.cfDelegate.displaySectionSelector(this.course); this.displaySectionSelector = this.cfDelegate.displaySectionSelector(this.course);
this.createComponent( this.createComponent(
'courseFormat', this.cfDelegate.getCourseFormatComponent(this.course), this.componentContainers['courseFormat']); 'courseFormat', this.cfDelegate.getCourseFormatComponent(this.course), this.componentContainers['courseFormat']);
} }
/** /**
* Detect changes on input properties. * Detect changes on input properties.
*/ */
ngOnChanges(changes: {[name: string]: SimpleChange}) { ngOnChanges(changes: { [name: string]: SimpleChange }): void {
if (changes.sections && this.sections) { if (changes.sections && this.sections) {
if (!this.selectedSection) { if (!this.selectedSection) {
// There is no selected section yet, calculate which one to load. // There is no selected section yet, calculate which one to load.
if (this.initialSectionId || this.initialSectionNumber) { if (this.initialSectionId || this.initialSectionNumber) {
// We have an input indicating the section ID to load. Search the section. // We have an input indicating the section ID to load. Search the section.
for (let i = 0; i < this.sections.length; i++) { for (let i = 0; i < this.sections.length; i++) {
let section = this.sections[i]; const section = this.sections[i];
if (section.id == this.initialSectionId || section.section == this.initialSectionNumber) { if (section.id == this.initialSectionId || section.section == this.initialSectionNumber) {
this.loaded = true; this.loaded = true;
this.sectionChanged(section); this.sectionChanged(section);
@ -166,7 +168,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
// We have a selected section, but the list has changed. Search the section in the list. // We have a selected section, but the list has changed. Search the section in the list.
let newSection; let newSection;
for (let i = 0; i < this.sections.length; i++) { for (let i = 0; i < this.sections.length; i++) {
let section = this.sections[i]; const section = this.sections[i];
if (this.compareSections(section, this.selectedSection)) { if (this.compareSections(section, this.selectedSection)) {
newSection = section; newSection = section;
break; break;
@ -186,10 +188,10 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
} }
// Apply the changes to the components and call ngOnChanges if it exists. // Apply the changes to the components and call ngOnChanges if it exists.
for (let type in this.componentInstances) { for (const type in this.componentInstances) {
let instance = this.componentInstances[type]; const instance = this.componentInstances[type];
for (let name in changes) { for (const name in changes) {
instance[name] = changes[name].currentValue; instance[name] = changes[name].currentValue;
} }
@ -207,7 +209,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* @param {ViewContainerRef} container The container to add the component to. * @param {ViewContainerRef} container The container to add the component to.
* @return {boolean} Whether the component was successfully created. * @return {boolean} Whether the component was successfully created.
*/ */
protected createComponent(type: string, componentClass: any, container: ViewContainerRef) : boolean { protected createComponent(type: string, componentClass: any, container: ViewContainerRef): boolean {
if (!componentClass || !container) { if (!componentClass || !container) {
// No component to instantiate or container doesn't exist right now. // No component to instantiate or container doesn't exist right now.
return false; return false;
@ -236,8 +238,9 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
this.cdr.detectChanges(); // The instances are used in ngIf, tell Angular that something has changed. this.cdr.detectChanges(); // The instances are used in ngIf, tell Angular that something has changed.
return true; return true;
} catch(ex) { } catch (ex) {
this.logger.error('Error creating component', type, ex); this.logger.error('Error creating component', type, ex);
return false; return false;
} }
} }
@ -247,8 +250,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* *
* @param {any} newSection The new selected section. * @param {any} newSection The new selected section.
*/ */
sectionChanged(newSection: any) { sectionChanged(newSection: any): void {
let previousValue = this.selectedSection; const previousValue = this.selectedSection;
this.selectedSection = newSection; this.selectedSection = newSection;
// If there is a component to render the current section, update its section. // If there is a component to render the current section, update its section.
@ -269,7 +272,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* @param {any} s2 Second section. * @param {any} s2 Second section.
* @return {boolean} Whether they're equal. * @return {boolean} Whether they're equal.
*/ */
compareSections(s1: any, s2: any) : boolean { compareSections(s1: any, s2: any): boolean {
return s1 && s2 ? s1.id === s2.id : s1 === s2; return s1 && s2 ? s1.id === s2.id : s1 === s2;
} }
@ -278,7 +281,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* *
* @param {boolean} refresh [description] * @param {boolean} refresh [description]
*/ */
protected calculateSectionsStatus(refresh?: boolean) : void { protected calculateSectionsStatus(refresh?: boolean): void {
this.courseHelper.calculateSectionsStatus(this.sections, this.course.id, refresh).catch(() => { this.courseHelper.calculateSectionsStatus(this.sections, this.course.id, refresh).catch(() => {
// Ignore errors (shouldn't happen). // Ignore errors (shouldn't happen).
}); });
@ -290,7 +293,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* @param {Event} e Click event. * @param {Event} e Click event.
* @param {any} section Section to download. * @param {any} section Section to download.
*/ */
prefetch(e: Event, section: any) : void { prefetch(e: Event, section: any): void {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -314,7 +317,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* @param {boolean} [manual] Whether the prefetch was started manually or it was automatically started because all modules * @param {boolean} [manual] Whether the prefetch was started manually or it was automatically started because all modules
* are being downloaded. * are being downloaded.
*/ */
protected prefetchSection(section: any, manual?: boolean) { protected prefetchSection(section: any, manual?: boolean): void {
this.courseHelper.prefetchSection(section, this.course.id, this.sections).catch((error) => { this.courseHelper.prefetchSection(section, this.course.id, this.sections).catch((error) => {
// Don't show error message if it's an automatic download. // Don't show error message if it's an automatic download.
if (!manual) { if (!manual) {
@ -328,7 +331,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
/** /**
* Component destroyed. * Component destroyed.
*/ */
ngOnDestroy() { ngOnDestroy(): void {
if (this.sectionStatusObserver) { if (this.sectionStatusObserver) {
this.sectionStatusObserver.off(); this.sectionStatusObserver.off();
} }

View File

@ -48,7 +48,7 @@ export class CoreCourseModuleCompletionComponent implements OnChanges {
/** /**
* Detect changes on input properties. * Detect changes on input properties.
*/ */
ngOnChanges(changes: {[name: string]: SimpleChange}) { ngOnChanges(changes: { [name: string]: SimpleChange }): void {
if (changes.completion && this.completion) { if (changes.completion && this.completion) {
this.showStatus(); this.showStatus();
} }
@ -59,7 +59,7 @@ export class CoreCourseModuleCompletionComponent implements OnChanges {
* *
* @param {Event} e The click event. * @param {Event} e The click event.
*/ */
completionClicked(e: Event) : void { completionClicked(e: Event): void {
if (this.completion) { if (this.completion) {
if (typeof this.completion.cmid == 'undefined' || this.completion.tracking !== 1) { if (typeof this.completion.cmid == 'undefined' || this.completion.tracking !== 1) {
return; return;
@ -68,7 +68,7 @@ export class CoreCourseModuleCompletionComponent implements OnChanges {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
let modal = this.domUtils.showModalLoading(), const modal = this.domUtils.showModalLoading(),
params = { params = {
cmid: this.completion.cmid, cmid: this.completion.cmid,
completed: this.completion.state === 1 ? 0 : 1 completed: this.completion.state === 1 ? 0 : 1
@ -92,9 +92,9 @@ export class CoreCourseModuleCompletionComponent implements OnChanges {
/** /**
* Set image and description to show as completion icon. * Set image and description to show as completion icon.
*/ */
protected showStatus() : void { protected showStatus(): void {
const moduleName = this.moduleName || '';
let langKey, let langKey,
moduleName = this.moduleName || '',
image; image;
if (this.completion.tracking === 1 && this.completion.state === 0) { if (this.completion.tracking === 1 && this.completion.state === 0) {
@ -132,18 +132,18 @@ export class CoreCourseModuleCompletionComponent implements OnChanges {
langKey += '-override'; langKey += '-override';
promise = this.userProvider.getProfile(this.completion.overrideby, this.completion.courseId, true).then( promise = this.userProvider.getProfile(this.completion.overrideby, this.completion.courseId, true).then(
(profile) => { (profile) => {
return { return {
overrideuser: profile.fullname, overrideuser: profile.fullname,
modname: modNameFormatted modname: modNameFormatted
}; };
}); });
} else { } else {
promise = Promise.resolve(modNameFormatted); promise = Promise.resolve(modNameFormatted);
} }
return promise.then((translateParams) => { return promise.then((translateParams) => {
this.completionDescription = this.translate.instant(langKey, {$a: translateParams}); this.completionDescription = this.translate.instant(langKey, { $a: translateParams });
}); });
}); });
} }

View File

@ -38,8 +38,10 @@ export class CoreCourseModuleDescriptionComponent {
@Input() description: string; // The description to display. @Input() description: string; // The description to display.
@Input() note?: string; // A note to display along with the description. @Input() note?: string; // A note to display along with the description.
@Input() component?: string; // Component for format text directive. @Input() component?: string; // Component for format text directive.
@Input() componentId?: string|number; // Component ID to use in conjunction with the component. @Input() componentId?: string | number; // Component ID to use in conjunction with the component.
@Input() showFull?: string|boolean; // Whether to always display the full description. @Input() showFull?: string | boolean; // Whether to always display the full description.
constructor() {} constructor() {
// Nothing to do.
}
} }

View File

@ -39,7 +39,7 @@ export class CoreCourseModuleComponent implements OnInit {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
// Handler data must be defined. If it isn't, set it to prevent errors. // Handler data must be defined. If it isn't, set it to prevent errors.
if (this.module && !this.module.handlerData) { if (this.module && !this.module.handlerData) {
this.module.handlerData = {}; this.module.handlerData = {};
@ -51,7 +51,7 @@ export class CoreCourseModuleComponent implements OnInit {
* *
* @param {Event} event Click event. * @param {Event} event Click event.
*/ */
moduleClicked(event: Event) { moduleClicked(event: Event): void {
if (this.module.uservisible !== false && this.module.handlerData.action) { if (this.module.uservisible !== false && this.module.handlerData.action) {
this.module.handlerData.action(event, this.navCtrl, this.module, this.courseId); this.module.handlerData.action(event, this.navCtrl, this.module, this.courseId);
} }
@ -63,7 +63,7 @@ export class CoreCourseModuleComponent implements OnInit {
* @param {Event} event Click event. * @param {Event} event Click event.
* @param {CoreCourseModuleHandlerButton} button The clicked button. * @param {CoreCourseModuleHandlerButton} button The clicked button.
*/ */
buttonClicked(event: Event, button: CoreCourseModuleHandlerButton) { buttonClicked(event: Event, button: CoreCourseModuleHandlerButton): void {
if (button && button.action) { if (button && button.action) {
button.action(event, this.navCtrl, this.module, this.courseId); button.action(event, this.navCtrl, this.module, this.courseId);
} }

View File

@ -31,12 +31,12 @@ export class CoreCourseUnsupportedModuleComponent implements OnInit {
isSupportedByTheApp: boolean; isSupportedByTheApp: boolean;
moduleName: string; moduleName: string;
constructor(private courseProvider: CoreCourseProvider, private moduleDelegate: CoreCourseModuleDelegate) {} constructor(private courseProvider: CoreCourseProvider, private moduleDelegate: CoreCourseModuleDelegate) { }
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
this.isDisabledInSite = this.moduleDelegate.isModuleDisabledInSite(this.module.modname); this.isDisabledInSite = this.moduleDelegate.isModuleDisabledInSite(this.module.modname);
this.isSupportedByTheApp = this.moduleDelegate.hasHandler(this.module.modname); this.isSupportedByTheApp = this.moduleDelegate.hasHandler(this.module.modname);
this.moduleName = this.courseProvider.translateModuleName(this.module.modname); this.moduleName = this.courseProvider.translateModuleName(this.module.modname);

View File

@ -22,7 +22,7 @@ import { CoreCourseOptionsDelegate } from './providers/options-delegate';
import { CoreCourseFormatDefaultHandler } from './providers/default-format'; import { CoreCourseFormatDefaultHandler } from './providers/default-format';
import { CoreCourseFormatSingleActivityModule } from './formats/singleactivity/singleactivity.module'; import { CoreCourseFormatSingleActivityModule } from './formats/singleactivity/singleactivity.module';
import { CoreCourseFormatSocialModule } from './formats/social/social.module'; import { CoreCourseFormatSocialModule } from './formats/social/social.module';
import { CoreCourseFormatTopicsModule} from './formats/topics/topics.module'; import { CoreCourseFormatTopicsModule } from './formats/topics/topics.module';
import { CoreCourseFormatWeeksModule } from './formats/weeks/weeks.module'; import { CoreCourseFormatWeeksModule } from './formats/weeks/weeks.module';
@NgModule({ @NgModule({

View File

@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import { Component, Input, OnChanges, ViewContainerRef, ComponentFactoryResolver, ChangeDetectorRef, import { Component, Input, OnChanges, ViewContainerRef, ComponentFactoryResolver, SimpleChange } from '@angular/core';
SimpleChange } from '@angular/core';
import { CoreLoggerProvider } from '../../../../../providers/logger'; import { CoreLoggerProvider } from '../../../../../providers/logger';
import { CoreCourseModuleDelegate } from '../../../providers/module-delegate'; import { CoreCourseModuleDelegate } from '../../../providers/module-delegate';
import { CoreCourseUnsupportedModuleComponent } from '../../../components/unsupported-module/unsupported-module'; import { CoreCourseUnsupportedModuleComponent } from '../../../components/unsupported-module/unsupported-module';
@ -37,17 +36,17 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
protected componentInstance: any; protected componentInstance: any;
constructor(logger: CoreLoggerProvider, private viewRef: ViewContainerRef, private factoryResolver: ComponentFactoryResolver, constructor(logger: CoreLoggerProvider, private viewRef: ViewContainerRef, private factoryResolver: ComponentFactoryResolver,
private cdr: ChangeDetectorRef, private moduleDelegate: CoreCourseModuleDelegate) { private moduleDelegate: CoreCourseModuleDelegate) {
this.logger = logger.getInstance('CoreCourseFormatSingleActivityComponent'); this.logger = logger.getInstance('CoreCourseFormatSingleActivityComponent');
} }
/** /**
* Detect changes on input properties. * Detect changes on input properties.
*/ */
ngOnChanges(changes: {[name: string]: SimpleChange}) { ngOnChanges(changes: { [name: string]: SimpleChange }): void {
if (this.course && this.sections && this.sections.length) { if (this.course && this.sections && this.sections.length) {
// In single activity the module should only have 1 section and 1 module. Get the module. // In single activity the module should only have 1 section and 1 module. Get the module.
let module = this.sections[0] && this.sections[0].modules && this.sections[0].modules[0]; const module = this.sections[0] && this.sections[0].modules && this.sections[0].modules[0];
if (module && !this.componentInstance) { if (module && !this.componentInstance) {
// We haven't created the component yet. Create it now. // We haven't created the component yet. Create it now.
this.createComponent(module); this.createComponent(module);
@ -55,11 +54,11 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
if (this.componentInstance && this.componentInstance.ngOnChanges) { if (this.componentInstance && this.componentInstance.ngOnChanges) {
// Call ngOnChanges of the component. // Call ngOnChanges of the component.
let newChanges: {[name: string]: SimpleChange} = {}; const newChanges: { [name: string]: SimpleChange } = {};
// Check if course has changed. // Check if course has changed.
if (changes.course) { if (changes.course) {
newChanges.course = changes.course newChanges.course = changes.course;
this.componentInstance.course = this.course; this.componentInstance.course = this.course;
} }
@ -69,7 +68,7 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
currentValue: module, currentValue: module,
firstChange: changes.sections.firstChange, firstChange: changes.sections.firstChange,
previousValue: this.module, previousValue: this.module,
isFirstChange: () => { isFirstChange: (): boolean => {
return newChanges.module.firstChange; return newChanges.module.firstChange;
} }
}; };
@ -90,8 +89,8 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
* @param {any} module The module. * @param {any} module The module.
* @return {boolean} Whether the component was successfully created. * @return {boolean} Whether the component was successfully created.
*/ */
protected createComponent(module: any) : boolean { protected createComponent(module: any): boolean {
let componentClass = this.moduleDelegate.getMainComponent(this.course, module) || CoreCourseUnsupportedModuleComponent; const componentClass = this.moduleDelegate.getMainComponent(this.course, module) || CoreCourseUnsupportedModuleComponent;
if (!componentClass) { if (!componentClass) {
// No component to instantiate. // No component to instantiate.
return false; return false;
@ -108,11 +107,10 @@ export class CoreCourseFormatSingleActivityComponent implements OnChanges {
this.componentInstance.courseId = this.course.id; this.componentInstance.courseId = this.course.id;
this.componentInstance.module = module; this.componentInstance.module = module;
// this.cdr.detectChanges(); // The instances are used in ngIf, tell Angular that something has changed.
return true; return true;
} catch(ex) { } catch (ex) {
this.logger.error('Error creating component', ex); this.logger.error('Error creating component', ex);
return false; return false;
} }
} }

View File

@ -23,14 +23,16 @@ import { CoreCourseFormatSingleActivityComponent } from '../components/format';
export class CoreCourseFormatSingleActivityHandler implements CoreCourseFormatHandler { export class CoreCourseFormatSingleActivityHandler implements CoreCourseFormatHandler {
name = 'singleactivity'; name = 'singleactivity';
constructor() {} constructor() {
// Nothing to do.
}
/** /**
* Whether or not the handler is enabled on a site level. * Whether or not the handler is enabled on a site level.
* *
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled. * @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/ */
isEnabled() : boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
return true; return true;
} }
@ -40,7 +42,7 @@ export class CoreCourseFormatSingleActivityHandler implements CoreCourseFormatHa
* @param {any} course The course to check. * @param {any} course The course to check.
* @type {boolean} Whether it can view all sections. * @type {boolean} Whether it can view all sections.
*/ */
canViewAllSections(course: any) : boolean { canViewAllSections(course: any): boolean {
return false; return false;
} }
@ -52,10 +54,11 @@ export class CoreCourseFormatSingleActivityHandler implements CoreCourseFormatHa
* @param {any[]} [sections] List of sections. * @param {any[]} [sections] List of sections.
* @return {string} Title. * @return {string} Title.
*/ */
getCourseTitle(course: any, sections?: any[]) : string { getCourseTitle(course: any, sections?: any[]): string {
if (sections && sections[0] && sections[0].modules && sections[0].modules[0]) { if (sections && sections[0] && sections[0].modules && sections[0].modules[0]) {
return sections[0].modules[0].name; return sections[0].modules[0].name;
} }
return course.fullname || ''; return course.fullname || '';
} }
@ -65,7 +68,7 @@ export class CoreCourseFormatSingleActivityHandler implements CoreCourseFormatHa
* @param {any} course The course to check. * @param {any} course The course to check.
* @type {boolean} Whether the default section selector should be displayed. * @type {boolean} Whether the default section selector should be displayed.
*/ */
displaySectionSelector(course: any) : boolean { displaySectionSelector(course: any): boolean {
return false; return false;
} }
@ -77,7 +80,7 @@ export class CoreCourseFormatSingleActivityHandler implements CoreCourseFormatHa
* @param {any} course The course to render. * @param {any} course The course to render.
* @return {any} The component to use, undefined if not found. * @return {any} The component to use, undefined if not found.
*/ */
getCourseFormatComponent(course: any) : any { getCourseFormatComponent(course: any): any {
return CoreCourseFormatSingleActivityComponent; return CoreCourseFormatSingleActivityComponent;
} }
} }

View File

@ -22,14 +22,16 @@ import { CoreCourseFormatHandler } from '../../../providers/format-delegate';
export class CoreCourseFormatTopicsHandler implements CoreCourseFormatHandler { export class CoreCourseFormatTopicsHandler implements CoreCourseFormatHandler {
name = 'topics'; name = 'topics';
constructor() {} constructor() {
// Nothing to do.
}
/** /**
* Whether or not the handler is enabled on a site level. * Whether or not the handler is enabled on a site level.
* *
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled. * @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/ */
isEnabled() : boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
return true; return true;
} }
} }

View File

@ -24,14 +24,14 @@ import { CoreConstants } from '../../../../constants';
export class CoreCourseFormatWeeksHandler implements CoreCourseFormatHandler { export class CoreCourseFormatWeeksHandler implements CoreCourseFormatHandler {
name = 'weeks'; name = 'weeks';
constructor(private timeUtils: CoreTimeUtilsProvider) {} constructor(private timeUtils: CoreTimeUtilsProvider) { }
/** /**
* Whether or not the handler is enabled on a site level. * Whether or not the handler is enabled on a site level.
* *
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled. * @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/ */
isEnabled() : boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
return true; return true;
} }
@ -42,8 +42,8 @@ export class CoreCourseFormatWeeksHandler implements CoreCourseFormatHandler {
* @param {any[]} sections List of sections. * @param {any[]} sections List of sections.
* @return {any|Promise<any>} Current section (or promise resolved with current section). * @return {any|Promise<any>} Current section (or promise resolved with current section).
*/ */
getCurrentSection(course: any, sections: any[]) : any|Promise<any> { getCurrentSection(course: any, sections: any[]): any | Promise<any> {
let now = this.timeUtils.timestamp(); const now = this.timeUtils.timestamp();
if (now < course.startdate || (course.enddate && now > course.enddate)) { if (now < course.startdate || (course.enddate && now > course.enddate)) {
// Course hasn't started yet or it has ended already. Return the first section. // Course hasn't started yet or it has ended already. Return the first section.
@ -51,12 +51,12 @@ export class CoreCourseFormatWeeksHandler implements CoreCourseFormatHandler {
} }
for (let i = 0; i < sections.length; i++) { for (let i = 0; i < sections.length; i++) {
let section = sections[i]; const section = sections[i];
if (typeof section.section == 'undefined' || section.section < 1) { if (typeof section.section == 'undefined' || section.section < 1) {
continue; continue;
} }
let dates = this.getSectionDates(section, course.startdate); const dates = this.getSectionDates(section, course.startdate);
if ((now >= dates.start) && (now < dates.end)) { if ((now >= dates.start) && (now < dates.end)) {
return section; return section;
} }
@ -73,15 +73,16 @@ export class CoreCourseFormatWeeksHandler implements CoreCourseFormatHandler {
* @param {number} startDate The course start date (in seconds). * @param {number} startDate The course start date (in seconds).
* @return {{start: number, end: number}} An object with the start and end date of the section. * @return {{start: number, end: number}} An object with the start and end date of the section.
*/ */
protected getSectionDates(section: any, startDate: number) : {start: number, end: number} { protected getSectionDates(section: any, startDate: number): { start: number, end: number } {
// Hack alert. We add 2 hours to avoid possible DST problems. (e.g. we go into daylight savings and the date changes). // Hack alert. We add 2 hours to avoid possible DST problems. (e.g. we go into daylight savings and the date changes).
startDate = startDate + 7200; startDate = startDate + 7200;
let dates = { const dates = {
start: startDate + (CoreConstants.SECONDS_WEEK * (section.section - 1)), start: startDate + (CoreConstants.SECONDS_WEEK * (section.section - 1)),
end: 0 end: 0
}; };
dates.end = dates.start + CoreConstants.SECONDS_WEEK; dates.end = dates.start + CoreConstants.SECONDS_WEEK;
return dates; return dates;
} }
} }

View File

@ -22,13 +22,14 @@ import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
import { CoreCourseProvider } from '../../providers/course'; import { CoreCourseProvider } from '../../providers/course';
import { CoreCourseHelperProvider } from '../../providers/helper'; import { CoreCourseHelperProvider } from '../../providers/helper';
import { CoreCourseFormatDelegate } from '../../providers/format-delegate'; import { CoreCourseFormatDelegate } from '../../providers/format-delegate';
import { CoreCourseModulePrefetchDelegate } from '../../providers/module-prefetch-delegate';
import { CoreCourseOptionsDelegate, CoreCourseOptionsHandlerToDisplay } from '../../providers/options-delegate'; import { CoreCourseOptionsDelegate, CoreCourseOptionsHandlerToDisplay } from '../../providers/options-delegate';
import { CoreCoursesProvider } from '../../../courses/providers/courses'; import { CoreCoursesProvider } from '../../../courses/providers/courses';
/** /**
* Page that displays the list of courses the user is enrolled in. * Page that displays the list of courses the user is enrolled in.
*/ */
@IonicPage({segment: 'core-course-section'}) @IonicPage({ segment: 'core-course-section' })
@Component({ @Component({
selector: 'page-core-course-section', selector: 'page-core-course-section',
templateUrl: 'section.html', templateUrl: 'section.html',
@ -44,7 +45,7 @@ export class CoreCourseSectionPage implements OnDestroy {
courseHandlers: CoreCourseOptionsHandlerToDisplay[]; courseHandlers: CoreCourseOptionsHandlerToDisplay[];
dataLoaded: boolean; dataLoaded: boolean;
downloadEnabled: boolean; downloadEnabled: boolean;
downloadEnabledIcon: string = 'square-outline'; // Disabled by default. downloadEnabledIcon = 'square-outline'; // Disabled by default.
prefetchCourseData = { prefetchCourseData = {
prefetchCourseIcon: 'spinner' prefetchCourseIcon: 'spinner'
}; };
@ -57,7 +58,8 @@ export class CoreCourseSectionPage implements OnDestroy {
private courseFormatDelegate: CoreCourseFormatDelegate, private courseOptionsDelegate: CoreCourseOptionsDelegate, private courseFormatDelegate: CoreCourseFormatDelegate, private courseOptionsDelegate: CoreCourseOptionsDelegate,
private translate: TranslateService, private courseHelper: CoreCourseHelperProvider, eventsProvider: CoreEventsProvider, private translate: TranslateService, private courseHelper: CoreCourseHelperProvider, eventsProvider: CoreEventsProvider,
private textUtils: CoreTextUtilsProvider, private coursesProvider: CoreCoursesProvider, private textUtils: CoreTextUtilsProvider, private coursesProvider: CoreCoursesProvider,
sitesProvider: CoreSitesProvider, private navCtrl: NavController) { sitesProvider: CoreSitesProvider, private navCtrl: NavController,
private prefetchDelegate: CoreCourseModulePrefetchDelegate) {
this.course = navParams.get('course'); this.course = navParams.get('course');
this.sectionId = navParams.get('sectionId'); this.sectionId = navParams.get('sectionId');
this.sectionNumber = navParams.get('sectionNumber'); this.sectionNumber = navParams.get('sectionNumber');
@ -82,9 +84,9 @@ export class CoreCourseSectionPage implements OnDestroy {
/** /**
* View loaded. * View loaded.
*/ */
ionViewDidLoad() { ionViewDidLoad(): void {
let module = this.navParams.get('module'); const module = this.navParams.get('module');
if (module) { if (module) {
this.courseHelper.openModule(this.navCtrl, module, this.course.id, this.sectionId); this.courseHelper.openModule(this.navCtrl, module, this.course.id, this.sectionId);
} }
@ -118,11 +120,11 @@ export class CoreCourseSectionPage implements OnDestroy {
/** /**
* Fetch and load all the data required for the view. * Fetch and load all the data required for the view.
*/ */
protected loadData(refresh?: boolean) { protected loadData(refresh?: boolean): Promise<any> {
// First of all, get the course because the data might have changed. // First of all, get the course because the data might have changed.
return this.coursesProvider.getUserCourse(this.course.id).then((course) => { return this.coursesProvider.getUserCourse(this.course.id).then((course) => {
let promises = [], const promises = [];
promise; let promise;
this.course = course; this.course = course;
@ -148,10 +150,10 @@ export class CoreCourseSectionPage implements OnDestroy {
section.formattedName = name; section.formattedName = name;
}); });
section.hasContent = this.courseHelper.sectionHasContent(section); section.hasContent = this.courseHelper.sectionHasContent(section);
return section; return section;
}); });
if (this.courseFormatDelegate.canViewAllSections(this.course)) { if (this.courseFormatDelegate.canViewAllSections(this.course)) {
// Add a fake first section (all sections). // Add a fake first section (all sections).
this.sections.unshift({ this.sections.unshift({
@ -181,7 +183,7 @@ export class CoreCourseSectionPage implements OnDestroy {
* *
* @param {any} refresher Refresher. * @param {any} refresher Refresher.
*/ */
doRefresh(refresher: any) { doRefresh(refresher: any): void {
this.invalidateData().finally(() => { this.invalidateData().finally(() => {
this.loadData(true).finally(() => { this.loadData(true).finally(() => {
refresher.complete(); refresher.complete();
@ -192,7 +194,7 @@ export class CoreCourseSectionPage implements OnDestroy {
/** /**
* The completion of any of the modules have changed. * The completion of any of the modules have changed.
*/ */
onCompletionChange() { onCompletionChange(): void {
this.invalidateData().finally(() => { this.invalidateData().finally(() => {
this.refreshAfterCompletionChange(); this.refreshAfterCompletionChange();
}); });
@ -201,16 +203,16 @@ export class CoreCourseSectionPage implements OnDestroy {
/** /**
* Invalidate the data. * Invalidate the data.
*/ */
protected invalidateData() { protected invalidateData(): Promise<any> {
let promises = []; const promises = [];
promises.push(this.courseProvider.invalidateSections(this.course.id)); promises.push(this.courseProvider.invalidateSections(this.course.id));
promises.push(this.coursesProvider.invalidateUserCourses()); promises.push(this.coursesProvider.invalidateUserCourses());
promises.push(this.courseFormatDelegate.invalidateData(this.course, this.sections)); promises.push(this.courseFormatDelegate.invalidateData(this.course, this.sections));
// if ($scope.sections) { if (this.sections) {
// promises.push($mmCoursePrefetchDelegate.invalidateCourseUpdates(courseId)); promises.push(this.prefetchDelegate.invalidateCourseUpdates(this.course.id));
// } }
return Promise.all(promises); return Promise.all(promises);
} }
@ -218,9 +220,9 @@ export class CoreCourseSectionPage implements OnDestroy {
/** /**
* Refresh list after a completion change since there could be new activities. * Refresh list after a completion change since there could be new activities.
*/ */
protected refreshAfterCompletionChange() { protected refreshAfterCompletionChange(): void {
// Save scroll position to restore it once done. // Save scroll position to restore it once done.
let scrollElement = this.content.getScrollElement(), const scrollElement = this.content.getScrollElement(),
scrollTop = scrollElement.scrollTop || 0, scrollTop = scrollElement.scrollTop || 0,
scrollLeft = scrollElement.scrollLeft || 0; scrollLeft = scrollElement.scrollLeft || 0;
@ -235,8 +237,10 @@ export class CoreCourseSectionPage implements OnDestroy {
/** /**
* Determines the prefetch icon of the course. * Determines the prefetch icon of the course.
*
* @return {Promise<void>} Promise resolved when done.
*/ */
protected determineCoursePrefetchIcon() { protected determineCoursePrefetchIcon(): Promise<void> {
return this.courseHelper.getCourseStatusIcon(this.course.id).then((icon) => { return this.courseHelper.getCourseStatusIcon(this.course.id).then((icon) => {
this.prefetchCourseData.prefetchCourseIcon = icon; this.prefetchCourseData.prefetchCourseIcon = icon;
}); });
@ -245,26 +249,26 @@ export class CoreCourseSectionPage implements OnDestroy {
/** /**
* Prefetch the whole course. * Prefetch the whole course.
*/ */
prefetchCourse() { prefetchCourse(): void {
this.courseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course, this.sections, this.courseHandlers) this.courseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course, this.sections, this.courseHandlers)
.then((downloaded) => { .then((downloaded) => {
if (downloaded && this.downloadEnabled) { if (downloaded && this.downloadEnabled) {
// Recalculate the status. // Recalculate the status.
this.courseHelper.calculateSectionsStatus(this.sections, this.course.id).catch(() => { this.courseHelper.calculateSectionsStatus(this.sections, this.course.id).catch(() => {
// Ignore errors (shouldn't happen). // Ignore errors (shouldn't happen).
}); });
} }
}).catch((error) => { }).catch((error) => {
if (!this.isDestroyed) { if (!this.isDestroyed) {
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
} }
}); });
} }
/** /**
* Toggle download enabled. * Toggle download enabled.
*/ */
toggleDownload() { toggleDownload(): void {
this.downloadEnabled = !this.downloadEnabled; this.downloadEnabled = !this.downloadEnabled;
this.downloadEnabledIcon = this.downloadEnabled ? 'checkbox-outline' : 'square-outline'; this.downloadEnabledIcon = this.downloadEnabled ? 'checkbox-outline' : 'square-outline';
} }
@ -272,7 +276,7 @@ export class CoreCourseSectionPage implements OnDestroy {
/** /**
* Page destroyed. * Page destroyed.
*/ */
ngOnDestroy() { ngOnDestroy(): void {
this.isDestroyed = true; this.isDestroyed = true;
if (this.completionObserver) { if (this.completionObserver) {
this.completionObserver.off(); this.completionObserver.off();

View File

@ -20,7 +20,7 @@ import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
/** /**
* Page that displays info about an unsupported module. * Page that displays info about an unsupported module.
*/ */
@IonicPage({segment: 'core-course-unsupported-module'}) @IonicPage({ segment: 'core-course-unsupported-module' })
@Component({ @Component({
selector: 'page-core-course-unsupported-module', selector: 'page-core-course-unsupported-module',
templateUrl: 'unsupported-module.html', templateUrl: 'unsupported-module.html',
@ -35,7 +35,7 @@ export class CoreCourseUnsupportedModulePage {
/** /**
* Expand the description. * Expand the description.
*/ */
expandDescription() { expandDescription(): void {
this.textUtils.expandText(this.translate.instant('core.description'), this.module.description); this.textUtils.expandText(this.translate.instant('core.description'), this.module.description);
} }
} }

View File

@ -27,9 +27,9 @@ import { CoreConstants } from '../../constants';
*/ */
@Injectable() @Injectable()
export class CoreCourseProvider { export class CoreCourseProvider {
public static ALL_SECTIONS_ID = -1; static ALL_SECTIONS_ID = -1;
public static ACCESS_GUEST = 'courses_access_guest'; static ACCESS_GUEST = 'courses_access_guest';
public static ACCESS_DEFAULT = 'courses_access_default'; static ACCESS_DEFAULT = 'courses_access_default';
protected ROOT_CACHE_KEY = 'mmCourse:'; protected ROOT_CACHE_KEY = 'mmCourse:';
@ -88,10 +88,10 @@ export class CoreCourseProvider {
* @param {number} courseId Course ID. * @param {number} courseId Course ID.
* @param {any} completion Completion status of the module. * @param {any} completion Completion status of the module.
*/ */
checkModuleCompletion(courseId: number, completion: any) : void { checkModuleCompletion(courseId: number, completion: any): void {
if (completion && completion.tracking === 2 && completion.state === 0) { if (completion && completion.tracking === 2 && completion.state === 0) {
this.invalidateSections(courseId).finally(() => { this.invalidateSections(courseId).finally(() => {
this.eventsProvider.trigger(CoreEventsProvider.COMPLETION_MODULE_VIEWED, {courseId: courseId}); this.eventsProvider.trigger(CoreEventsProvider.COMPLETION_MODULE_VIEWED, { courseId: courseId });
}); });
} }
} }
@ -102,7 +102,7 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<void>} Promise resolved when all status are cleared. * @return {Promise<void>} Promise resolved when all status are cleared.
*/ */
clearAllCoursesStatus(siteId?: string) : Promise<void> { clearAllCoursesStatus(siteId?: string): Promise<void> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
this.logger.debug('Clear all course status for site ' + site.id); this.logger.debug('Clear all course status for site ' + site.id);
@ -120,13 +120,13 @@ export class CoreCourseProvider {
* @param {number} [userId] User ID. If not defined, current user. * @param {number} [userId] User ID. If not defined, current user.
* @return {Promise<any>} Promise resolved with the completion statuses: object where the key is module ID. * @return {Promise<any>} Promise resolved with the completion statuses: object where the key is module ID.
*/ */
getActivitiesCompletionStatus(courseId: number, siteId?: string, userId?: number) : Promise<any> { getActivitiesCompletionStatus(courseId: number, siteId?: string, userId?: number): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
userId = userId || site.getUserId(); userId = userId || site.getUserId();
this.logger.debug(`Getting completion status for user ${userId} in course ${courseId}`); this.logger.debug(`Getting completion status for user ${userId} in course ${courseId}`);
let params = { const params = {
courseid: courseId, courseid: courseId,
userid: userId userid: userId
}, },
@ -138,6 +138,7 @@ export class CoreCourseProvider {
if (data && data.statuses) { if (data && data.statuses) {
return this.utils.arrayToObject(data.statuses, 'cmid'); return this.utils.arrayToObject(data.statuses, 'cmid');
} }
return Promise.reject(null); return Promise.reject(null);
}); });
}); });
@ -150,7 +151,7 @@ export class CoreCourseProvider {
* @param {number} userId User ID. * @param {number} userId User ID.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getActivitiesCompletionCacheKey(courseId: number, userId: number) : string { protected getActivitiesCompletionCacheKey(courseId: number, userId: number): string {
return this.ROOT_CACHE_KEY + 'activitiescompletion:' + courseId + ':' + userId; return this.ROOT_CACHE_KEY + 'activitiescompletion:' + courseId + ':' + userId;
} }
@ -161,12 +162,13 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved with the data. * @return {Promise<any>} Promise resolved with the data.
*/ */
getCourseStatusData(courseId: number, siteId?: string) : Promise<any> { getCourseStatusData(courseId: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.getDb().getRecord(this.COURSE_STATUS_TABLE, {id: courseId}).then((entry) => { return site.getDb().getRecord(this.COURSE_STATUS_TABLE, { id: courseId }).then((entry) => {
if (!entry) { if (!entry) {
return Promise.reject(null); return Promise.reject(null);
} }
return entry; return entry;
}); });
}); });
@ -179,7 +181,7 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<string>} Promise resolved with the status. * @return {Promise<string>} Promise resolved with the status.
*/ */
getCourseStatus(courseId: number, siteId?: string) : Promise<string> { getCourseStatus(courseId: number, siteId?: string): Promise<string> {
return this.getCourseStatusData(courseId, siteId).then((entry) => { return this.getCourseStatusData(courseId, siteId).then((entry) => {
return entry.status || CoreConstants.NOT_DOWNLOADED; return entry.status || CoreConstants.NOT_DOWNLOADED;
}).catch(() => { }).catch(() => {
@ -199,7 +201,7 @@ export class CoreCourseProvider {
* @return {Promise<any>} Promise resolved with the module. * @return {Promise<any>} Promise resolved with the module.
*/ */
getModule(moduleId: number, courseId?: number, sectionId?: number, preferCache?: boolean, ignoreCache?: boolean, getModule(moduleId: number, courseId?: number, sectionId?: number, preferCache?: boolean, ignoreCache?: boolean,
siteId?: string) : Promise<any> { siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
let promise; let promise;
@ -222,7 +224,7 @@ export class CoreCourseProvider {
// We have courseId, we can use core_course_get_contents for compatibility. // We have courseId, we can use core_course_get_contents for compatibility.
this.logger.debug(`Getting module ${moduleId} in course ${courseId}`); this.logger.debug(`Getting module ${moduleId} in course ${courseId}`);
let params = { const params = {
courseid: courseId, courseid: courseId,
options: [ options: [
{ {
@ -253,15 +255,17 @@ export class CoreCourseProvider {
return this.getSections(courseId, false, false, preSets, siteId); return this.getSections(courseId, false, false, preSets, siteId);
}).then((sections) => { }).then((sections) => {
for (let i = 0; i < sections.length; i++) { for (let i = 0; i < sections.length; i++) {
let section = sections[i]; const section = sections[i];
for (let j = 0; j < section.modules.length; j++) { for (let j = 0; j < section.modules.length; j++) {
let module = section.modules[j]; const module = section.modules[j];
if (module.id == moduleId) { if (module.id == moduleId) {
module.course = courseId; module.course = courseId;
return module; return module;
} }
} }
} }
return Promise.reject(null); return Promise.reject(null);
}); });
}); });
@ -274,9 +278,9 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved with the module's info. * @return {Promise<any>} Promise resolved with the module's info.
*/ */
getModuleBasicInfo(moduleId: number, siteId?: string) : Promise<any> { getModuleBasicInfo(moduleId: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let params = { const params = {
cmid: moduleId cmid: moduleId
}, },
preSets = { preSets = {
@ -284,11 +288,12 @@ export class CoreCourseProvider {
}; };
return site.read('core_course_get_course_module', params, preSets).then((response) => { return site.read('core_course_get_course_module', params, preSets).then((response) => {
if (response.warnings && response.warnings.length) { if (response.warnings && response.warnings.length) {
return Promise.reject(response.warnings[0]); return Promise.reject(response.warnings[0]);
} else if (response.cm) { } else if (response.cm) {
return response.cm; return response.cm;
} }
return Promise.reject(null); return Promise.reject(null);
}); });
}); });
@ -301,9 +306,9 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved with the module's grade info. * @return {Promise<any>} Promise resolved with the module's grade info.
*/ */
getModuleBasicGradeInfo(moduleId: number, siteId?: string) : Promise<any> { getModuleBasicGradeInfo(moduleId: number, siteId?: string): Promise<any> {
return this.getModuleBasicInfo(moduleId, siteId).then((info) => { return this.getModuleBasicInfo(moduleId, siteId).then((info) => {
let grade = { const grade = {
advancedgrading: info.advancedgrading || false, advancedgrading: info.advancedgrading || false,
grade: info.grade || false, grade: info.grade || false,
gradecat: info.gradecat || false, gradecat: info.gradecat || false,
@ -315,6 +320,7 @@ export class CoreCourseProvider {
if (grade.grade !== false || grade.advancedgrading !== false || grade.outcomes !== false) { if (grade.grade !== false || grade.advancedgrading !== false || grade.outcomes !== false) {
return grade; return grade;
} }
return false; return false;
}); });
} }
@ -327,9 +333,9 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved with the module's info. * @return {Promise<any>} Promise resolved with the module's info.
*/ */
getModuleBasicInfoByInstance(id: number, module: string, siteId?: string) : Promise<any> { getModuleBasicInfoByInstance(id: number, module: string, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let params = { const params = {
instance: id, instance: id,
module: module module: module
}, },
@ -338,11 +344,12 @@ export class CoreCourseProvider {
}; };
return site.read('core_course_get_course_module_by_instance', params, preSets).then((response) => { return site.read('core_course_get_course_module_by_instance', params, preSets).then((response) => {
if (response.warnings && response.warnings.length) { if (response.warnings && response.warnings.length) {
return Promise.reject(response.warnings[0]); return Promise.reject(response.warnings[0]);
} else if (response.cm) { } else if (response.cm) {
return response.cm; return response.cm;
} }
return Promise.reject(null); return Promise.reject(null);
}); });
}); });
@ -355,7 +362,7 @@ export class CoreCourseProvider {
* @param {string} module Name of the module. E.g. 'glossary'. * @param {string} module Name of the module. E.g. 'glossary'.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getModuleBasicInfoByInstanceCacheKey(id: number, module: string) : string { protected getModuleBasicInfoByInstanceCacheKey(id: number, module: string): string {
return this.ROOT_CACHE_KEY + 'moduleByInstance:' + module + ':' + id; return this.ROOT_CACHE_KEY + 'moduleByInstance:' + module + ':' + id;
} }
@ -365,7 +372,7 @@ export class CoreCourseProvider {
* @param {number} moduleId Module ID. * @param {number} moduleId Module ID.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getModuleCacheKey(moduleId: number) : string { protected getModuleCacheKey(moduleId: number): string {
return this.ROOT_CACHE_KEY + 'module:' + moduleId; return this.ROOT_CACHE_KEY + 'module:' + moduleId;
} }
@ -375,7 +382,7 @@ export class CoreCourseProvider {
* @param {string} moduleName The module name. * @param {string} moduleName The module name.
* @return {string} The IMG src. * @return {string} The IMG src.
*/ */
getModuleIconSrc(moduleName: string) : string { getModuleIconSrc(moduleName: string): string {
if (this.CORE_MODULES.indexOf(moduleName) < 0) { if (this.CORE_MODULES.indexOf(moduleName) < 0) {
moduleName = 'external-tool'; moduleName = 'external-tool';
} }
@ -390,7 +397,7 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<number>} Promise resolved with the section ID. * @return {Promise<number>} Promise resolved with the section ID.
*/ */
getModuleSectionId(moduleId: number, siteId?: string) : Promise<number> { getModuleSectionId(moduleId: number, siteId?: string): Promise<number> {
// Try to get the section using getModuleBasicInfo. // Try to get the section using getModuleBasicInfo.
return this.getModuleBasicInfo(moduleId, siteId).then((module) => { return this.getModuleBasicInfo(moduleId, siteId).then((module) => {
return module.section; return module.section;
@ -408,7 +415,7 @@ export class CoreCourseProvider {
* @return {Promise<any>} Promise resolved with the section. * @return {Promise<any>} Promise resolved with the section.
*/ */
getSection(courseId: number, sectionId?: number, excludeModules?: boolean, excludeContents?: boolean, siteId?: string) getSection(courseId: number, sectionId?: number, excludeModules?: boolean, excludeContents?: boolean, siteId?: string)
: Promise<any> { : Promise<any> {
if (sectionId < 0) { if (sectionId < 0) {
return Promise.reject('Invalid section ID'); return Promise.reject('Invalid section ID');
@ -436,30 +443,30 @@ export class CoreCourseProvider {
* @return {Promise} The reject contains the error message, else contains the sections. * @return {Promise} The reject contains the error message, else contains the sections.
*/ */
getSections(courseId?: number, excludeModules?: boolean, excludeContents?: boolean, preSets?: CoreSiteWSPreSets, getSections(courseId?: number, excludeModules?: boolean, excludeContents?: boolean, preSets?: CoreSiteWSPreSets,
siteId?: string) : Promise<any[]> { siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
preSets = preSets || {}; preSets = preSets || {};
preSets.cacheKey = this.getSectionsCacheKey(courseId); preSets.cacheKey = this.getSectionsCacheKey(courseId);
preSets.getCacheUsingCacheKey = true; // This is to make sure users don't lose offline access when updating. preSets.getCacheUsingCacheKey = true; // This is to make sure users don't lose offline access when updating.
let params = { const params = {
courseid: courseId, courseid: courseId,
options: [ options: [
{ {
name: 'excludemodules', name: 'excludemodules',
value: excludeModules ? 1 : 0 value: excludeModules ? 1 : 0
}, },
{ {
name: 'excludecontents', name: 'excludecontents',
value: excludeContents ? 1 : 0 value: excludeContents ? 1 : 0
} }
] ]
}; };
return site.read('core_course_get_contents', params, preSets).then((sections) => { return site.read('core_course_get_contents', params, preSets).then((sections) => {
let siteHomeId = site.getSiteHomeId(), const siteHomeId = site.getSiteHomeId();
showSections = true; let showSections = true;
if (courseId == siteHomeId) { if (courseId == siteHomeId) {
showSections = site.getStoredConfig('numsections'); showSections = site.getStoredConfig('numsections');
@ -481,7 +488,7 @@ export class CoreCourseProvider {
* @param {number} courseId Course ID. * @param {number} courseId Course ID.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getSectionsCacheKey(courseId) : string { protected getSectionsCacheKey(courseId: number): string {
return this.ROOT_CACHE_KEY + 'sections:' + courseId; return this.ROOT_CACHE_KEY + 'sections:' + courseId;
} }
@ -491,8 +498,8 @@ export class CoreCourseProvider {
* @param {any[]} sections Sections. * @param {any[]} sections Sections.
* @return {any[]} Modules. * @return {any[]} Modules.
*/ */
getSectionsModules(sections: any[]) : any[] { getSectionsModules(sections: any[]): any[] {
if (!sections || !sections.length) { if (!sections || !sections.length) {
return []; return [];
} }
@ -502,6 +509,7 @@ export class CoreCourseProvider {
modules = modules.concat(section.modules); modules = modules.concat(section.modules);
} }
}); });
return modules; return modules;
} }
@ -512,7 +520,7 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateModule(moduleId: number, siteId?: string) : Promise<any> { invalidateModule(moduleId: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getModuleCacheKey(moduleId)); return site.invalidateWsCacheForKey(this.getModuleCacheKey(moduleId));
}); });
@ -526,7 +534,7 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateModuleByInstance(id: number, module: string, siteId?: string) : Promise<any> { invalidateModuleByInstance(id: number, module: string, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getModuleBasicInfoByInstanceCacheKey(id, module)); return site.invalidateWsCacheForKey(this.getModuleBasicInfoByInstanceCacheKey(id, module));
}); });
@ -540,9 +548,9 @@ export class CoreCourseProvider {
* @param {number} [userId] User ID. If not defined, current user. * @param {number} [userId] User ID. If not defined, current user.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateSections(courseId: number, siteId?: string, userId?: number) : Promise<any> { invalidateSections(courseId: number, siteId?: string, userId?: number): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let promises = [], const promises = [],
siteHomeId = site.getSiteHomeId(); siteHomeId = site.getSiteHomeId();
userId = userId || site.getUserId(); userId = userId || site.getUserId();
@ -552,6 +560,7 @@ export class CoreCourseProvider {
if (courseId == siteHomeId) { if (courseId == siteHomeId) {
promises.push(site.invalidateConfig()); promises.push(site.invalidateConfig());
} }
return Promise.all(promises); return Promise.all(promises);
}); });
} }
@ -568,7 +577,7 @@ export class CoreCourseProvider {
* @return {Promise<void>} Promise resolved when loaded. * @return {Promise<void>} Promise resolved when loaded.
*/ */
loadModuleContents(module: any, courseId?: number, sectionId?: number, preferCache?: boolean, ignoreCache?: boolean, loadModuleContents(module: any, courseId?: number, sectionId?: number, preferCache?: boolean, ignoreCache?: boolean,
siteId?: string) : Promise<void> { siteId?: string): Promise<void> {
if (!ignoreCache && module.contents && module.contents.length) { if (!ignoreCache && module.contents && module.contents.length) {
// Already loaded. // Already loaded.
return Promise.resolve(); return Promise.resolve();
@ -587,10 +596,11 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<void>} Promise resolved when the WS call is successful. * @return {Promise<void>} Promise resolved when the WS call is successful.
*/ */
logView(courseId: number, sectionNumber?: number, siteId?: string) : Promise<void> { logView(courseId: number, sectionNumber?: number, siteId?: string): Promise<void> {
let params: any = { const params: any = {
courseid: courseId courseid: courseId
}; };
if (typeof sectionNumber != 'undefined') { if (typeof sectionNumber != 'undefined') {
params.sectionnumber = sectionNumber; params.sectionnumber = sectionNumber;
} }
@ -600,7 +610,7 @@ export class CoreCourseProvider {
if (!response.status) { if (!response.status) {
return Promise.reject(null); return Promise.reject(null);
} }
}) });
}); });
} }
@ -611,13 +621,13 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<string>} Promise resolved when the status is changed. Resolve param: new status. * @return {Promise<string>} Promise resolved when the status is changed. Resolve param: new status.
*/ */
setCoursePreviousStatus(courseId: number, siteId?: string) : Promise<string> { setCoursePreviousStatus(courseId: number, siteId?: string): Promise<string> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
this.logger.debug(`Set previous status for course ${courseId} in site ${siteId}`); this.logger.debug(`Set previous status for course ${courseId} in site ${siteId}`);
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let db = site.getDb(), const db = site.getDb(),
newData: any = {}; newData: any = {};
// Get current stored data. // Get current stored data.
@ -631,9 +641,10 @@ export class CoreCourseProvider {
newData.downloadTime = entry.previousDownloadTime; newData.downloadTime = entry.previousDownloadTime;
} }
return db.updateRecords(this.COURSE_STATUS_TABLE, newData, {id: courseId}).then(() => { return db.updateRecords(this.COURSE_STATUS_TABLE, newData, { id: courseId }).then(() => {
// Success updating, trigger event. // Success updating, trigger event.
this.triggerCourseStatusChanged(courseId, newData.status, siteId); this.triggerCourseStatusChanged(courseId, newData.status, siteId);
return newData.status; return newData.status;
}); });
}); });
@ -648,8 +659,8 @@ export class CoreCourseProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<void>} Promise resolved when the status is stored. * @return {Promise<void>} Promise resolved when the status is stored.
*/ */
setCourseStatus(courseId: number, status: string, siteId?: string) : Promise<void> { setCourseStatus(courseId: number, status: string, siteId?: string): Promise<void> {
siteId = siteId || this.sitesProvider.getCurrentSiteId() siteId = siteId || this.sitesProvider.getCurrentSiteId();
this.logger.debug(`Set status '${status}' for course ${courseId} in site ${siteId}`); this.logger.debug(`Set status '${status}' for course ${courseId} in site ${siteId}`);
@ -669,7 +680,7 @@ export class CoreCourseProvider {
downloadTime = entry.downloadTime; downloadTime = entry.downloadTime;
previousDownloadTime = entry.previousDownloadTime; previousDownloadTime = entry.previousDownloadTime;
} else { } else {
// downloadTime will be updated, store current time as previous. // The downloadTime will be updated, store current time as previous.
previousDownloadTime = entry.downloadTime; previousDownloadTime = entry.downloadTime;
} }
@ -679,7 +690,7 @@ export class CoreCourseProvider {
}).then((previousStatus) => { }).then((previousStatus) => {
if (previousStatus != status) { if (previousStatus != status) {
// Status has changed, update it. // Status has changed, update it.
let data = { const data = {
id: courseId, id: courseId,
status: status, status: status,
previous: previousStatus, previous: previousStatus,
@ -688,7 +699,7 @@ export class CoreCourseProvider {
previousDownloadTime: previousDownloadTime previousDownloadTime: previousDownloadTime
}; };
return site.getDb().insertOrUpdateRecord(this.COURSE_STATUS_TABLE, data, {id: courseId}); return site.getDb().insertOrUpdateRecord(this.COURSE_STATUS_TABLE, data, { id: courseId });
} }
}).then(() => { }).then(() => {
// Success inserting, trigger event. // Success inserting, trigger event.
@ -703,7 +714,7 @@ export class CoreCourseProvider {
* @param {string} moduleName The module name. * @param {string} moduleName The module name.
* @return {string} Translated name. * @return {string} Translated name.
*/ */
translateModuleName(moduleName: string) : string { translateModuleName(moduleName: string): string {
if (this.CORE_MODULES.indexOf(moduleName) < 0) { if (this.CORE_MODULES.indexOf(moduleName) < 0) {
moduleName = 'external-tool'; moduleName = 'external-tool';
} }
@ -721,7 +732,7 @@ export class CoreCourseProvider {
* @param {string} status New course status. * @param {string} status New course status.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
*/ */
protected triggerCourseStatusChanged(courseId: number, status: string, siteId?: string) : void { protected triggerCourseStatusChanged(courseId: number, status: string, siteId?: string): void {
this.eventsProvider.trigger(CoreEventsProvider.COURSE_STATUS_CHANGED, { this.eventsProvider.trigger(CoreEventsProvider.COURSE_STATUS_CHANGED, {
courseId: courseId, courseId: courseId,
status: status status: status

View File

@ -25,14 +25,14 @@ import { CoreCourseProvider } from './course';
export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler { export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
name = 'default'; name = 'default';
constructor(private coursesProvider: CoreCoursesProvider) {} constructor(private coursesProvider: CoreCoursesProvider) { }
/** /**
* Whether or not the handler is enabled on a site level. * Whether or not the handler is enabled on a site level.
* *
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled. * @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
*/ */
isEnabled() : boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
return true; return true;
} }
@ -42,7 +42,7 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
* @param {any} course The course. * @param {any} course The course.
* @return {string} Title. * @return {string} Title.
*/ */
getCourseTitle?(course: any) : string { getCourseTitle?(course: any): string {
return course.fullname || ''; return course.fullname || '';
} }
@ -52,7 +52,7 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
* @param {any} course The course to check. * @param {any} course The course to check.
* @type {boolean} Whether it can view all sections. * @type {boolean} Whether it can view all sections.
*/ */
canViewAllSections(course: any) : boolean { canViewAllSections(course: any): boolean {
return true; return true;
} }
@ -62,7 +62,7 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
* @param {any} course The course to check. * @param {any} course The course to check.
* @type {boolean} Whether the default section selector should be displayed. * @type {boolean} Whether the default section selector should be displayed.
*/ */
displaySectionSelector(course: any) : boolean { displaySectionSelector(course: any): boolean {
return true; return true;
} }
@ -73,16 +73,16 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
* @param {any[]} sections List of sections. * @param {any[]} sections List of sections.
* @return {any|Promise<any>} Current section (or promise resolved with current section). * @return {any|Promise<any>} Current section (or promise resolved with current section).
*/ */
getCurrentSection(course: any, sections: any[]) : any|Promise<any> { getCurrentSection(course: any, sections: any[]): any | Promise<any> {
// We need the "marker" to determine the current section. // We need the "marker" to determine the current section.
return this.coursesProvider.getCoursesByField('id', course.id).catch(() => { return this.coursesProvider.getCoursesByField('id', course.id).catch(() => {
// Ignore errors. // Ignore errors.
}).then((courses) => { }).then((courses) => {
if (courses && courses[0]) { if (courses && courses[0]) {
// Find the marked section. // Find the marked section.
let course = courses[0]; const course = courses[0];
for (let i = 0; i < sections.length; i++) { for (let i = 0; i < sections.length; i++) {
let section = sections[i]; const section = sections[i];
if (section.section == course.marker) { if (section.section == course.marker) {
return section; return section;
} }
@ -91,7 +91,7 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
// Marked section not found or we couldn't retrieve the marker. Return the first section. // Marked section not found or we couldn't retrieve the marker. Return the first section.
for (let i = 0; i < sections.length; i++) { for (let i = 0; i < sections.length; i++) {
let section = sections[i]; const section = sections[i];
if (section.id != CoreCourseProvider.ALL_SECTIONS_ID) { if (section.id != CoreCourseProvider.ALL_SECTIONS_ID) {
return section; return section;
} }
@ -108,7 +108,7 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
* @param {any[]} sections List of sections. * @param {any[]} sections List of sections.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateData(course: any, sections: any[]) : Promise<any> { invalidateData(course: any, sections: any[]): Promise<any> {
return this.coursesProvider.invalidateCoursesByField('id', course.id); return this.coursesProvider.invalidateCoursesByField('id', course.id);
} }
@ -122,7 +122,7 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
* @param {any} course The course to open. It should contain a "format" attribute. * @param {any} course The course to open. It should contain a "format" attribute.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
openCourse(navCtrl: NavController, course: any) : Promise<any> { openCourse(navCtrl: NavController, course: any): Promise<any> {
return navCtrl.push('CoreCourseSectionPage', {course: course}); return navCtrl.push('CoreCourseSectionPage', { course: course });
} }
} }

View File

@ -136,7 +136,7 @@ export class CoreCourseFormatDelegate extends CoreDelegate {
protected featurePrefix = 'CoreCourseFormatHandler_'; protected featurePrefix = 'CoreCourseFormatHandler_';
constructor(loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, constructor(loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider,
protected defaultHandler: CoreCourseFormatDefaultHandler) { protected defaultHandler: CoreCourseFormatDefaultHandler) {
super('CoreCoursesCourseFormatDelegate', loggerProvider, sitesProvider, eventsProvider); super('CoreCoursesCourseFormatDelegate', loggerProvider, sitesProvider, eventsProvider);
} }
@ -170,7 +170,7 @@ export class CoreCourseFormatDelegate extends CoreDelegate {
* @return {any} Function returned value or default value. * @return {any} Function returned value or default value.
*/ */
protected executeFunction(format: string, fnName: string, params?: any[]): any { protected executeFunction(format: string, fnName: string, params?: any[]): any {
let handler = this.enabledHandlers[format]; const handler = this.enabledHandlers[format];
if (handler && handler[fnName]) { if (handler && handler[fnName]) {
return handler[fnName].apply(handler, params); return handler[fnName].apply(handler, params);
} else if (this.defaultHandler[fnName]) { } else if (this.defaultHandler[fnName]) {

View File

@ -107,14 +107,14 @@ export type CoreCourseCoursesProgress = {
@Injectable() @Injectable()
export class CoreCourseHelperProvider { export class CoreCourseHelperProvider {
protected courseDwnPromises: {[s: string]: {[id: number]: Promise<any>}} = {}; protected courseDwnPromises: { [s: string]: { [id: number]: Promise<any> } } = {};
constructor(private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider, constructor(private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
private moduleDelegate: CoreCourseModuleDelegate, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private moduleDelegate: CoreCourseModuleDelegate, private prefetchDelegate: CoreCourseModulePrefetchDelegate,
private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider, private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider,
private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider, private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider,
private utils: CoreUtilsProvider, private translate: TranslateService, private loginHelper: CoreLoginHelperProvider, private utils: CoreUtilsProvider, private translate: TranslateService, private loginHelper: CoreLoginHelperProvider,
private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider) {} private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider) { }
/** /**
* This function treats every module on the sections provided to load the handler data, treat completion * This function treats every module on the sections provided to load the handler data, treat completion
@ -125,7 +125,7 @@ export class CoreCourseHelperProvider {
* @param {any[]} [completionStatus] List of completion status. * @param {any[]} [completionStatus] List of completion status.
* @return {boolean} Whether the sections have content. * @return {boolean} Whether the sections have content.
*/ */
addHandlerDataForModules(sections: any[], courseId: number, completionStatus?: any) { addHandlerDataForModules(sections: any[], courseId: number, completionStatus?: any): boolean {
let hasContent = false; let hasContent = false;
sections.forEach((section) => { sections.forEach((section) => {
@ -157,7 +157,7 @@ export class CoreCourseHelperProvider {
* @param {boolean} [refresh] True if it shouldn't use module status cache (slower). * @param {boolean} [refresh] True if it shouldn't use module status cache (slower).
* @return {Promise<any>} Promise resolved when the status is calculated. * @return {Promise<any>} Promise resolved when the status is calculated.
*/ */
calculateSectionStatus(section: any, courseId: number, refresh?: boolean) : Promise<any> { calculateSectionStatus(section: any, courseId: number, refresh?: boolean): Promise<any> {
if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) { if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) {
return Promise.reject(null); return Promise.reject(null);
@ -199,10 +199,10 @@ export class CoreCourseHelperProvider {
* @param {boolean} [refresh] True if it shouldn't use module status cache (slower). * @param {boolean} [refresh] True if it shouldn't use module status cache (slower).
* @return {Promise<void>} Promise resolved when the states are calculated. * @return {Promise<void>} Promise resolved when the states are calculated.
*/ */
calculateSectionsStatus(sections: any[], courseId: number, refresh?: boolean) : Promise<void> { calculateSectionsStatus(sections: any[], courseId: number, refresh?: boolean): Promise<void> {
const promises = [];
let allSectionsSection, let allSectionsSection,
allSectionsStatus, allSectionsStatus;
promises = [];
sections.forEach((section) => { sections.forEach((section) => {
if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) { if (section.id === CoreCourseProvider.ALL_SECTIONS_ID) {
@ -248,9 +248,10 @@ export class CoreCourseHelperProvider {
*/ */
confirmAndPrefetchCourse(iconData: any, course: any, sections?: any[], courseHandlers?: CoreCourseOptionsHandlerToDisplay[]) confirmAndPrefetchCourse(iconData: any, course: any, sections?: any[], courseHandlers?: CoreCourseOptionsHandlerToDisplay[])
: Promise<boolean> { : Promise<boolean> {
let initialIcon = iconData.prefetchCourseIcon,
promise, const initialIcon = iconData.prefetchCourseIcon,
siteId = this.sitesProvider.getCurrentSiteId(); siteId = this.sitesProvider.getCurrentSiteId();
let promise;
iconData.prefetchCourseIcon = 'spinner'; iconData.prefetchCourseIcon = 'spinner';
@ -278,16 +279,17 @@ export class CoreCourseHelperProvider {
// Download successful. // Download successful.
return true; return true;
}); });
}, (error) : any => { }, (error): any => {
// User cancelled or there was an error calculating the size. // User cancelled or there was an error calculating the size.
iconData.prefetchCourseIcon = initialIcon; iconData.prefetchCourseIcon = initialIcon;
if (error) { if (error) {
return Promise.reject(error); return Promise.reject(error);
} }
return false; return false;
}); });
}); });
}; }
/** /**
* Confirm and prefetches a list of courses. * Confirm and prefetches a list of courses.
@ -296,18 +298,18 @@ export class CoreCourseHelperProvider {
* @param {Function} [onProgress] Function to call everytime a course is downloaded. * @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. * @return {Promise<boolean>} Resolved with true when downloaded, resolved with false if user cancels, rejected if error.
*/ */
confirmAndPrefetchCourses(courses: any[], onProgress?: (data: CoreCourseCoursesProgress) => void) : Promise<boolean> { confirmAndPrefetchCourses(courses: any[], onProgress?: (data: CoreCourseCoursesProgress) => void): Promise<boolean> {
const siteId = this.sitesProvider.getCurrentSiteId(); const siteId = this.sitesProvider.getCurrentSiteId();
// Confirm the download without checking size because it could take a while. // Confirm the download without checking size because it could take a while.
return this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => { return this.domUtils.showConfirm(this.translate.instant('core.areyousure')).then(() => {
let promises = [], const promises = [],
total = courses.length, total = courses.length;
count = 0; let count = 0;
courses.forEach((course) => { courses.forEach((course) => {
let subPromises = [], const subPromises = [];
sections, let sections,
handlers, handlers,
success = true; success = true;
@ -323,19 +325,20 @@ export class CoreCourseHelperProvider {
return this.prefetchCourse(course, sections, handlers, siteId); return this.prefetchCourse(course, sections, handlers, siteId);
}).catch((error) => { }).catch((error) => {
success = false; success = false;
return Promise.reject(error); return Promise.reject(error);
}).finally(() => { }).finally(() => {
// Course downloaded or failed, notify the progress. // Course downloaded or failed, notify the progress.
count++; count++;
if (onProgress) { if (onProgress) {
onProgress({count: count, total: total, courseId: course.id, success: success}); onProgress({ count: count, total: total, courseId: course.id, success: success });
} }
})); }));
}); });
if (onProgress) { if (onProgress) {
// Notify the start of the download. // Notify the start of the download.
onProgress({count: 0, total: total, success: true}); onProgress({ count: 0, total: total, success: true });
} }
return this.utils.allPromises(promises).then(() => { return this.utils.allPromises(promises).then(() => {
@ -345,7 +348,7 @@ export class CoreCourseHelperProvider {
// User cancelled. // User cancelled.
return false; return false;
}); });
}; }
/** /**
* Show confirmation dialog and then remove a module files. * Show confirmation dialog and then remove a module files.
@ -354,7 +357,7 @@ export class CoreCourseHelperProvider {
* @param {number} courseId Course ID the module belongs to. * @param {number} courseId Course ID the module belongs to.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
confirmAndRemoveFiles(module: any, courseId: number) : Promise<any> { confirmAndRemoveFiles(module: any, courseId: number): Promise<any> {
return this.domUtils.showConfirm(this.translate.instant('course.confirmdeletemodulefiles')).then(() => { return this.domUtils.showConfirm(this.translate.instant('course.confirmdeletemodulefiles')).then(() => {
return this.prefetchDelegate.removeModuleFiles(module, courseId); return this.prefetchDelegate.removeModuleFiles(module, courseId);
}); });
@ -369,14 +372,14 @@ export class CoreCourseHelperProvider {
* @param {boolean} [alwaysConfirm] True to show a confirm even if the size isn't high, false otherwise. * @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. * @return {Promise<any>} Promise resolved if the user confirms or there's no need to confirm.
*/ */
confirmDownloadSizeSection(courseId: number, section?: any, sections?: any[], alwaysConfirm?: boolean) : Promise<any> { confirmDownloadSizeSection(courseId: number, section?: any, sections?: any[], alwaysConfirm?: boolean): Promise<any> {
let sizePromise; let sizePromise;
// Calculate the size of the download. // Calculate the size of the download.
if (section && section.id != CoreCourseProvider.ALL_SECTIONS_ID) { if (section && section.id != CoreCourseProvider.ALL_SECTIONS_ID) {
sizePromise = this.prefetchDelegate.getDownloadSize(section.modules, courseId); sizePromise = this.prefetchDelegate.getDownloadSize(section.modules, courseId);
} else { } else {
let promises = [], const promises = [],
results = { results = {
size: 0, size: 0,
total: true total: true
@ -408,7 +411,7 @@ export class CoreCourseHelperProvider {
* @param {any[]} courses Courses * @param {any[]} courses Courses
* @return {Promise<string>} Promise resolved with the status. * @return {Promise<string>} Promise resolved with the status.
*/ */
determineCoursesStatus(courses: any[]) : Promise<string> { determineCoursesStatus(courses: any[]): Promise<string> {
// Get the status of each course. // Get the status of each course.
const promises = [], const promises = [],
siteId = this.sitesProvider.getCurrentSiteId(); siteId = this.sitesProvider.getCurrentSiteId();
@ -423,6 +426,7 @@ export class CoreCourseHelperProvider {
for (let i = 1; i < statuses.length; i++) { for (let i = 1; i < statuses.length; i++) {
status = this.filepoolProvider.determinePackagesStatus(status, statuses[i]); status = this.filepoolProvider.determinePackagesStatus(status, statuses[i]);
} }
return status; return status;
}); });
} }
@ -434,8 +438,9 @@ export class CoreCourseHelperProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Download promise, undefined if not found. * @return {Promise<any>} Download promise, undefined if not found.
*/ */
getCourseDownloadPromise(courseId: number, siteId?: string) : Promise<any> { getCourseDownloadPromise(courseId: number, siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
return this.courseDwnPromises[siteId] && this.courseDwnPromises[siteId][courseId]; return this.courseDwnPromises[siteId] && this.courseDwnPromises[siteId][courseId];
} }
@ -446,7 +451,7 @@ export class CoreCourseHelperProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<string>} Promise resolved with the icon name. * @return {Promise<string>} Promise resolved with the icon name.
*/ */
getCourseStatusIcon(courseId: number, siteId?: string) : Promise<string> { getCourseStatusIcon(courseId: number, siteId?: string): Promise<string> {
return this.courseProvider.getCourseStatus(courseId, siteId).then((status) => { return this.courseProvider.getCourseStatus(courseId, siteId).then((status) => {
return this.getCourseStatusIconFromStatus(status); return this.getCourseStatusIconFromStatus(status);
}); });
@ -461,7 +466,7 @@ export class CoreCourseHelperProvider {
* @param {String} status Course status. * @param {String} status Course status.
* @return {String} Icon name. * @return {String} Icon name.
*/ */
getCourseStatusIconFromStatus(status: string) : string { getCourseStatusIconFromStatus(status: string): string {
if (status == CoreConstants.DOWNLOADED) { if (status == CoreConstants.DOWNLOADED) {
// Always show refresh icon, we cannot knew if there's anything new in course options. // Always show refresh icon, we cannot knew if there's anything new in course options.
return 'refresh'; return 'refresh';
@ -480,11 +485,12 @@ export class CoreCourseHelperProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<number>} Promise resolved with the module's course ID. * @return {Promise<number>} Promise resolved with the module's course ID.
*/ */
getModuleCourseIdByInstance(id: number, module: any, siteId?: string) : Promise<number> { getModuleCourseIdByInstance(id: number, module: any, siteId?: string): Promise<number> {
return this.courseProvider.getModuleBasicInfoByInstance(id, module, siteId).then((cm) => { return this.courseProvider.getModuleBasicInfoByInstance(id, module, siteId).then((cm) => {
return cm.course; return cm.course;
}).catch((error) => { }).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true); this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
return Promise.reject(null); return Promise.reject(null);
}); });
} }
@ -500,7 +506,7 @@ export class CoreCourseHelperProvider {
*/ */
getModulePrefetchInfo(module: any, courseId: number, invalidateCache?: boolean, component?: string) getModulePrefetchInfo(module: any, courseId: number, invalidateCache?: boolean, component?: string)
: Promise<CoreCourseModulePrefetchInfo> { : Promise<CoreCourseModulePrefetchInfo> {
let moduleInfo: CoreCourseModulePrefetchInfo = {}, const moduleInfo: CoreCourseModulePrefetchInfo = {},
siteId = this.sitesProvider.getCurrentSiteId(), siteId = this.sitesProvider.getCurrentSiteId(),
promises = []; promises = [];
@ -514,19 +520,6 @@ export class CoreCourseHelperProvider {
})); }));
// @todo: Decide what to display instead of timemodified. Last check_updates? // @todo: Decide what to display instead of timemodified. Last check_updates?
// promises.push(this.prefetchDelegate.getModuleTimemodified(module, courseId).then(function(moduleModified) {
// moduleInfo.timemodified = moduleModified;
// if (moduleModified > 0) {
// var now = $mmUtil.timestamp();
// if (now - moduleModified < 7 * 86400) {
// moduleInfo.timemodifiedReadable = moment(moduleModified * 1000).fromNow();
// } else {
// moduleInfo.timemodifiedReadable = moment(moduleModified * 1000).calendar();
// }
// } else {
// moduleInfo.timemodifiedReadable = "";
// }
// }));
promises.push(this.prefetchDelegate.getModuleStatus(module, courseId).then((moduleStatus) => { promises.push(this.prefetchDelegate.getModuleStatus(module, courseId).then((moduleStatus) => {
moduleInfo.status = moduleStatus; moduleInfo.status = moduleStatus;
@ -573,7 +566,7 @@ export class CoreCourseHelperProvider {
* @param {any} section Section. * @param {any} section Section.
* @return {string} Section download ID. * @return {string} Section download ID.
*/ */
getSectionDownloadId(section: any) : string { getSectionDownloadId(section: any): string {
return 'Section-' + section.id; return 'Section-' + section.id;
} }
@ -586,11 +579,11 @@ export class CoreCourseHelperProvider {
* @param {number} [sectionId] Section the module belongs to. 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. * @return {Promise<void>} Promise resolved when done.
*/ */
navigateToModule(moduleId: number, siteId?: string, courseId?: number, sectionId?: number) : Promise<void> { navigateToModule(moduleId: number, siteId?: string, courseId?: number, sectionId?: number): Promise<void> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
let modal = this.domUtils.showModalLoading(), const modal = this.domUtils.showModalLoading();
promise, let promise,
site: CoreSite; site: CoreSite;
if (courseId && sectionId) { if (courseId && sectionId) {
@ -619,7 +612,7 @@ export class CoreCourseHelperProvider {
return this.courseProvider.getModule(moduleId, courseId, sectionId, false, false, siteId); return this.courseProvider.getModule(moduleId, courseId, sectionId, false, false, siteId);
}).then((module) => { }).then((module) => {
const params = { const params = {
course: {id: courseId}, course: { id: courseId },
module: module, module: module,
sectionId: sectionId sectionId: sectionId
}; };
@ -649,12 +642,12 @@ export class CoreCourseHelperProvider {
* @param {number} courseId The course ID of the module. * @param {number} courseId The course ID of the module.
* @param {number} [sectionId] The section ID of the module. * @param {number} [sectionId] The section ID of the module.
*/ */
openModule(navCtrl: NavController, module: any, courseId: number, sectionId?: number) : void { openModule(navCtrl: NavController, module: any, courseId: number, sectionId?: number): void {
if (!module.handlerData) { if (!module.handlerData) {
module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, sectionId); module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, sectionId);
} }
module.handlerData.action(new Event('click'), navCtrl, module, courseId, {animate: false}); module.handlerData.action(new Event('click'), navCtrl, module, courseId, { animate: false });
} }
/** /**
@ -679,35 +672,35 @@ export class CoreCourseHelperProvider {
// First of all, mark the course as being downloaded. // First of all, mark the course as being downloaded.
this.courseDwnPromises[siteId][course.id] = this.courseProvider.setCourseStatus(course.id, CoreConstants.DOWNLOADING, this.courseDwnPromises[siteId][course.id] = this.courseProvider.setCourseStatus(course.id, CoreConstants.DOWNLOADING,
siteId).then(() => { siteId).then(() => {
let promises = [], const promises = [];
allSectionsSection = sections[0]; let allSectionsSection = sections[0];
// Prefetch all the sections. If the first section is "All sections", use it. Otherwise, use a fake "All sections". // 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) { if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) {
allSectionsSection = {id: CoreCourseProvider.ALL_SECTIONS_ID}; allSectionsSection = { id: CoreCourseProvider.ALL_SECTIONS_ID };
}
promises.push(this.prefetchSection(allSectionsSection, course.id, sections));
// Prefetch course options.
courseHandlers.forEach((handler) => {
if (handler.prefetch) {
promises.push(handler.prefetch(course));
} }
}); promises.push(this.prefetchSection(allSectionsSection, course.id, sections));
return this.utils.allPromises(promises); // Prefetch course options.
}).then(() => { courseHandlers.forEach((handler) => {
// Download success, mark the course as downloaded. if (handler.prefetch) {
return this.courseProvider.setCourseStatus(course.id, CoreConstants.DOWNLOADED, siteId); promises.push(handler.prefetch(course));
}).catch((error) => { }
// Error, restore previous status. });
return this.courseProvider.setCoursePreviousStatus(course.id, siteId).then(() => {
return Promise.reject(error); 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];
}); });
}).finally(() => {
delete this.courseDwnPromises[siteId][course.id];
});
return this.courseDwnPromises[siteId][course.id]; return this.courseDwnPromises[siteId][course.id];
} }
@ -723,11 +716,12 @@ export class CoreCourseHelperProvider {
* @param {boolean} [refresh] True if refreshing, false otherwise. * @param {boolean} [refresh] True if refreshing, false otherwise.
* @return {Promise<any>} Promise resolved when downloaded. * @return {Promise<any>} Promise resolved when downloaded.
*/ */
prefetchModule(handler: any, module: any, size: any, courseId: number, refresh?: boolean) : Promise<any> { prefetchModule(handler: any, module: any, size: any, courseId: number, refresh?: boolean): Promise<any> {
// Show confirmation if needed. // Show confirmation if needed.
return this.domUtils.confirmDownloadSize(size).then(() => { return this.domUtils.confirmDownloadSize(size).then(() => {
// Invalidate content if refreshing and download the data. // Invalidate content if refreshing and download the data.
let promise = refresh ? handler.invalidateContent(module.id, courseId) : Promise.resolve(); const promise = refresh ? handler.invalidateContent(module.id, courseId) : Promise.resolve();
return promise.catch(() => { return promise.catch(() => {
// Ignore errors. // Ignore errors.
}).then(() => { }).then(() => {
@ -745,7 +739,7 @@ export class CoreCourseHelperProvider {
* @param {any[]} [sections] List of sections. Used when downloading all the sections. * @param {any[]} [sections] List of sections. Used when downloading all the sections.
* @return {Promise<any>} Promise resolved when the prefetch is finished. * @return {Promise<any>} Promise resolved when the prefetch is finished.
*/ */
prefetchSection(section: any, courseId: number, sections?: any[]) : Promise<any> { prefetchSection(section: any, courseId: number, sections?: any[]): Promise<any> {
if (section.id != CoreCourseProvider.ALL_SECTIONS_ID) { if (section.id != CoreCourseProvider.ALL_SECTIONS_ID) {
// Download only this section. // Download only this section.
return this.prefetchSingleSectionIfNeeded(section, courseId).then(() => { return this.prefetchSingleSectionIfNeeded(section, courseId).then(() => {
@ -754,8 +748,8 @@ export class CoreCourseHelperProvider {
}); });
} else { } else {
// Download all the sections except "All sections". // Download all the sections except "All sections".
let promises = [], const promises = [];
allSectionsStatus; let allSectionsStatus;
section.isDownloading = true; section.isDownloading = true;
sections.forEach((section) => { sections.forEach((section) => {
@ -789,7 +783,7 @@ export class CoreCourseHelperProvider {
* @param {number} courseId Course ID the section belongs to. * @param {number} courseId Course ID the section belongs to.
* @return {Promise<any>} Promise resolved when the section is prefetched. * @return {Promise<any>} Promise resolved when the section is prefetched.
*/ */
protected prefetchSingleSectionIfNeeded(section: any, courseId: number) : Promise<any> { protected prefetchSingleSectionIfNeeded(section: any, courseId: number): Promise<any> {
if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) { if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) {
return Promise.resolve(); return Promise.resolve();
} }
@ -802,9 +796,11 @@ export class CoreCourseHelperProvider {
// Section is downloaded or not downloadable, nothing to do. // Section is downloaded or not downloadable, nothing to do.
return; return;
} }
return this.prefetchSingleSection(section, result, courseId); return this.prefetchSingleSection(section, result, courseId);
}, (error) => { }, (error) => {
section.isDownloading = false; section.isDownloading = false;
return Promise.reject(error); return Promise.reject(error);
}); });
} }
@ -818,7 +814,7 @@ export class CoreCourseHelperProvider {
* @param {number} courseId Course ID the section belongs to. * @param {number} courseId Course ID the section belongs to.
* @return {Promise<any>} Promise resolved when the section has been prefetched. * @return {Promise<any>} Promise resolved when the section has been prefetched.
*/ */
protected prefetchSingleSection(section: any, result: any, courseId: number) { protected prefetchSingleSection(section: any, result: any, courseId: number): Promise<any> {
if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) { if (section.id == CoreCourseProvider.ALL_SECTIONS_ID) {
return Promise.resolve(); return Promise.resolve();
} }
@ -829,14 +825,13 @@ export class CoreCourseHelperProvider {
} }
// We only download modules with status notdownloaded, downloading or outdated. // We only download modules with status notdownloaded, downloading or outdated.
let modules = result[CoreConstants.OUTDATED].concat(result[CoreConstants.NOT_DOWNLOADED]) const modules = result[CoreConstants.OUTDATED].concat(result[CoreConstants.NOT_DOWNLOADED])
.concat(result[CoreConstants.DOWNLOADING]), .concat(result[CoreConstants.DOWNLOADING]),
downloadId = this.getSectionDownloadId(section); downloadId = this.getSectionDownloadId(section);
section.isDownloading = true; section.isDownloading = true;
// We prefetch all the modules to prevent incoeherences in the download count // Prefetch all modules to prevent incoeherences in download count and to download stale data not marked as outdated.
// and also to download stale data that might not be marked as outdated.
return this.prefetchDelegate.prefetchModules(downloadId, modules, courseId, (data) => { return this.prefetchDelegate.prefetchModules(downloadId, modules, courseId, (data) => {
section.count = data.count; section.count = data.count;
section.total = data.total; section.total = data.total;
@ -849,12 +844,12 @@ export class CoreCourseHelperProvider {
* @param {any} section Section to check. * @param {any} section Section to check.
* @return {boolean} Whether the section has content. * @return {boolean} Whether the section has content.
*/ */
sectionHasContent(section: any) : boolean { sectionHasContent(section: any): boolean {
if (section.id == CoreCourseProvider.ALL_SECTIONS_ID || section.hiddenbynumsections) { if (section.id == CoreCourseProvider.ALL_SECTIONS_ID || section.hiddenbynumsections) {
return false; return false;
} }
return (typeof section.availabilityinfo != 'undefined' && section.availabilityinfo != '') || return (typeof section.availabilityinfo != 'undefined' && section.availabilityinfo != '') ||
section.summary != '' || (section.modules && section.modules.length > 0); section.summary != '' || (section.modules && section.modules.length > 0);
} }
} }

View File

@ -43,7 +43,7 @@ export interface CoreCourseModuleHandler extends CoreDelegateHandler {
* @return {any} The component to use, undefined if not found. * @return {any} The component to use, undefined if not found.
*/ */
getMainComponent(course: any, module: any): any; getMainComponent(course: any, module: any): any;
}; }
/** /**
* Data needed to render the module in course contents. * Data needed to render the module in course contents.
@ -134,7 +134,7 @@ export interface CoreCourseModuleHandlerButton {
* @param {number} courseId The course ID. * @param {number} courseId The course ID.
*/ */
action(event: Event, navCtrl: NavController, module: any, courseId: number): void; action(event: Event, navCtrl: NavController, module: any, courseId: number): void;
}; }
/** /**
* Delegate to register module handlers. * Delegate to register module handlers.
@ -146,7 +146,7 @@ export class CoreCourseModuleDelegate extends CoreDelegate {
protected featurePrefix = '$mmCourseDelegate_'; protected featurePrefix = '$mmCourseDelegate_';
constructor(loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider, constructor(loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider,
protected courseProvider: CoreCourseProvider) { protected courseProvider: CoreCourseProvider) {
super('CoreCourseModuleDelegate', loggerProvider, sitesProvider, eventsProvider); super('CoreCourseModuleDelegate', loggerProvider, sitesProvider, eventsProvider);
} }
@ -158,9 +158,9 @@ export class CoreCourseModuleDelegate extends CoreDelegate {
* @return {any} The component to use, undefined if not found. * @return {any} The component to use, undefined if not found.
*/ */
getMainComponent?(course: any, module: any): any { getMainComponent?(course: any, module: any): any {
let handler = this.enabledHandlers[module.modname]; const handler = this.enabledHandlers[module.modname];
if (handler && handler.getMainComponent) { if (handler && handler.getMainComponent) {
let component = handler.getMainComponent(course, module); const component = handler.getMainComponent(course, module);
if (component) { if (component) {
return component; return component;
} }
@ -182,11 +182,11 @@ export class CoreCourseModuleDelegate extends CoreDelegate {
} }
// Return the default data. // Return the default data.
let defaultData: CoreCourseModuleHandlerData = { const defaultData: CoreCourseModuleHandlerData = {
icon: this.courseProvider.getModuleIconSrc(module.modname), icon: this.courseProvider.getModuleIconSrc(module.modname),
title: module.name, title: module.name,
class: 'core-course-default-handler core-course-module-' + module.modname + '-handler', class: 'core-course-default-handler core-course-module-' + module.modname + '-handler',
action: (event: Event, navCtrl: NavController, module: any, courseId: number, options?: NavOptions) => { action: (event: Event, navCtrl: NavController, module: any, courseId: number, options?: NavOptions): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -198,7 +198,7 @@ export class CoreCourseModuleDelegate extends CoreDelegate {
defaultData.buttons = [{ defaultData.buttons = [{
icon: 'open', icon: 'open',
label: 'core.openinbrowser', label: 'core.openinbrowser',
action: (e: Event) => { action: (e: Event): void => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(module.url); this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(module.url);

View File

@ -210,8 +210,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
// Promises for check updates, to prevent performing the same request twice at the same time. // Promises for check updates, to prevent performing the same request twice at the same time.
protected courseUpdatesPromises: { [s: string]: { [s: string]: Promise<any> } } = {}; protected courseUpdatesPromises: { [s: string]: { [s: string]: Promise<any> } } = {};
// Promises and observables for prefetching, to prevent downloading the same section twice at the same time // Promises and observables for prefetching, to prevent downloading same section twice at the same time and notify progress.
// and notify the progress of the download.
protected prefetchData: { protected prefetchData: {
[s: string]: { [s: string]: {
[s: string]: { [s: string]: {
@ -222,9 +221,10 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
} }
} = {}; } = {};
constructor(loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, protected eventsProvider: CoreEventsProvider, constructor(loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
private courseProvider: CoreCourseProvider, private filepoolProvider: CoreFilepoolProvider, private courseProvider: CoreCourseProvider, private filepoolProvider: CoreFilepoolProvider,
private timeUtils: CoreTimeUtilsProvider, private utils: CoreUtilsProvider, private fileProvider: CoreFileProvider) { private timeUtils: CoreTimeUtilsProvider, private fileProvider: CoreFileProvider,
protected eventsProvider: CoreEventsProvider) {
super('CoreCourseModulePrefetchDelegate', loggerProvider, sitesProvider); super('CoreCourseModulePrefetchDelegate', loggerProvider, sitesProvider);
this.sitesProvider.createTableFromSchema(this.checkUpdatesTableSchema); this.sitesProvider.createTableFromSchema(this.checkUpdatesTableSchema);
@ -277,10 +277,10 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* @return {Promise<{toCheck: any[], cannotUse: any[]}>} Promise resolved with the lists. * @return {Promise<{toCheck: any[], cannotUse: any[]}>} Promise resolved with the lists.
*/ */
protected createToCheckList(modules: any[], courseId: number): Promise<{ toCheck: any[], cannotUse: any[] }> { protected createToCheckList(modules: any[], courseId: number): Promise<{ toCheck: any[], cannotUse: any[] }> {
let result = { const result = {
toCheck: [], toCheck: [],
cannotUse: [] cannotUse: []
}, },
promises = []; promises = [];
modules.forEach((module) => { modules.forEach((module) => {
@ -341,6 +341,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
return handler.determineStatus(module, status, canCheck); return handler.determineStatus(module, status, canCheck);
} }
} }
return status; return status;
} }
@ -358,7 +359,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
} }
// Check if there's already a getCourseUpdates in progress. // Check if there's already a getCourseUpdates in progress.
let id = <string>Md5.hashAsciiStr(courseId + '#' + JSON.stringify(modules)), const id = <string> Md5.hashAsciiStr(courseId + '#' + JSON.stringify(modules)),
siteId = this.sitesProvider.getCurrentSiteId(); siteId = this.sitesProvider.getCurrentSiteId();
if (this.courseUpdatesPromises[siteId] && this.courseUpdatesPromises[siteId][id]) { if (this.courseUpdatesPromises[siteId] && this.courseUpdatesPromises[siteId][id]) {
@ -369,7 +370,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
} }
this.courseUpdatesPromises[siteId][id] = this.createToCheckList(modules, courseId).then((data) => { this.courseUpdatesPromises[siteId][id] = this.createToCheckList(modules, courseId).then((data) => {
let result = {}; const result = {};
// Mark as false the modules that cannot use check updates WS. // Mark as false the modules that cannot use check updates WS.
data.cannotUse.forEach((module) => { data.cannotUse.forEach((module) => {
@ -383,10 +384,10 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
// Get the site, maybe the user changed site. // Get the site, maybe the user changed site.
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let params = { const params = {
courseid: courseId, courseid: courseId,
tocheck: data.toCheck tocheck: data.toCheck
}, },
preSets: CoreSiteWSPreSets = { preSets: CoreSiteWSPreSets = {
cacheKey: this.getCourseUpdatesCacheKey(courseId), cacheKey: this.getCourseUpdatesCacheKey(courseId),
emergencyCache: false, // If downloaded data has changed and offline, just fail. See MOBILE-2085. emergencyCache: false, // If downloaded data has changed and offline, just fail. See MOBILE-2085.
@ -399,7 +400,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
} }
// Store the last execution of the check updates call. // Store the last execution of the check updates call.
let entry = { const entry = {
courseId: courseId, courseId: courseId,
time: this.timeUtils.timestamp() time: this.timeUtils.timestamp()
}; };
@ -407,8 +408,8 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
return this.treatCheckUpdatesResult(data.toCheck, response, result); return this.treatCheckUpdatesResult(data.toCheck, response, result);
}).catch((error) => { }).catch((error) => {
// Cannot get updates. Get the cached entries but discard the modules with a download time higher // Cannot get updates.
// than the last execution of check updates. // Get cached entries but discard modules with a download time higher than the last execution of check updates.
return site.getDb().getRecord(this.CHECK_UPDATES_TIMES_TABLE, { courseId: courseId }).then((entry) => { return site.getDb().getRecord(this.CHECK_UPDATES_TIMES_TABLE, { courseId: courseId }).then((entry) => {
preSets.getCacheUsingCacheKey = true; preSets.getCacheUsingCacheKey = true;
preSets.omitExpires = true; preSets.omitExpires = true;
@ -502,9 +503,9 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* to calculate the total size. * to calculate the total size.
*/ */
getModuleDownloadSize(module: any, courseId: number, single?: boolean): Promise<{ size: number, total: boolean }> { getModuleDownloadSize(module: any, courseId: number, single?: boolean): Promise<{ size: number, total: boolean }> {
const handler = this.getPrefetchHandlerFor(module);
let downloadSize, let downloadSize,
packageId, packageId;
handler = this.getPrefetchHandlerFor(module);
// Check if the module has a prefetch handler. // Check if the module has a prefetch handler.
if (handler) { if (handler) {
@ -526,6 +527,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
if (cachedSize) { if (cachedSize) {
return cachedSize; return cachedSize;
} }
return Promise.reject(error); return Promise.reject(error);
}); });
}); });
@ -542,10 +544,10 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* @return {Promise<number>} Promise resolved with the size. * @return {Promise<number>} Promise resolved with the size.
*/ */
getModuleDownloadedSize(module: any, courseId: number): Promise<number> { getModuleDownloadedSize(module: any, courseId: number): Promise<number> {
const handler = this.getPrefetchHandlerFor(module);
let downloadedSize, let downloadedSize,
packageId, packageId,
promise, promise;
handler = this.getPrefetchHandlerFor(module);
// Check if the module has a prefetch handler. // Check if the module has a prefetch handler.
if (handler) { if (handler) {
@ -566,9 +568,9 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
} else { } else {
// Handler doesn't implement it, get the module files and check if they're downloaded. // Handler doesn't implement it, get the module files and check if they're downloaded.
promise = this.getModuleFiles(module, courseId).then((files) => { promise = this.getModuleFiles(module, courseId).then((files) => {
let siteId = this.sitesProvider.getCurrentSiteId(), const siteId = this.sitesProvider.getCurrentSiteId(),
promises = [], promises = [];
size = 0; let size = 0;
// Retrieve file size if it's downloaded. // Retrieve file size if it's downloaded.
files.forEach((file) => { files.forEach((file) => {
@ -641,15 +643,15 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* @return {Promise<string>} Promise resolved with the status. * @return {Promise<string>} Promise resolved with the status.
*/ */
getModuleStatus(module: any, courseId: number, updates?: any, refresh?: boolean, sectionId?: number): Promise<string> { getModuleStatus(module: any, courseId: number, updates?: any, refresh?: boolean, sectionId?: number): Promise<string> {
let handler = this.getPrefetchHandlerFor(module), const handler = this.getPrefetchHandlerFor(module),
siteId = this.sitesProvider.getCurrentSiteId(), siteId = this.sitesProvider.getCurrentSiteId(),
canCheck = this.canCheckUpdates(); canCheck = this.canCheckUpdates();
if (handler) { if (handler) {
// Check if the status is cached. // Check if the status is cached.
let component = handler.component, const component = handler.component,
packageId = this.filepoolProvider.getPackageId(component, module.id), packageId = this.filepoolProvider.getPackageId(component, module.id);
status = this.statusCache.getValue(packageId, 'status'), let status = this.statusCache.getValue(packageId, 'status'),
updateStatus = true, updateStatus = true,
promise; promise;
@ -696,6 +698,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
// Has updates, mark the module as outdated. // Has updates, mark the module as outdated.
status = CoreConstants.OUTDATED; status = CoreConstants.OUTDATED;
return this.filepoolProvider.storePackageStatus(siteId, component, module.id, status).catch(() => { return this.filepoolProvider.storePackageStatus(siteId, component, module.id, status).catch(() => {
// Ignore errors. // Ignore errors.
}).then(() => { }).then(() => {
@ -704,11 +707,13 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
}).catch(() => { }).catch(() => {
// Error checking if module has updates. // Error checking if module has updates.
const status = this.statusCache.getValue(packageId, 'status', true); const status = this.statusCache.getValue(packageId, 'status', true);
return this.determineModuleStatus(module, status, canCheck); return this.determineModuleStatus(module, status, canCheck);
}); });
}, () => { }, () => {
// Error getting updates, show the stored status. // Error getting updates, show the stored status.
updateStatus = false; updateStatus = false;
return currentStatus; return currentStatus;
}); });
}); });
@ -716,6 +721,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
if (updateStatus) { if (updateStatus) {
this.updateStatusCache(status, courseId, component, module.id, sectionId); this.updateStatusCache(status, courseId, component, module.id, sectionId);
} }
return this.determineModuleStatus(module, status, canCheck); return this.determineModuleStatus(module, status, canCheck);
}); });
} }
@ -741,11 +747,11 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* - CoreConstants.OUTDATED (any[]) Modules with state OUTDATED. * - CoreConstants.OUTDATED (any[]) Modules with state OUTDATED.
*/ */
getModulesStatus(modules: any[], courseId: number, sectionId?: number, refresh?: boolean): any { getModulesStatus(modules: any[], courseId: number, sectionId?: number, refresh?: boolean): any {
let promises = [], const promises = [],
status = CoreConstants.NOT_DOWNLOADABLE,
result: any = { result: any = {
total: 0 total: 0
}; };
let status = CoreConstants.NOT_DOWNLOADABLE;
// Init result. // Init result.
result[CoreConstants.NOT_DOWNLOADED] = []; result[CoreConstants.NOT_DOWNLOADED] = [];
@ -761,9 +767,9 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
modules.forEach((module) => { modules.forEach((module) => {
// Check if the module has a prefetch handler. // Check if the module has a prefetch handler.
let handler = this.getPrefetchHandlerFor(module); const handler = this.getPrefetchHandlerFor(module);
if (handler) { if (handler) {
let packageId = this.filepoolProvider.getPackageId(handler.component, module.id); const packageId = this.filepoolProvider.getPackageId(handler.component, module.id);
promises.push(this.getModuleStatus(module, courseId, updates, refresh).then((modStatus) => { promises.push(this.getModuleStatus(module, courseId, updates, refresh).then((modStatus) => {
if (modStatus != CoreConstants.NOT_DOWNLOADABLE) { if (modStatus != CoreConstants.NOT_DOWNLOADABLE) {
@ -793,6 +799,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
return Promise.all(promises).then(() => { return Promise.all(promises).then(() => {
result.status = status; result.status = status;
return result; return result;
}); });
}); });
@ -806,12 +813,12 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* @return {Promise<{status: string, downloadTime?: number}>} Promise resolved with the data. * @return {Promise<{status: string, downloadTime?: number}>} Promise resolved with the data.
*/ */
protected getModuleStatusAndDownloadTime(module: any, courseId: number): Promise<{ status: string, downloadTime?: number }> { protected getModuleStatusAndDownloadTime(module: any, courseId: number): Promise<{ status: string, downloadTime?: number }> {
let handler = this.getPrefetchHandlerFor(module), const handler = this.getPrefetchHandlerFor(module),
siteId = this.sitesProvider.getCurrentSiteId(); siteId = this.sitesProvider.getCurrentSiteId();
if (handler) { if (handler) {
// Get the status from the cache. // Get the status from the cache.
let packageId = this.filepoolProvider.getPackageId(handler.component, module.id), const packageId = this.filepoolProvider.getPackageId(handler.component, module.id),
status = this.statusCache.getValue(packageId, 'status'); status = this.statusCache.getValue(packageId, 'status');
if (typeof status != 'undefined' && status != CoreConstants.DOWNLOADED) { if (typeof status != 'undefined' && status != CoreConstants.DOWNLOADED) {
@ -873,7 +880,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* @return {Promise<any>} Promise resolved when modules are invalidated. * @return {Promise<any>} Promise resolved when modules are invalidated.
*/ */
invalidateModules(modules: any[], courseId: number): Promise<any> { invalidateModules(modules: any[], courseId: number): Promise<any> {
let promises = []; const promises = [];
modules.forEach((module) => { modules.forEach((module) => {
const handler = this.getPrefetchHandlerFor(module); const handler = this.getPrefetchHandlerFor(module);
@ -914,6 +921,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
*/ */
isBeingDownloaded(id: string): boolean { isBeingDownloaded(id: string): boolean {
const siteId = this.sitesProvider.getCurrentSiteId(); const siteId = this.sitesProvider.getCurrentSiteId();
return !!(this.prefetchData[siteId] && this.prefetchData[siteId][id]); return !!(this.prefetchData[siteId] && this.prefetchData[siteId][id]);
} }
@ -925,11 +933,11 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* @return {Promise<boolean>} Promise resolved with true if downloadable, false otherwise. * @return {Promise<boolean>} Promise resolved with true if downloadable, false otherwise.
*/ */
isModuleDownloadable(module: any, courseId: number): Promise<boolean> { isModuleDownloadable(module: any, courseId: number): Promise<boolean> {
let handler = this.getPrefetchHandlerFor(module); const handler = this.getPrefetchHandlerFor(module);
if (handler) { if (handler) {
if (typeof handler.isDownloadable == 'function') { if (typeof handler.isDownloadable == 'function') {
let packageId = this.filepoolProvider.getPackageId(handler.component, module.id), const packageId = this.filepoolProvider.getPackageId(handler.component, module.id),
downloadable = this.statusCache.getValue(packageId, 'downloadable'); downloadable = this.statusCache.getValue(packageId, 'downloadable');
if (typeof downloadable != 'undefined') { if (typeof downloadable != 'undefined') {
@ -961,7 +969,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* @return {Promise<boolean>} Promise resolved with boolean: whether the module has updates. * @return {Promise<boolean>} Promise resolved with boolean: whether the module has updates.
*/ */
moduleHasUpdates(module: any, courseId: number, updates: any): Promise<boolean> { moduleHasUpdates(module: any, courseId: number, updates: any): Promise<boolean> {
let handler = this.getPrefetchHandlerFor(module), const handler = this.getPrefetchHandlerFor(module),
moduleUpdates = updates[module.id]; moduleUpdates = updates[module.id];
if (handler && handler.hasUpdates) { if (handler && handler.hasUpdates) {
@ -1000,6 +1008,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
if (handler) { if (handler) {
return handler.prefetch(module, courseId, single); return handler.prefetch(module, courseId, single);
} }
return Promise.resolve(); return Promise.resolve();
} }
@ -1023,22 +1032,21 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
if (onProgress) { if (onProgress) {
currentData.subscriptions.push(currentData.observable.subscribe(onProgress)); currentData.subscriptions.push(currentData.observable.subscribe(onProgress));
} }
return currentData.promise; return currentData.promise;
} }
let promises = [], let count = 0;
count = 0, const promises = [],
total = modules.length, total = modules.length,
moduleIds = modules.map((module) => { moduleIds = modules.map((module) => {
return module.id; return module.id;
}); }),
prefetchData = {
// Initialize the prefetch data. observable: new BehaviorSubject<CoreCourseModulesProgress>({ count: count, total: total }),
const prefetchData = { promise: undefined,
observable: new BehaviorSubject<CoreCourseModulesProgress>({ count: count, total: total }), subscriptions: []
promise: undefined, };
subscriptions: []
};
if (onProgress) { if (onProgress) {
prefetchData.observable.subscribe(onProgress); prefetchData.observable.subscribe(onProgress);
@ -1054,7 +1062,7 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
} }
return handler.prefetch(module, courseId).then(() => { return handler.prefetch(module, courseId).then(() => {
let index = moduleIds.indexOf(id); const index = moduleIds.indexOf(id);
if (index > -1) { if (index > -1) {
// It's one of the modules we were expecting to download. // It's one of the modules we were expecting to download.
moduleIds.splice(index, 1); moduleIds.splice(index, 1);
@ -1092,9 +1100,9 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* @return {Promise<void>} Promise resolved when done. * @return {Promise<void>} Promise resolved when done.
*/ */
removeModuleFiles(module: any, courseId: number): Promise<void> { removeModuleFiles(module: any, courseId: number): Promise<void> {
let handler = this.getPrefetchHandlerFor(module), const handler = this.getPrefetchHandlerFor(module),
siteId = this.sitesProvider.getCurrentSiteId(), siteId = this.sitesProvider.getCurrentSiteId();
promise; let promise;
if (handler && handler.removeFiles) { if (handler && handler.removeFiles) {
// Handler implements a method to remove the files, use it. // Handler implements a method to remove the files, use it.
@ -1102,12 +1110,13 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
} else { } else {
// No method to remove files, use get files to try to remove the files. // No method to remove files, use get files to try to remove the files.
promise = this.getModuleFiles(module, courseId).then((files) => { promise = this.getModuleFiles(module, courseId).then((files) => {
let promises = []; const promises = [];
files.forEach((file) => { files.forEach((file) => {
promises.push(this.filepoolProvider.removeFileByUrl(siteId, file.url || file.fileurl).catch(() => { promises.push(this.filepoolProvider.removeFileByUrl(siteId, file.url || file.fileurl).catch(() => {
// Ignore errors. // Ignore errors.
})); }));
}); });
return Promise.all(promises); return Promise.all(promises);
}); });
} }
@ -1180,10 +1189,11 @@ export class CoreCourseModulePrefetchDelegate extends CoreDelegate {
* @param {string|number} [componentId] An ID to use in conjunction with the component. * @param {string|number} [componentId] An ID to use in conjunction with the component.
* @param {number} [sectionId] Section ID of the module. * @param {number} [sectionId] Section ID of the module.
*/ */
updateStatusCache(status: string, courseId: number, component: string, componentId?: string | number, sectionId?: number): void { updateStatusCache(status: string, courseId: number, component: string, componentId?: string | number, sectionId?: number)
let notify, : void {
packageId = this.filepoolProvider.getPackageId(component, componentId), const packageId = this.filepoolProvider.getPackageId(component, componentId),
cachedStatus = this.statusCache.getValue(packageId, 'status', true); cachedStatus = this.statusCache.getValue(packageId, 'status', true);
let notify;
// If the status has changed, notify that the section has changed. // If the status has changed, notify that the section has changed.
notify = typeof cachedStatus != 'undefined' && cachedStatus !== status; notify = typeof cachedStatus != 'undefined' && cachedStatus !== status;

View File

@ -154,7 +154,7 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
protected featurePrefix = '$mmCoursesDelegate_'; protected featurePrefix = '$mmCoursesDelegate_';
constructor(loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider, constructor(loggerProvider: CoreLoggerProvider, protected sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
protected eventsProvider: CoreEventsProvider, private coursesProvider: CoreCoursesProvider) { protected eventsProvider: CoreEventsProvider, private coursesProvider: CoreCoursesProvider) {
super('CoreCourseOptionsDelegate', loggerProvider, sitesProvider, eventsProvider); super('CoreCourseOptionsDelegate', loggerProvider, sitesProvider, eventsProvider);
eventsProvider.on(CoreEventsProvider.LOGOUT, () => { eventsProvider.on(CoreEventsProvider.LOGOUT, () => {
@ -194,7 +194,7 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
clearAndInvalidateCoursesOptions(courseId?: number): Promise<any> { clearAndInvalidateCoursesOptions(courseId?: number): Promise<any> {
let promises = []; const promises = [];
this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED); this.eventsProvider.trigger(CoreCoursesProvider.EVENT_MY_COURSES_REFRESHED);
@ -208,7 +208,7 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
promises.push(this.coursesProvider.invalidateUserNavigationOptions()); promises.push(this.coursesProvider.invalidateUserNavigationOptions());
promises.push(this.coursesProvider.invalidateUserAdministrationOptions()); promises.push(this.coursesProvider.invalidateUserAdministrationOptions());
for (let cId in this.coursesHandlers) { for (const cId in this.coursesHandlers) {
promises.push(this.invalidateCourseHandlers(parseInt(cId, 10))); promises.push(this.invalidateCourseHandlers(parseInt(cId, 10)));
} }
} }
@ -229,7 +229,7 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
* @return {Promise<CoreCourseOptionsHandler[]>} Promise resolved with array of handlers. * @return {Promise<CoreCourseOptionsHandler[]>} Promise resolved with array of handlers.
*/ */
protected getHandlersForAccess(courseId: number, refresh: boolean, accessData: any, navOptions?: any, protected getHandlersForAccess(courseId: number, refresh: boolean, accessData: any, navOptions?: any,
admOptions?: any): Promise<CoreCourseOptionsHandler[]> { admOptions?: any): Promise<CoreCourseOptionsHandler[]> {
// If the handlers aren't loaded, do not refresh. // If the handlers aren't loaded, do not refresh.
if (!this.loaded[courseId]) { if (!this.loaded[courseId]) {
@ -264,10 +264,10 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
* @return {Promise<CoreCourseOptionsHandlerToDisplay[]>} Promise resolved with array of handlers. * @return {Promise<CoreCourseOptionsHandlerToDisplay[]>} Promise resolved with array of handlers.
*/ */
getHandlersToDisplay(course: any, refresh?: boolean, isGuest?: boolean, navOptions?: any, admOptions?: any): getHandlersToDisplay(course: any, refresh?: boolean, isGuest?: boolean, navOptions?: any, admOptions?: any):
Promise<CoreCourseOptionsHandlerToDisplay[]> { Promise<CoreCourseOptionsHandlerToDisplay[]> {
course.id = parseInt(course.id, 10); course.id = parseInt(course.id, 10);
let accessData = { const accessData = {
type: isGuest ? CoreCourseProvider.ACCESS_GUEST : CoreCourseProvider.ACCESS_DEFAULT type: isGuest ? CoreCourseProvider.ACCESS_GUEST : CoreCourseProvider.ACCESS_DEFAULT
}; };
@ -282,9 +282,9 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
// Call getHandlersForAccess to make sure the handlers have been loaded. // Call getHandlersForAccess to make sure the handlers have been loaded.
return this.getHandlersForAccess(course.id, refresh, accessData, course.navOptions, course.admOptions); return this.getHandlersForAccess(course.id, refresh, accessData, course.navOptions, course.admOptions);
}).then(() => { }).then(() => {
let handlersToDisplay: CoreCourseOptionsHandlerToDisplay[] = [], const handlersToDisplay: CoreCourseOptionsHandlerToDisplay[] = [],
promises = [], promises = [];
promise; let promise;
this.coursesHandlers[course.id].enabledHandlers.forEach((handler) => { this.coursesHandlers[course.id].enabledHandlers.forEach((handler) => {
if (handler.shouldDisplayForCourse) { if (handler.shouldDisplayForCourse) {
@ -342,9 +342,10 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
*/ */
hasHandlersForDefault(courseId: number, refresh?: boolean, navOptions?: any, admOptions?: any): Promise<boolean> { hasHandlersForDefault(courseId: number, refresh?: boolean, navOptions?: any, admOptions?: any): Promise<boolean> {
// Default access. // Default access.
let accessData = { const accessData = {
type: CoreCourseProvider.ACCESS_DEFAULT type: CoreCourseProvider.ACCESS_DEFAULT
}; };
return this.getHandlersForAccess(courseId, refresh, accessData, navOptions, admOptions).then((handlers) => { return this.getHandlersForAccess(courseId, refresh, accessData, navOptions, admOptions).then((handlers) => {
return !!(handlers && handlers.length); return !!(handlers && handlers.length);
}); });
@ -361,9 +362,10 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
*/ */
hasHandlersForGuest(courseId: number, refresh?: boolean, navOptions?: any, admOptions?: any): Promise<boolean> { hasHandlersForGuest(courseId: number, refresh?: boolean, navOptions?: any, admOptions?: any): Promise<boolean> {
// Guest access. // Guest access.
var accessData = { const accessData = {
type: CoreCourseProvider.ACCESS_GUEST type: CoreCourseProvider.ACCESS_GUEST
}; };
return this.getHandlersForAccess(courseId, refresh, accessData, navOptions, admOptions).then((handlers) => { return this.getHandlersForAccess(courseId, refresh, accessData, navOptions, admOptions).then((handlers) => {
return !!(handlers && handlers.length); return !!(handlers && handlers.length);
}); });
@ -376,7 +378,7 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
invalidateCourseHandlers(courseId: number): Promise<any> { invalidateCourseHandlers(courseId: number): Promise<any> {
let promises = [], const promises = [],
courseData = this.coursesHandlers[courseId]; courseData = this.coursesHandlers[courseId];
if (!courseData) { if (!courseData) {
@ -405,6 +407,7 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
if (!this.lastUpdateHandlersForCoursesStart[courseId]) { if (!this.lastUpdateHandlersForCoursesStart[courseId]) {
return true; return true;
} }
return time == this.lastUpdateHandlersForCoursesStart[courseId]; return time == this.lastUpdateHandlersForCoursesStart[courseId];
} }
@ -434,8 +437,8 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
updateData(siteId?: string): void { updateData(siteId?: string): void {
if (this.sitesProvider.getCurrentSiteId() === siteId) { if (this.sitesProvider.getCurrentSiteId() === siteId) {
// Update handlers for all courses. // Update handlers for all courses.
for (let courseId in this.coursesHandlers) { for (const courseId in this.coursesHandlers) {
let handler = this.coursesHandlers[courseId]; const handler = this.coursesHandlers[courseId];
this.updateHandlersForCourse(parseInt(courseId, 10), handler.access, handler.navOptions, handler.admOptions); this.updateHandlersForCourse(parseInt(courseId, 10), handler.access, handler.navOptions, handler.admOptions);
} }
} }
@ -452,15 +455,15 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
* @protected * @protected
*/ */
updateHandlersForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): Promise<any> { updateHandlersForCourse(courseId: number, accessData: any, navOptions?: any, admOptions?: any): Promise<any> {
let promises = [], const promises = [],
enabledForCourse = [], enabledForCourse = [],
siteId = this.sitesProvider.getCurrentSiteId(), siteId = this.sitesProvider.getCurrentSiteId(),
now = Date.now(); now = Date.now();
this.lastUpdateHandlersForCoursesStart[courseId] = now; this.lastUpdateHandlersForCoursesStart[courseId] = now;
for (let name in this.enabledHandlers) { for (const name in this.enabledHandlers) {
let handler = this.enabledHandlers[name]; const handler = this.enabledHandlers[name];
// Checks if the handler is enabled for the user. // Checks if the handler is enabled for the user.
promises.push(Promise.resolve(handler.isEnabledForCourse(courseId, accessData, navOptions, admOptions)) promises.push(Promise.resolve(handler.isEnabledForCourse(courseId, accessData, navOptions, admOptions))

View File

@ -37,9 +37,9 @@ export class CoreCoursesCourseListItemComponent implements OnInit {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
// Check if the user is enrolled in the course. // Check if the user is enrolled in the course.
return this.coursesProvider.getUserCourse(this.course.id).then(() => { this.coursesProvider.getUserCourse(this.course.id).then(() => {
this.course.isEnrolled = true; this.course.isEnrolled = true;
}).catch(() => { }).catch(() => {
this.course.isEnrolled = false; this.course.isEnrolled = false;
@ -75,9 +75,10 @@ export class CoreCoursesCourseListItemComponent implements OnInit {
/** /**
* Open a course. * Open a course.
*
* @param {any} course The course to open.
*/ */
openCourse(course) { openCourse(course: any): void {
this.navCtrl.push('CoreCoursesCoursePreviewPage', {course: course}); this.navCtrl.push('CoreCoursesCoursePreviewPage', {course: course});
} }
} }

View File

@ -58,7 +58,7 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy {
/** /**
* Component being initialized. * Component being initialized.
*/ */
ngOnInit() { ngOnInit(): void {
// Determine course prefetch icon. // Determine course prefetch icon.
this.courseHelper.getCourseStatusIcon(this.course.id).then((icon) => { this.courseHelper.getCourseStatusIcon(this.course.id).then((icon) => {
this.prefetchCourseData.prefetchCourseIcon = icon; this.prefetchCourseData.prefetchCourseIcon = icon;
@ -83,8 +83,10 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy {
/** /**
* Open a course. * Open a course.
*
* @param {any} course The course to open.
*/ */
openCourse(course) { openCourse(course: any): void {
this.courseFormatDelegate.openCourse(this.navCtrl, course); this.courseFormatDelegate.openCourse(this.navCtrl, course);
} }
@ -93,7 +95,7 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy {
* *
* @param {Event} e Click event. * @param {Event} e Click event.
*/ */
prefetchCourse(e: Event) { prefetchCourse(e: Event): void {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
@ -101,13 +103,13 @@ export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy {
if (!this.isDestroyed) { if (!this.isDestroyed) {
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
} }
}) });
} }
/** /**
* Component destroyed. * Component destroyed.
*/ */
ngOnDestroy() { ngOnDestroy(): void {
this.isDestroyed = true; this.isDestroyed = true;
if (this.courseStatusObserver) { if (this.courseStatusObserver) {

View File

@ -31,7 +31,7 @@ import * as moment from 'moment';
}) })
export class CoreCoursesOverviewEventsComponent implements OnChanges { export class CoreCoursesOverviewEventsComponent implements OnChanges {
@Input() events: any[]; // The events to render. @Input() events: any[]; // The events to render.
@Input() showCourse?: boolean|string; // Whether to show the course name. @Input() showCourse?: boolean | string; // Whether to show the course name.
@Input() canLoadMore?: boolean; // Whether more events can be loaded. @Input() canLoadMore?: boolean; // Whether more events can be loaded.
@Output() loadMore: EventEmitter<void>; // Notify that more events should be loaded. @Output() loadMore: EventEmitter<void>; // Notify that more events should be loaded.
@ -52,7 +52,7 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges {
/** /**
* Detect changes on input properties. * Detect changes on input properties.
*/ */
ngOnChanges(changes: {[name: string]: SimpleChange}) { ngOnChanges(changes: {[name: string]: SimpleChange}): void {
this.showCourse = this.utils.isTrueOrOne(this.showCourse); this.showCourse = this.utils.isTrueOrOne(this.showCourse);
if (changes.events) { if (changes.events) {
@ -65,8 +65,9 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges {
* *
* @param {number} start Number of days to start getting events from today. E.g. -1 will get events from yesterday. * @param {number} start Number of days to start getting events from today. E.g. -1 will get events from yesterday.
* @param {number} [end] Number of days after the start. * @param {number} [end] Number of days after the start.
* @return {any[]} Filtered events.
*/ */
protected filterEventsByTime(start: number, end?: number) { protected filterEventsByTime(start: number, end?: number): any[] {
start = moment().add(start, 'days').unix(); start = moment().add(start, 'days').unix();
end = typeof end != 'undefined' ? moment().add(end, 'days').unix() : end; end = typeof end != 'undefined' ? moment().add(end, 'days').unix() : end;
@ -78,6 +79,7 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges {
return start <= event.timesort; return start <= event.timesort;
}).map((event) => { }).map((event) => {
event.iconUrl = this.courseProvider.getModuleIconSrc(event.icon.component); event.iconUrl = this.courseProvider.getModuleIconSrc(event.icon.component);
return event; return event;
}); });
} }
@ -85,7 +87,7 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges {
/** /**
* Update the events displayed. * Update the events displayed.
*/ */
protected updateEvents() { protected updateEvents(): void {
this.empty = !this.events || this.events.length <= 0; this.empty = !this.events || this.events.length <= 0;
if (!this.empty) { if (!this.empty) {
this.recentlyOverdue = this.filterEventsByTime(-14, 0); this.recentlyOverdue = this.filterEventsByTime(-14, 0);
@ -99,7 +101,7 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges {
/** /**
* Load more events clicked. * Load more events clicked.
*/ */
loadMoreEvents() { loadMoreEvents(): void {
this.loadingMore = true; this.loadingMore = true;
this.loadMore.emit(); this.loadMore.emit();
} }
@ -110,14 +112,14 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges {
* @param {Event} e Click event. * @param {Event} e Click event.
* @param {string} url Url of the action. * @param {string} url Url of the action.
*/ */
action(e: Event, url: string) { action(e: Event, url: string): void {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
// Fix URL format. // Fix URL format.
url = this.textUtils.decodeHTMLEntities(url); url = this.textUtils.decodeHTMLEntities(url);
let modal = this.domUtils.showModalLoading(); const modal = this.domUtils.showModalLoading();
this.contentLinksHelper.handleLink(url, undefined, this.navCtrl).then((treated) => { this.contentLinksHelper.handleLink(url, undefined, this.navCtrl).then((treated) => {
if (!treated) { if (!treated) {
return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url); return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url);
@ -125,7 +127,5 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges {
}).finally(() => { }).finally(() => {
modal.dismiss(); modal.dismiss();
}); });
return false;
} }
} }

View File

@ -21,7 +21,7 @@ import { CoreCoursesProvider } from '../../providers/courses';
/** /**
* Page that displays available courses in current site. * Page that displays available courses in current site.
*/ */
@IonicPage({segment: "core-courses-available-courses"}) @IonicPage({ segment: 'core-courses-available-courses' })
@Component({ @Component({
selector: 'page-core-courses-available-courses', selector: 'page-core-courses-available-courses',
templateUrl: 'available-courses.html', templateUrl: 'available-courses.html',
@ -31,12 +31,12 @@ export class CoreCoursesAvailableCoursesPage {
coursesLoaded: boolean; coursesLoaded: boolean;
constructor(private coursesProvider: CoreCoursesProvider, private domUtils: CoreDomUtilsProvider, constructor(private coursesProvider: CoreCoursesProvider, private domUtils: CoreDomUtilsProvider,
private sitesProvider: CoreSitesProvider) {} private sitesProvider: CoreSitesProvider) { }
/** /**
* View loaded. * View loaded.
*/ */
ionViewDidLoad() { ionViewDidLoad(): void {
this.loadCourses().finally(() => { this.loadCourses().finally(() => {
this.coursesLoaded = true; this.coursesLoaded = true;
}); });
@ -44,9 +44,12 @@ export class CoreCoursesAvailableCoursesPage {
/** /**
* Load the courses. * Load the courses.
*
* @return {Promise<any>} Promise resolved when done.
*/ */
protected loadCourses() { protected loadCourses(): Promise<any> {
const frontpageCourseId = this.sitesProvider.getCurrentSite().getSiteHomeId(); const frontpageCourseId = this.sitesProvider.getCurrentSite().getSiteHomeId();
return this.coursesProvider.getCoursesByField().then((courses) => { return this.coursesProvider.getCoursesByField().then((courses) => {
this.courses = courses.filter((course) => { this.courses = courses.filter((course) => {
return course.id != frontpageCourseId; return course.id != frontpageCourseId;
@ -61,8 +64,8 @@ export class CoreCoursesAvailableCoursesPage {
* *
* @param {any} refresher Refresher. * @param {any} refresher Refresher.
*/ */
refreshCourses(refresher: any) { refreshCourses(refresher: any): void {
let promises = []; const promises = [];
promises.push(this.coursesProvider.invalidateUserCourses()); promises.push(this.coursesProvider.invalidateUserCourses());
promises.push(this.coursesProvider.invalidateCoursesByField()); promises.push(this.coursesProvider.invalidateCoursesByField());
@ -72,5 +75,5 @@ export class CoreCoursesAvailableCoursesPage {
refresher.complete(); refresher.complete();
}); });
}); });
}; }
} }

View File

@ -23,7 +23,7 @@ import { CoreCoursesProvider } from '../../providers/courses';
/** /**
* Page that displays a list of categories and the courses in the current category if any. * Page that displays a list of categories and the courses in the current category if any.
*/ */
@IonicPage({segment: "core-courses-categories"}) @IonicPage({ segment: 'core-courses-categories' })
@Component({ @Component({
selector: 'page-core-courses-categories', selector: 'page-core-courses-categories',
templateUrl: 'categories.html', templateUrl: 'categories.html',
@ -47,7 +47,7 @@ export class CoreCoursesCategoriesPage {
/** /**
* View loaded. * View loaded.
*/ */
ionViewDidLoad() { ionViewDidLoad(): void {
this.fetchCategories().finally(() => { this.fetchCategories().finally(() => {
this.categoriesLoaded = true; this.categoriesLoaded = true;
}); });
@ -55,8 +55,10 @@ export class CoreCoursesCategoriesPage {
/** /**
* Fetch the categories. * Fetch the categories.
*
* @return {Promise<any>} Promise resolved when done.
*/ */
protected fetchCategories() { protected fetchCategories(): Promise<any> {
return this.coursesProvider.getCategories(this.categoryId, true).then((cats) => { return this.coursesProvider.getCategories(this.categoryId, true).then((cats) => {
this.currentCategory = undefined; this.currentCategory = undefined;
@ -69,10 +71,11 @@ export class CoreCoursesCategoriesPage {
}); });
// Sort by depth and sortorder to avoid problems formatting Tree. // Sort by depth and sortorder to avoid problems formatting Tree.
cats.sort((a,b) => { cats.sort((a, b) => {
if (a.depth == b.depth) { if (a.depth == b.depth) {
return (a.sortorder > b.sortorder) ? 1 : ((b.sortorder > a.sortorder) ? -1 : 0); return (a.sortorder > b.sortorder) ? 1 : ((b.sortorder > a.sortorder) ? -1 : 0);
} }
return a.depth > b.depth ? 1 : -1; return a.depth > b.depth ? 1 : -1;
}); });
@ -97,8 +100,8 @@ export class CoreCoursesCategoriesPage {
* *
* @param {any} refresher Refresher. * @param {any} refresher Refresher.
*/ */
refreshCategories(refresher: any) { refreshCategories(refresher: any): void {
let promises = []; const promises = [];
promises.push(this.coursesProvider.invalidateUserCourses()); promises.push(this.coursesProvider.invalidateUserCourses());
promises.push(this.coursesProvider.invalidateCategories(this.categoryId, true)); promises.push(this.coursesProvider.invalidateCategories(this.categoryId, true));
@ -116,7 +119,7 @@ export class CoreCoursesCategoriesPage {
* *
* @param {number} categoryId The category ID. * @param {number} categoryId The category ID.
*/ */
openCategory(categoryId: number) { openCategory(categoryId: number): void {
this.navCtrl.push('CoreCoursesCategoriesPage', {categoryId: categoryId}); this.navCtrl.push('CoreCoursesCategoriesPage', { categoryId: categoryId });
} }
} }

View File

@ -28,7 +28,7 @@ import { CoreCourseHelperProvider } from '../../../course/providers/helper';
/** /**
* Page that allows "previewing" a course and enrolling in it if enabled and not enrolled. * Page that allows "previewing" a course and enrolling in it if enabled and not enrolled.
*/ */
@IonicPage({segment: "core-courses-course-preview"}) @IonicPage({ segment: 'core-courses-course-preview' })
@Component({ @Component({
selector: 'page-core-courses-course-preview', selector: 'page-core-courses-course-preview',
templateUrl: 'course-preview.html', templateUrl: 'course-preview.html',
@ -36,7 +36,7 @@ import { CoreCourseHelperProvider } from '../../../course/providers/helper';
export class CoreCoursesCoursePreviewPage implements OnDestroy { export class CoreCoursesCoursePreviewPage implements OnDestroy {
course: any; course: any;
isEnrolled: boolean; isEnrolled: boolean;
handlersShouldBeShown: boolean = true; handlersShouldBeShown = true;
handlersLoaded: boolean; handlersLoaded: boolean;
component = 'CoreCoursesCoursePreview'; component = 'CoreCoursesCoursePreview';
selfEnrolInstances: any[] = []; selfEnrolInstances: any[] = [];
@ -47,7 +47,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
}; };
protected guestWSAvailable: boolean; protected guestWSAvailable: boolean;
protected isGuestEnabled: boolean = false; protected isGuestEnabled = false;
protected guestInstanceId: number; protected guestInstanceId: number;
protected enrollmentMethods: any[]; protected enrollmentMethods: any[];
protected waitStart = 0; protected waitStart = 0;
@ -82,7 +82,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
/** /**
* View loaded. * View loaded.
*/ */
ionViewDidLoad() { ionViewDidLoad(): void {
const currentSite = this.sitesProvider.getCurrentSite(), const currentSite = this.sitesProvider.getCurrentSite(),
currentSiteUrl = currentSite && currentSite.getURL(); currentSiteUrl = currentSite && currentSite.getURL();
@ -107,7 +107,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
if (icon == 'spinner') { if (icon == 'spinner') {
// Course is being downloaded. Get the download promise. // Course is being downloaded. Get the download promise.
let promise = this.courseHelper.getCourseDownloadPromise(this.course.id); const promise = this.courseHelper.getCourseDownloadPromise(this.course.id);
if (promise) { if (promise) {
// There is a download promise. If it fails, show an error. // There is a download promise. If it fails, show an error.
promise.catch((error) => { promise.catch((error) => {
@ -127,7 +127,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
/** /**
* Page destroyed. * Page destroyed.
*/ */
ngOnDestroy() { ngOnDestroy(): void {
this.pageDestroyed = true; this.pageDestroyed = true;
if (this.courseStatusObserver) { if (this.courseStatusObserver) {
@ -141,7 +141,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
* @return {Promise<boolean>} Promise resolved if can access as guest, rejected otherwise. Resolve param indicates if * @return {Promise<boolean>} Promise resolved if can access as guest, rejected otherwise. Resolve param indicates if
* password is required for guest access. * password is required for guest access.
*/ */
protected canAccessAsGuest() : Promise<boolean> { protected canAccessAsGuest(): Promise<boolean> {
if (!this.isGuestEnabled) { if (!this.isGuestEnabled) {
return Promise.reject(null); return Promise.reject(null);
} }
@ -149,7 +149,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
// Search instance ID of guest enrolment method. // Search instance ID of guest enrolment method.
this.guestInstanceId = undefined; this.guestInstanceId = undefined;
for (let i = 0; i < this.enrollmentMethods.length; i++) { for (let i = 0; i < this.enrollmentMethods.length; i++) {
let method = this.enrollmentMethods[i]; const method = this.enrollmentMethods[i];
if (method.type == 'guest') { if (method.type == 'guest') {
this.guestInstanceId = method.id; this.guestInstanceId = method.id;
break; break;
@ -162,6 +162,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
// Not active, reject. // Not active, reject.
return Promise.reject(null); return Promise.reject(null);
} }
return info.passwordrequired; return info.passwordrequired;
}); });
} }
@ -174,9 +175,10 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
* *
* @param {boolean} refresh Whether the user is refreshing the data. * @param {boolean} refresh Whether the user is refreshing the data.
*/ */
protected getCourse(refresh?: boolean) : Promise<any> { protected getCourse(refresh?: boolean): Promise<any> {
// Get course enrolment methods. // Get course enrolment methods.
this.selfEnrolInstances = []; this.selfEnrolInstances = [];
return this.coursesProvider.getCourseEnrolmentMethods(this.course.id).then((methods) => { return this.coursesProvider.getCourseEnrolmentMethods(this.course.id).then((methods) => {
this.enrollmentMethods = methods; this.enrollmentMethods = methods;
@ -193,15 +195,18 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
// Check if user is enrolled in the course. // Check if user is enrolled in the course.
return this.coursesProvider.getUserCourse(this.course.id).then((course) => { return this.coursesProvider.getUserCourse(this.course.id).then((course) => {
this.isEnrolled = true; this.isEnrolled = true;
return course; return course;
}).catch(() => { }).catch(() => {
// The user is not enrolled in the course. Use getCourses to see if it's an admin/manager and can see the course. // The user is not enrolled in the course. Use getCourses to see if it's an admin/manager and can see the course.
this.isEnrolled = false; this.isEnrolled = false;
return this.coursesProvider.getCourse(this.course.id); return this.coursesProvider.getCourse(this.course.id);
}).then((course) => { }).then((course) => {
// Success retrieving the course, we can assume the user has permissions to view it. // Success retrieving the course, we can assume the user has permissions to view it.
this.course.fullname = course.fullname || this.course.fullname; this.course.fullname = course.fullname || this.course.fullname;
this.course.summary = course.summary || this.course.summary; this.course.summary = course.summary || this.course.summary;
return this.loadCourseHandlers(refresh, false); return this.loadCourseHandlers(refresh, false);
}).catch(() => { }).catch(() => {
// The user is not an admin/manager. Check if we can provide guest access to the course. // The user is not an admin/manager. Check if we can provide guest access to the course.
@ -227,7 +232,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
* @param {boolean} refresh Whether the user is refreshing the data. * @param {boolean} refresh Whether the user is refreshing the data.
* @param {boolean} guest Whether it's guest access. * @param {boolean} guest Whether it's guest access.
*/ */
protected loadCourseHandlers(refresh: boolean, guest: boolean) : Promise<any> { protected loadCourseHandlers(refresh: boolean, guest: boolean): Promise<any> {
return this.courseOptionsDelegate.getHandlersToDisplay(this.course, refresh, guest, true).then((handlers) => { return this.courseOptionsDelegate.getHandlersToDisplay(this.course, refresh, guest, true).then((handlers) => {
this.course._handlers = handlers; this.course._handlers = handlers;
this.handlersShouldBeShown = true; this.handlersShouldBeShown = true;
@ -238,26 +243,27 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
/** /**
* Open the course. * Open the course.
*/ */
openCourse() { openCourse(): void {
if (!this.handlersShouldBeShown) { if (!this.handlersShouldBeShown) {
// Course cannot be opened. // Course cannot be opened.
return; return;
} }
this.navCtrl.push('CoreCourseSectionPage', {course: this.course}); this.navCtrl.push('CoreCourseSectionPage', { course: this.course });
} }
/** /**
* Enrol using PayPal. * Enrol using PayPal.
*/ */
paypalEnrol() { paypalEnrol(): void {
let window, let window,
hasReturnedFromPaypal = false, hasReturnedFromPaypal = false,
inAppLoadSubscription, inAppLoadSubscription,
inAppFinishSubscription, inAppFinishSubscription,
inAppExitSubscription, inAppExitSubscription,
appResumeSubscription, appResumeSubscription;
urlLoaded = (event) => {
const urlLoaded = (event): void => {
if (event.url.indexOf(this.paypalReturnUrl) != -1) { if (event.url.indexOf(this.paypalReturnUrl) != -1) {
hasReturnedFromPaypal = true; hasReturnedFromPaypal = true;
} else if (event.url.indexOf(this.courseUrl) != -1 && hasReturnedFromPaypal) { } else if (event.url.indexOf(this.courseUrl) != -1 && hasReturnedFromPaypal) {
@ -266,7 +272,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
window.close(); window.close();
} }
}, },
inAppClosed = () => { inAppClosed = (): void => {
// InAppBrowser closed, refresh data. // InAppBrowser closed, refresh data.
unsubscribeAll(); unsubscribeAll();
@ -276,7 +282,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
this.dataLoaded = false; this.dataLoaded = false;
this.refreshData(); this.refreshData();
}, },
unsubscribeAll = () => { unsubscribeAll = (): void => {
inAppLoadSubscription && inAppLoadSubscription.unsubscribe(); inAppLoadSubscription && inAppLoadSubscription.unsubscribe();
inAppFinishSubscription && inAppFinishSubscription.unsubscribe(); inAppFinishSubscription && inAppFinishSubscription.unsubscribe();
inAppExitSubscription && inAppExitSubscription.unsubscribe(); inAppExitSubscription && inAppExitSubscription.unsubscribe();
@ -315,7 +321,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
* *
* @param {number} instanceId The instance ID of the enrolment method. * @param {number} instanceId The instance ID of the enrolment method.
*/ */
selfEnrolClicked(instanceId: number) { selfEnrolClicked(instanceId: number): void {
this.domUtils.showConfirm(this.translate.instant('core.courses.confirmselfenrol')).then(() => { this.domUtils.showConfirm(this.translate.instant('core.courses.confirmselfenrol')).then(() => {
this.selfEnrolInCourse('', instanceId); this.selfEnrolInCourse('', instanceId);
}).catch(() => { }).catch(() => {
@ -330,8 +336,8 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
* @param {number} instanceId The instance ID. * @param {number} instanceId The instance ID.
* @return {Promise<any>} Promise resolved when self enrolled. * @return {Promise<any>} Promise resolved when self enrolled.
*/ */
selfEnrolInCourse(password: string, instanceId: number) : Promise<any> { selfEnrolInCourse(password: string, instanceId: number): Promise<any> {
let modal = this.domUtils.showModalLoading('core.loading', true); const modal = this.domUtils.showModalLoading('core.loading', true);
return this.coursesProvider.selfEnrol(this.course.id, password, instanceId).then(() => { return this.coursesProvider.selfEnrol(this.course.id, password, instanceId).then(() => {
// Close modal and refresh data. // Close modal and refresh data.
@ -343,7 +349,7 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
this.refreshData().finally(() => { this.refreshData().finally(() => {
// My courses have been updated, trigger event. // My courses have been updated, trigger event.
this.eventsProvider.trigger( this.eventsProvider.trigger(
CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, {}, this.sitesProvider.getCurrentSiteId()); CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, {}, this.sitesProvider.getCurrentSiteId());
}); });
}); });
}).catch((error) => { }).catch((error) => {
@ -369,8 +375,8 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
* *
* @param {any} [refresher] The refresher if this was triggered by a Pull To Refresh. * @param {any} [refresher] The refresher if this was triggered by a Pull To Refresh.
*/ */
refreshData(refresher?: any) : Promise<any> { refreshData(refresher?: any): Promise<any> {
let promises = []; const promises = [];
promises.push(this.coursesProvider.invalidateUserCourses()); promises.push(this.coursesProvider.invalidateUserCourses());
promises.push(this.coursesProvider.invalidateCourse(this.course.id)); promises.push(this.coursesProvider.invalidateCourse(this.course.id));
@ -393,8 +399,9 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
* Wait for the user to be enrolled in the course. * Wait for the user to be enrolled in the course.
* *
* @param {boolean} first If it's the first call (true) or it's a recursive call (false). * @param {boolean} first If it's the first call (true) or it's a recursive call (false).
* @return {Promise<any>} Promise resolved when enrolled or timeout.
*/ */
protected waitForEnrolled(first?: boolean) { protected waitForEnrolled(first?: boolean): Promise<any> {
if (first) { if (first) {
this.waitStart = Date.now(); this.waitStart = Date.now();
} }
@ -406,12 +413,12 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
return this.coursesProvider.getUserCourse(this.course.id); return this.coursesProvider.getUserCourse(this.course.id);
}).catch(() => { }).catch(() => {
// Not enrolled, wait a bit and try again. // Not enrolled, wait a bit and try again.
if (this.pageDestroyed || (Date.now() - this.waitStart > 60000)) { if (this.pageDestroyed || (Date.now() - this.waitStart > 60000)) {
// Max time reached or the user left the view, stop. // Max time reached or the user left the view, stop.
return; return;
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject): void => {
setTimeout(() => { setTimeout(() => {
if (!this.pageDestroyed) { if (!this.pageDestroyed) {
// Wait again. // Wait again.
@ -427,12 +434,12 @@ export class CoreCoursesCoursePreviewPage implements OnDestroy {
/** /**
* Prefetch the course. * Prefetch the course.
*/ */
prefetchCourse() { prefetchCourse(): void {
this.courseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course, undefined, this.course._handlers) this.courseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course, undefined, this.course._handlers)
.catch((error) => { .catch((error) => {
if (!this.pageDestroyed) { if (!this.pageDestroyed) {
this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true); this.domUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
} }
}) });
} }
} }

View File

@ -24,7 +24,7 @@ import { CoreCourseOptionsDelegate } from '../../../course/providers/options-del
/** /**
* Page that displays the list of courses the user is enrolled in. * Page that displays the list of courses the user is enrolled in.
*/ */
@IonicPage({segment: "core-courses-my-courses"}) @IonicPage({ segment: 'core-courses-my-courses' })
@Component({ @Component({
selector: 'page-core-courses-my-courses', selector: 'page-core-courses-my-courses',
templateUrl: 'my-courses.html', templateUrl: 'my-courses.html',
@ -46,12 +46,12 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider, constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider, private domUtils: CoreDomUtilsProvider, private eventsProvider: CoreEventsProvider,
private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider, private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider,
private courseOptionsDelegate: CoreCourseOptionsDelegate) {} private courseOptionsDelegate: CoreCourseOptionsDelegate) { }
/** /**
* View loaded. * View loaded.
*/ */
ionViewDidLoad() { ionViewDidLoad(): void {
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite(); this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
this.fetchCourses().finally(() => { this.fetchCourses().finally(() => {
@ -69,8 +69,10 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
/** /**
* Fetch the user courses. * Fetch the user courses.
*
* @return {Promise<any>} Promise resolved when done.
*/ */
protected fetchCourses() { protected fetchCourses(): Promise<any> {
return this.coursesProvider.getUserCourses().then((courses) => { return this.coursesProvider.getUserCourses().then((courses) => {
const courseIds = courses.map((course) => { const courseIds = courses.map((course) => {
@ -98,8 +100,8 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
* *
* @param {any} refresher Refresher. * @param {any} refresher Refresher.
*/ */
refreshCourses(refresher: any) { refreshCourses(refresher: any): void {
let promises = []; const promises = [];
promises.push(this.coursesProvider.invalidateUserCourses()); promises.push(this.coursesProvider.invalidateUserCourses());
promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions()); promises.push(this.courseOptionsDelegate.clearAndInvalidateCoursesOptions());
@ -116,7 +118,7 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
/** /**
* Show or hide the filter. * Show or hide the filter.
*/ */
switchFilter() { switchFilter(): void {
this.filter = ''; this.filter = '';
this.showFilter = !this.showFilter; this.showFilter = !this.showFilter;
this.filteredCourses = this.courses; this.filteredCourses = this.courses;
@ -125,7 +127,7 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
/** /**
* Go to search courses. * Go to search courses.
*/ */
openSearch() { openSearch(): void {
this.navCtrl.push('CoreCoursesSearchPage'); this.navCtrl.push('CoreCoursesSearchPage');
} }
@ -134,7 +136,7 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
* *
* @param {string} newValue New filter value. * @param {string} newValue New filter value.
*/ */
filterChanged(newValue: string) { filterChanged(newValue: string): void {
if (!newValue || !this.courses) { if (!newValue || !this.courses) {
this.filteredCourses = this.courses; this.filteredCourses = this.courses;
} else { } else {
@ -146,12 +148,15 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
/** /**
* Prefetch all the courses. * Prefetch all the courses.
*
* @return {Promise<any>} Promise resolved when done.
*/ */
prefetchCourses() { prefetchCourses(): Promise<any> {
let initialIcon = this.prefetchCoursesData.icon; const initialIcon = this.prefetchCoursesData.icon;
this.prefetchCoursesData.icon = 'spinner'; this.prefetchCoursesData.icon = 'spinner';
this.prefetchCoursesData.badge = ''; this.prefetchCoursesData.badge = '';
return this.courseHelper.confirmAndPrefetchCourses(this.courses, (progress) => { return this.courseHelper.confirmAndPrefetchCourses(this.courses, (progress) => {
this.prefetchCoursesData.badge = progress.count + ' / ' + progress.total; this.prefetchCoursesData.badge = progress.count + ' / ' + progress.total;
}).then((downloaded) => { }).then((downloaded) => {
@ -169,7 +174,7 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
/** /**
* Initialize the prefetch icon for the list of courses. * Initialize the prefetch icon for the list of courses.
*/ */
protected initPrefetchCoursesIcon() { protected initPrefetchCoursesIcon(): void {
if (this.prefetchIconInitialized) { if (this.prefetchIconInitialized) {
// Already initialized. // Already initialized.
return; return;
@ -180,6 +185,7 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
if (!this.courses || this.courses.length < 2) { if (!this.courses || this.courses.length < 2) {
// Not enough courses. // Not enough courses.
this.prefetchCoursesData.icon = ''; this.prefetchCoursesData.icon = '';
return; return;
} }
@ -196,7 +202,7 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
/** /**
* Page destroyed. * Page destroyed.
*/ */
ngOnDestroy() { ngOnDestroy(): void {
this.isDestroyed = true; this.isDestroyed = true;
this.myCoursesObserver && this.myCoursesObserver.off(); this.myCoursesObserver && this.myCoursesObserver.off();
this.siteUpdatedObserver && this.siteUpdatedObserver.off(); this.siteUpdatedObserver && this.siteUpdatedObserver.off();

View File

@ -26,7 +26,7 @@ import * as moment from 'moment';
/** /**
* Page that displays My Overview. * Page that displays My Overview.
*/ */
@IonicPage({segment: "core-courses-my-overview"}) @IonicPage({ segment: 'core-courses-my-overview' })
@Component({ @Component({
selector: 'page-core-courses-my-overview', selector: 'page-core-courses-my-overview',
templateUrl: 'my-overview.html', templateUrl: 'my-overview.html',
@ -34,7 +34,7 @@ import * as moment from 'moment';
export class CoreCoursesMyOverviewPage implements OnDestroy { export class CoreCoursesMyOverviewPage implements OnDestroy {
firstSelectedTab: number; firstSelectedTab: number;
siteHomeEnabled: boolean; siteHomeEnabled: boolean;
tabsReady: boolean = false; tabsReady = false;
tabShown = 'courses'; tabShown = 'courses';
timeline = { timeline = {
sort: 'sortbydates', sort: 'sortbydates',
@ -71,17 +71,17 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider, constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
private domUtils: CoreDomUtilsProvider, private myOverviewProvider: CoreCoursesMyOverviewProvider, private domUtils: CoreDomUtilsProvider, private myOverviewProvider: CoreCoursesMyOverviewProvider,
private courseHelper: CoreCourseHelperProvider, private sitesProvider: CoreSitesProvider, private courseHelper: CoreCourseHelperProvider, private sitesProvider: CoreSitesProvider,
private siteHomeProvider: CoreSiteHomeProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate) {} private siteHomeProvider: CoreSiteHomeProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate) { }
/** /**
* View loaded. * View loaded.
*/ */
ionViewDidLoad() { ionViewDidLoad(): void {
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite(); this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
// Decide which tab to load first. // Decide which tab to load first.
this.siteHomeProvider.isAvailable().then((enabled) => { this.siteHomeProvider.isAvailable().then((enabled) => {
let site = this.sitesProvider.getCurrentSite(), const site = this.sitesProvider.getCurrentSite(),
displaySiteHome = site.getInfo() && site.getInfo().userhomepage === 0; displaySiteHome = site.getInfo() && site.getInfo().userhomepage === 0;
this.siteHomeEnabled = enabled; this.siteHomeEnabled = enabled;
@ -96,7 +96,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
* @param {number} [afterEventId] The last event id. * @param {number} [afterEventId] The last event id.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
protected fetchMyOverviewTimeline(afterEventId?: number) : Promise<any> { protected fetchMyOverviewTimeline(afterEventId?: number): Promise<any> {
return this.myOverviewProvider.getActionEventsByTimesort(afterEventId).then((events) => { return this.myOverviewProvider.getActionEventsByTimesort(afterEventId).then((events) => {
this.timeline.events = events.events; this.timeline.events = events.events;
this.timeline.canLoadMore = events.canLoadMore; this.timeline.canLoadMore = events.canLoadMore;
@ -110,10 +110,10 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
* *
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
protected fetchMyOverviewTimelineByCourses() : Promise<any> { protected fetchMyOverviewTimelineByCourses(): Promise<any> {
return this.fetchUserCourses().then((courses) => { return this.fetchUserCourses().then((courses) => {
let today = moment().unix(), const today = moment().unix();
courseIds; let courseIds;
courses = courses.filter((course) => { courses = courses.filter((course) => {
return course.startdate <= today && (!course.enddate || course.enddate >= today); return course.startdate <= today && (!course.enddate || course.enddate >= today);
}); });
@ -141,7 +141,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
* *
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
protected fetchMyOverviewCourses() : Promise<any> { protected fetchMyOverviewCourses(): Promise<any> {
return this.fetchUserCourses().then((courses) => { return this.fetchUserCourses().then((courses) => {
const today = moment().unix(); const today = moment().unix();
@ -177,10 +177,9 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
* *
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
protected fetchUserCourses() : Promise<any> { protected fetchUserCourses(): Promise<any> {
let courseIds;
return this.coursesProvider.getUserCourses().then((courses) => { return this.coursesProvider.getUserCourses().then((courses) => {
courseIds = courses.map((course) => { const courseIds = courses.map((course) => {
return course.id; return course.id;
}); });
@ -204,7 +203,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
/** /**
* Show or hide the filter. * Show or hide the filter.
*/ */
switchFilter() { switchFilter(): void {
this.showFilter = !this.showFilter; this.showFilter = !this.showFilter;
this.courses.filter = ''; this.courses.filter = '';
this.filteredCourses = this.courses[this.courses.selected]; this.filteredCourses = this.courses[this.courses.selected];
@ -215,7 +214,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
* *
* @param {string} newValue New filter value. * @param {string} newValue New filter value.
*/ */
filterChanged(newValue: string) { filterChanged(newValue: string): void {
if (!newValue || !this.courses[this.courses.selected]) { if (!newValue || !this.courses[this.courses.selected]) {
this.filteredCourses = this.courses[this.courses.selected]; this.filteredCourses = this.courses[this.courses.selected];
} else { } else {
@ -229,9 +228,10 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
* Refresh the data. * Refresh the data.
* *
* @param {any} refresher Refresher. * @param {any} refresher Refresher.
* @return {Promise<any>} Promise resolved when done.
*/ */
refreshMyOverview(refresher: any) { refreshMyOverview(refresher: any): Promise<any> {
let promises = []; const promises = [];
if (this.tabShown == 'timeline') { if (this.tabShown == 'timeline') {
promises.push(this.myOverviewProvider.invalidateActionEventsByTimesort()); promises.push(this.myOverviewProvider.invalidateActionEventsByTimesort());
@ -249,11 +249,14 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
return this.fetchMyOverviewTimeline(); return this.fetchMyOverviewTimeline();
case 'sortbycourses': case 'sortbycourses':
return this.fetchMyOverviewTimelineByCourses(); return this.fetchMyOverviewTimelineByCourses();
default:
} }
break; break;
case 'courses': case 'courses':
this.prefetchIconsInitialized = false; this.prefetchIconsInitialized = false;
return this.fetchMyOverviewCourses(); return this.fetchMyOverviewCourses();
default:
} }
}).finally(() => { }).finally(() => {
refresher.complete(); refresher.complete();
@ -263,7 +266,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
/** /**
* Change timeline sort being viewed. * Change timeline sort being viewed.
*/ */
switchSort() { switchSort(): void {
switch (this.timeline.sort) { switch (this.timeline.sort) {
case 'sortbydates': case 'sortbydates':
if (!this.timeline.loaded) { if (!this.timeline.loaded) {
@ -279,6 +282,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
}); });
} }
break; break;
default:
} }
} }
@ -287,7 +291,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
* *
* @param {string} tab Name of the new tab. * @param {string} tab Name of the new tab.
*/ */
tabChanged(tab: string) { tabChanged(tab: string): void {
this.tabShown = tab; this.tabShown = tab;
switch (this.tabShown) { switch (this.tabShown) {
case 'timeline': case 'timeline':
@ -304,13 +308,14 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
}); });
} }
break; break;
default:
} }
} }
/** /**
* Load more events. * Load more events.
*/ */
loadMoreTimeline() : Promise<any> { loadMoreTimeline(): Promise<any> {
return this.fetchMyOverviewTimeline(this.timeline.canLoadMore); return this.fetchMyOverviewTimeline(this.timeline.canLoadMore);
} }
@ -318,8 +323,9 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
* Load more events. * Load more events.
* *
* @param {any} course Course. * @param {any} course Course.
* @return {Promise<any>} Promise resolved when done.
*/ */
loadMoreCourse(course) { loadMoreCourse(course: any): Promise<any> {
return this.myOverviewProvider.getActionEventsByCourse(course.id, course.canLoadMore).then((courseEvents) => { return this.myOverviewProvider.getActionEventsByCourse(course.id, course.canLoadMore).then((courseEvents) => {
course.events = course.events.concat(courseEvents.events); course.events = course.events.concat(courseEvents.events);
course.canLoadMore = courseEvents.canLoadMore; course.canLoadMore = courseEvents.canLoadMore;
@ -329,27 +335,30 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
/** /**
* Go to search courses. * Go to search courses.
*/ */
openSearch() { openSearch(): void {
this.navCtrl.push('CoreCoursesSearchPage'); this.navCtrl.push('CoreCoursesSearchPage');
} }
/** /**
* The selected courses have changed. * The selected courses have changed.
*/ */
selectedChanged() { selectedChanged(): void {
this.filteredCourses = this.courses[this.courses.selected]; this.filteredCourses = this.courses[this.courses.selected];
} }
/** /**
* Prefetch all the shown courses. * Prefetch all the shown courses.
*
* @return {Promise<any>} Promise resolved when done.
*/ */
prefetchCourses() { prefetchCourses(): Promise<any> {
let selected = this.courses.selected, const selected = this.courses.selected,
selectedData = this.prefetchCoursesData[selected], selectedData = this.prefetchCoursesData[selected],
initialIcon = selectedData.icon; initialIcon = selectedData.icon;
selectedData.icon = 'spinner'; selectedData.icon = 'spinner';
selectedData.badge = ''; selectedData.badge = '';
return this.courseHelper.confirmAndPrefetchCourses(this.courses[selected], (progress) => { return this.courseHelper.confirmAndPrefetchCourses(this.courses[selected], (progress) => {
selectedData.badge = progress.count + ' / ' + progress.total; selectedData.badge = progress.count + ' / ' + progress.total;
}).then((downloaded) => { }).then((downloaded) => {
@ -367,7 +376,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
/** /**
* Initialize the prefetch icon for selected courses. * Initialize the prefetch icon for selected courses.
*/ */
protected initPrefetchCoursesIcons() { protected initPrefetchCoursesIcons(): void {
if (this.prefetchIconsInitialized) { if (this.prefetchIconsInitialized) {
// Already initialized. // Already initialized.
return; return;
@ -379,6 +388,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
if (!this.courses[filter] || this.courses[filter].length < 2) { if (!this.courses[filter] || this.courses[filter].length < 2) {
// Not enough courses. // Not enough courses.
this.prefetchCoursesData[filter].icon = ''; this.prefetchCoursesData[filter].icon = '';
return; return;
} }
@ -397,7 +407,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
/** /**
* Component being destroyed. * Component being destroyed.
*/ */
ngOnDestroy() { ngOnDestroy(): void {
this.isDestroyed = true; this.isDestroyed = true;
} }
} }

View File

@ -20,7 +20,7 @@ import { CoreCoursesProvider } from '../../providers/courses';
/** /**
* Page that allows searching for courses. * Page that allows searching for courses.
*/ */
@IonicPage({segment: "core-courses-search"}) @IonicPage({ segment: 'core-courses-search' })
@Component({ @Component({
selector: 'page-core-courses-search', selector: 'page-core-courses-search',
templateUrl: 'search.html', templateUrl: 'search.html',
@ -33,19 +33,19 @@ export class CoreCoursesSearchPage {
protected page = 0; protected page = 0;
protected currentSearch = ''; protected currentSearch = '';
constructor(private domUtils: CoreDomUtilsProvider, private coursesProvider: CoreCoursesProvider) {} constructor(private domUtils: CoreDomUtilsProvider, private coursesProvider: CoreCoursesProvider) { }
/** /**
* Search a new text. * Search a new text.
* *
* @param {string} text The text to search. * @param {string} text The text to search.
*/ */
search(text: string) { search(text: string): void {
this.currentSearch = text; this.currentSearch = text;
this.courses = undefined; this.courses = undefined;
this.page = 0; this.page = 0;
let modal = this.domUtils.showModalLoading('core.searching', true); const modal = this.domUtils.showModalLoading('core.searching', true);
this.searchCourses().finally(() => { this.searchCourses().finally(() => {
modal.dismiss(); modal.dismiss();
}); });
@ -53,8 +53,10 @@ export class CoreCoursesSearchPage {
/** /**
* Load more results. * Load more results.
*
* @param {any} infiniteScroll The infinit scroll instance.
*/ */
loadMoreResults(infiniteScroll) { loadMoreResults(infiniteScroll: any): void {
this.searchCourses().finally(() => { this.searchCourses().finally(() => {
infiniteScroll.complete(); infiniteScroll.complete();
}); });
@ -62,8 +64,10 @@ export class CoreCoursesSearchPage {
/** /**
* Search courses or load the next page of current search. * Search courses or load the next page of current search.
*
* @return {Promise<any>} Promise resolved when done.
*/ */
protected searchCourses() { protected searchCourses(): Promise<any> {
return this.coursesProvider.search(this.currentSearch, this.page).then((response) => { return this.coursesProvider.search(this.currentSearch, this.page).then((response) => {
if (this.page === 0) { if (this.page === 0) {
this.courses = response.courses; this.courses = response.courses;

View File

@ -18,18 +18,18 @@ import { IonicPage, ViewController } from 'ionic-angular';
/** /**
* Page that displays a form to enter a password to self enrol in a course. * Page that displays a form to enter a password to self enrol in a course.
*/ */
@IonicPage({segment: "core-courses-self-enrol-password"}) @IonicPage({ segment: 'core-courses-self-enrol-password' })
@Component({ @Component({
selector: 'page-core-courses-self-enrol-password', selector: 'page-core-courses-self-enrol-password',
templateUrl: 'self-enrol-password.html', templateUrl: 'self-enrol-password.html',
}) })
export class CoreCoursesSelfEnrolPasswordPage { export class CoreCoursesSelfEnrolPasswordPage {
constructor(private viewCtrl: ViewController) {} constructor(private viewCtrl: ViewController) { }
/** /**
* Close help modal. * Close help modal.
*/ */
close() : void { close(): void {
this.viewCtrl.dismiss(); this.viewCtrl.dismiss();
} }
@ -38,7 +38,7 @@ export class CoreCoursesSelfEnrolPasswordPage {
* *
* @param {string} password Password to submit. * @param {string} password Password to submit.
*/ */
submitPassword(password: string) { submitPassword(password: string): void {
this.viewCtrl.dismiss(password); this.viewCtrl.dismiss(password);
} }
} }

View File

@ -47,15 +47,15 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
* @param {number} [courseId] Course ID related to the URL. Optional but recommended. * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions. * @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
*/ */
getActions(siteIds: string[], url: string, params: any, courseId?: number) : getActions(siteIds: string[], url: string, params: any, courseId?: number):
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
courseId = parseInt(params.id, 10); courseId = parseInt(params.id, 10);
let sectionId = params.sectionid ? parseInt(params.sectionid, 10) : null, const sectionId = params.sectionid ? parseInt(params.sectionid, 10) : null,
sectionNumber = typeof params.section != 'undefined' ? parseInt(params.section, 10) : NaN, sectionNumber = typeof params.section != 'undefined' ? parseInt(params.section, 10) : NaN,
pageParams: any = { pageParams: any = {
course: {id: courseId}, course: { id: courseId },
sectionId: sectionId || null sectionId: sectionId || null
}; };
if (!isNaN(sectionNumber)) { if (!isNaN(sectionNumber)) {
@ -63,7 +63,7 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
} }
return [{ return [{
action: (siteId, navCtrl?) => { action: (siteId, navCtrl?): void => {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
if (siteId == this.sitesProvider.getCurrentSiteId()) { if (siteId == this.sitesProvider.getCurrentSiteId()) {
this.actionEnrol(courseId, url, pageParams).catch(() => { this.actionEnrol(courseId, url, pageParams).catch(() => {
@ -87,7 +87,7 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
* @param {number} [courseId] Course ID related to the URL. Optional but recommended. * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
* @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site. * @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site.
*/ */
isEnabled(siteId: string, url: string, params: any, courseId?: number) : boolean|Promise<boolean> { isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise<boolean> {
courseId = parseInt(params.id, 10); courseId = parseInt(params.id, 10);
if (!courseId) { if (!courseId) {
@ -96,8 +96,8 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
// Get the course id of Site Home. // Get the course id of Site Home.
return this.sitesProvider.getSiteHomeId(siteId).then((siteHomeId) => { return this.sitesProvider.getSiteHomeId(siteId).then((siteHomeId) => {
return courseId != siteHomeId; return courseId != siteHomeId;
}); });
} }
/** /**
@ -108,8 +108,8 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
* @param {any} pageParams Params to send to the new page. * @param {any} pageParams Params to send to the new page.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
protected actionEnrol(courseId: number, url: string, pageParams: any) : Promise<any> { protected actionEnrol(courseId: number, url: string, pageParams: any): Promise<any> {
let modal = this.domUtils.showModalLoading(), const modal = this.domUtils.showModalLoading(),
isEnrolUrl = !!url.match(/(\/enrol\/index\.php)|(\/course\/enrol\.php)/); isEnrolUrl = !!url.match(/(\/enrol\/index\.php)|(\/course\/enrol\.php)/);
// Check if user is enrolled in the course. // Check if user is enrolled in the course.
@ -119,8 +119,8 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
modal.dismiss(); modal.dismiss();
// The user can self enrol. If it's not a enrolment URL we'll ask for confirmation. // The user can self enrol. If it's not a enrolment URL we'll ask for confirmation.
let promise = isEnrolUrl ? Promise.resolve() : const promise = isEnrolUrl ? Promise.resolve() :
this.domUtils.showConfirm(this.translate.instant('core.courses.confirmselfenrol')); this.domUtils.showConfirm(this.translate.instant('core.courses.confirmselfenrol'));
return promise.then(() => { return promise.then(() => {
// Enrol URL or user confirmed. // Enrol URL or user confirmed.
@ -128,6 +128,7 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
if (error) { if (error) {
this.domUtils.showErrorModal(error); this.domUtils.showErrorModal(error);
} }
return Promise.reject(null); return Promise.reject(null);
}); });
}, () => { }, () => {
@ -147,13 +148,14 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
error = this.translate.instant('core.courses.notenroled'); error = this.translate.instant('core.courses.notenroled');
} }
let body = this.translate.instant('core.twoparagraphs', const body = this.translate.instant('core.twoparagraphs',
{p1: error, p2: this.translate.instant('core.confirmopeninbrowser')}); { p1: error, p2: this.translate.instant('core.confirmopeninbrowser') });
this.domUtils.showConfirm(body).then(() => { this.domUtils.showConfirm(body).then(() => {
this.sitesProvider.getCurrentSite().openInBrowserWithAutoLogin(url); this.sitesProvider.getCurrentSite().openInBrowserWithAutoLogin(url);
}).catch(() => { }).catch(() => {
// User cancelled. // User cancelled.
}); });
return Promise.reject(null); return Promise.reject(null);
}); });
}); });
@ -171,7 +173,7 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
* @param {number} courseId Course ID. * @param {number} courseId Course ID.
* @return {Promise<any>} Promise resolved if user can be enrolled in a course, rejected otherwise. * @return {Promise<any>} Promise resolved if user can be enrolled in a course, rejected otherwise.
*/ */
protected canSelfEnrol(courseId: number) : Promise<any> { protected canSelfEnrol(courseId: number): Promise<any> {
// Check that the course has self enrolment enabled. // Check that the course has self enrolment enabled.
return this.coursesProvider.getCourseEnrolmentMethods(courseId).then((methods) => { return this.coursesProvider.getCourseEnrolmentMethods(courseId).then((methods) => {
let isSelfEnrolEnabled = false, let isSelfEnrolEnabled = false,
@ -198,8 +200,9 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
* @param {string} [password] Password. * @param {string} [password] Password.
* @return {Promise<any>} Promise resolved when the user is enrolled, rejected otherwise. * @return {Promise<any>} Promise resolved when the user is enrolled, rejected otherwise.
*/ */
protected selfEnrol(courseId: number, password?: string) : Promise<any> { protected selfEnrol(courseId: number, password?: string): Promise<any> {
const modal = this.domUtils.showModalLoading(); const modal = this.domUtils.showModalLoading();
return this.coursesProvider.selfEnrol(courseId, password).then(() => { return this.coursesProvider.selfEnrol(courseId, password).then(() => {
// Success self enrolling the user, invalidate the courses list. // Success self enrolling the user, invalidate the courses list.
return this.coursesProvider.invalidateUserCourses().catch(() => { return this.coursesProvider.invalidateUserCourses().catch(() => {
@ -215,7 +218,7 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
modal.dismiss(); modal.dismiss();
if (error && error.code === CoreCoursesProvider.ENROL_INVALID_KEY) { if (error && error.code === CoreCoursesProvider.ENROL_INVALID_KEY) {
// Invalid password. Allow the user to input password. // Invalid password. Allow the user to input password.
let title = this.translate.instant('core.courses.selfenrolment'), const title = this.translate.instant('core.courses.selfenrolment'),
body = ' ', // Empty message. body = ' ', // Empty message.
placeholder = this.translate.instant('core.courses.password'); placeholder = this.translate.instant('core.courses.password');
@ -240,7 +243,7 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
* @param {boolean} first If it's the first call (true) or it's a recursive call (false). * @param {boolean} first If it's the first call (true) or it's a recursive call (false).
* @return {Promise<any>} Promise resolved when enrolled or timeout. * @return {Promise<any>} Promise resolved when enrolled or timeout.
*/ */
protected waitForEnrolled(courseId: number, first?: boolean) : Promise<any> { protected waitForEnrolled(courseId: number, first?: boolean): Promise<any> {
if (first) { if (first) {
this.waitStart = Date.now(); this.waitStart = Date.now();
} }
@ -257,7 +260,7 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
return; return;
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject): void => {
setTimeout(() => { setTimeout(() => {
this.waitForEnrolled(courseId).then(resolve); this.waitForEnrolled(courseId).then(resolve);
}, 5000); }, 5000);

View File

@ -38,14 +38,14 @@ export class CoreCoursesIndexLinkHandler extends CoreContentLinksHandlerBase {
* @param {string} url The URL to treat. * @param {string} url The URL to treat.
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1} * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
* @param {number} [courseId] Course ID related to the URL. Optional but recommended. * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions. * @return {CoreContentLinksAction[] | Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
*/ */
getActions(siteIds: string[], url: string, params: any, courseId?: number) : getActions(siteIds: string[], url: string, params: any, courseId?: number):
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return [{ return [{
action: (siteId, navCtrl?) => { action: (siteId, navCtrl?): void => {
var page = 'CoreCoursesMyCoursesPage', // By default, go to My Courses. let page = 'CoreCoursesMyCoursesPage'; // By default, go to My Courses.
pageParams: any = {}; const pageParams: any = {};
if (this.coursesProvider.isGetCoursesByFieldAvailable()) { if (this.coursesProvider.isGetCoursesByFieldAvailable()) {
if (params.categoryid) { if (params.categoryid) {

View File

@ -22,10 +22,10 @@ import { CoreSite } from '../../../classes/site';
*/ */
@Injectable() @Injectable()
export class CoreCoursesProvider { export class CoreCoursesProvider {
public static SEARCH_PER_PAGE = 20; static SEARCH_PER_PAGE = 20;
public static ENROL_INVALID_KEY = 'CoreCoursesEnrolInvalidKey'; static ENROL_INVALID_KEY = 'CoreCoursesEnrolInvalidKey';
public static EVENT_MY_COURSES_UPDATED = 'courses_my_courses_updated'; static EVENT_MY_COURSES_UPDATED = 'courses_my_courses_updated';
public static EVENT_MY_COURSES_REFRESHED = 'courses_my_courses_refreshed'; static EVENT_MY_COURSES_REFRESHED = 'courses_my_courses_refreshed';
protected ROOT_CACHE_KEY = 'mmCourses:'; protected ROOT_CACHE_KEY = 'mmCourses:';
protected logger; protected logger;
@ -41,10 +41,10 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site to get the courses from. If not defined, use current site. * @param {string} [siteId] Site to get the courses from. If not defined, use current site.
* @return {Promise<any[]>} Promise resolved with the categories. * @return {Promise<any[]>} Promise resolved with the categories.
*/ */
getCategories(categoryId: number, addSubcategories?: boolean, siteId?: string) : Promise<any[]> { getCategories(categoryId: number, addSubcategories?: boolean, siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
// Get parent when id is the root category. // Get parent when id is the root category.
let criteriaKey = categoryId == 0 ? 'parent' : 'id', const criteriaKey = categoryId == 0 ? 'parent' : 'id',
data = { data = {
criteria: [ criteria: [
{ key: criteriaKey, value: categoryId } { key: criteriaKey, value: categoryId }
@ -53,7 +53,7 @@ export class CoreCoursesProvider {
}, },
preSets = { preSets = {
cacheKey: this.getCategoriesCacheKey(categoryId, addSubcategories) cacheKey: this.getCategoriesCacheKey(categoryId, addSubcategories)
} };
return site.read('core_course_get_categories', data, preSets); return site.read('core_course_get_categories', data, preSets);
}); });
@ -66,7 +66,7 @@ export class CoreCoursesProvider {
* @param {boolean} [addSubcategories] If add subcategories to the list. * @param {boolean} [addSubcategories] If add subcategories to the list.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getCategoriesCacheKey(categoryId: number, addSubcategories?: boolean) : string { protected getCategoriesCacheKey(categoryId: number, addSubcategories?: boolean): string {
return this.ROOT_CACHE_KEY + 'categories:' + categoryId + ':' + !!addSubcategories; return this.ROOT_CACHE_KEY + 'categories:' + categoryId + ':' + !!addSubcategories;
} }
@ -77,15 +77,15 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise} Promise resolved with the list of course IDs. * @return {Promise} Promise resolved with the list of course IDs.
*/ */
protected getCourseIdsForAdminAndNavOptions(courseIds: number[], siteId?: string) : Promise<number[]> { protected getCourseIdsForAdminAndNavOptions(courseIds: number[], siteId?: string): Promise<number[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const siteHomeId = site.getSiteHomeId(); const siteHomeId = site.getSiteHomeId();
if (courseIds.length == 1) { if (courseIds.length == 1) {
// Only 1 course, check if it belongs to the user courses. If so, use all user courses. // Only 1 course, check if it belongs to the user courses. If so, use all user courses.
return this.getUserCourses(true, siteId).then((courses) => { return this.getUserCourses(true, siteId).then((courses) => {
let courseId = courseIds[0], const courseId = courseIds[0];
useAllCourses = false; let useAllCourses = false;
if (courseId == siteHomeId) { if (courseId == siteHomeId) {
// It's site home, use all courses. // It's site home, use all courses.
@ -126,7 +126,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<boolean>} Promise resolved with true if disabled, rejected or resolved with false otherwise. * @return {Promise<boolean>} Promise resolved with true if disabled, rejected or resolved with false otherwise.
*/ */
isMyCoursesDisabled(siteId?: string) : Promise<boolean> { isMyCoursesDisabled(siteId?: string): Promise<boolean> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return this.isMyCoursesDisabledInSite(site); return this.isMyCoursesDisabledInSite(site);
}); });
@ -138,8 +138,9 @@ export class CoreCoursesProvider {
* @param {CoreSite} [site] Site. If not defined, use current site. * @param {CoreSite} [site] Site. If not defined, use current site.
* @return {boolean} Whether it's disabled. * @return {boolean} Whether it's disabled.
*/ */
isMyCoursesDisabledInSite(site?: CoreSite) : boolean { isMyCoursesDisabledInSite(site?: CoreSite): boolean {
site = site || this.sitesProvider.getCurrentSite(); site = site || this.sitesProvider.getCurrentSite();
return site.isFeatureDisabled('$mmSideMenuDelegate_mmCourses'); return site.isFeatureDisabled('$mmSideMenuDelegate_mmCourses');
} }
@ -149,7 +150,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<boolean>} Promise resolved with true if disabled, rejected or resolved with false otherwise. * @return {Promise<boolean>} Promise resolved with true if disabled, rejected or resolved with false otherwise.
*/ */
isSearchCoursesDisabled(siteId?: string) : Promise<boolean> { isSearchCoursesDisabled(siteId?: string): Promise<boolean> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return this.isSearchCoursesDisabledInSite(site); return this.isSearchCoursesDisabledInSite(site);
}); });
@ -161,8 +162,9 @@ export class CoreCoursesProvider {
* @param {CoreSite} [site] Site. If not defined, use current site. * @param {CoreSite} [site] Site. If not defined, use current site.
* @return {boolean} Whether it's disabled. * @return {boolean} Whether it's disabled.
*/ */
isSearchCoursesDisabledInSite(site?: CoreSite) : boolean { isSearchCoursesDisabledInSite(site?: CoreSite): boolean {
site = site || this.sitesProvider.getCurrentSite(); site = site || this.sitesProvider.getCurrentSite();
return site.isFeatureDisabled('$mmCoursesDelegate_search'); return site.isFeatureDisabled('$mmCoursesDelegate_search');
} }
@ -173,11 +175,12 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site to get the courses from. If not defined, use current site. * @param {string} [siteId] Site to get the courses from. If not defined, use current site.
* @return {Promise<any>} Promise resolved with the course. * @return {Promise<any>} Promise resolved with the course.
*/ */
getCourse(id: number, siteId?: string) : Promise<any> { getCourse(id: number, siteId?: string): Promise<any> {
return this.getCourses([id], siteId).then((courses) => { return this.getCourses([id], siteId).then((courses) => {
if (courses && courses.length > 0) { if (courses && courses.length > 0) {
return courses[0]; return courses[0];
} }
return Promise.reject(null); return Promise.reject(null);
}); });
} }
@ -189,14 +192,14 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID. If not defined, use current site. * @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<any[]} Promise resolved with the methods. * @return {Promise<any[]} Promise resolved with the methods.
*/ */
getCourseEnrolmentMethods(id: number, siteId?: string) : Promise<any[]> { getCourseEnrolmentMethods(id: number, siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let params = { const params = {
courseid: id courseid: id
}, },
preSets = { preSets = {
cacheKey: this.getCourseEnrolmentMethodsCacheKey(id) cacheKey: this.getCourseEnrolmentMethodsCacheKey(id)
} };
return site.read('core_enrol_get_course_enrolment_methods', params, preSets); return site.read('core_enrol_get_course_enrolment_methods', params, preSets);
}); });
@ -208,7 +211,7 @@ export class CoreCoursesProvider {
* @param {number} id Course ID. * @param {number} id Course ID.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getCourseEnrolmentMethodsCacheKey(id: number) : string { protected getCourseEnrolmentMethodsCacheKey(id: number): string {
return this.ROOT_CACHE_KEY + 'enrolmentmethods:' + id; return this.ROOT_CACHE_KEY + 'enrolmentmethods:' + id;
} }
@ -219,14 +222,14 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID. If not defined, use current site. * @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the info is retrieved. * @return {Promise<any>} Promise resolved when the info is retrieved.
*/ */
getCourseGuestEnrolmentInfo(instanceId: number, siteId?: string) : Promise<any> { getCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let params = { const params = {
instanceid: instanceId instanceid: instanceId
}, },
preSets = { preSets = {
cacheKey: this.getCourseGuestEnrolmentInfoCacheKey(instanceId) cacheKey: this.getCourseGuestEnrolmentInfoCacheKey(instanceId)
} };
return site.read('enrol_guest_get_instance_info', params, preSets).then((response) => { return site.read('enrol_guest_get_instance_info', params, preSets).then((response) => {
return response.instanceinfo; return response.instanceinfo;
@ -240,7 +243,7 @@ export class CoreCoursesProvider {
* @param {number} instanceId Guest instance ID. * @param {number} instanceId Guest instance ID.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getCourseGuestEnrolmentInfoCacheKey(instanceId: number) : string { protected getCourseGuestEnrolmentInfoCacheKey(instanceId: number): string {
return this.ROOT_CACHE_KEY + 'guestinfo:' + instanceId; return this.ROOT_CACHE_KEY + 'guestinfo:' + instanceId;
} }
@ -253,7 +256,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site to get the courses from. If not defined, use current site. * @param {string} [siteId] Site to get the courses from. If not defined, use current site.
* @return {Promise<any[]>} Promise resolved with the courses. * @return {Promise<any[]>} Promise resolved with the courses.
*/ */
getCourses(ids: number[], siteId?: string) : Promise<any[]> { getCourses(ids: number[], siteId?: string): Promise<any[]> {
if (!Array.isArray(ids)) { if (!Array.isArray(ids)) {
return Promise.reject(null); return Promise.reject(null);
} else if (ids.length === 0) { } else if (ids.length === 0) {
@ -261,14 +264,14 @@ export class CoreCoursesProvider {
} }
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let data = { const data = {
options: { options: {
ids: ids ids: ids
} }
}, },
preSets = { preSets = {
cacheKey: this.getCoursesCacheKey(ids) cacheKey: this.getCoursesCacheKey(ids)
} };
return site.read('core_course_get_courses', data, preSets); return site.read('core_course_get_courses', data, preSets);
}); });
@ -280,7 +283,7 @@ export class CoreCoursesProvider {
* @param {number[]} ids Courses IDs. * @param {number[]} ids Courses IDs.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getCoursesCacheKey(ids: number[]) : string { protected getCoursesCacheKey(ids: number[]): string {
return this.ROOT_CACHE_KEY + 'course:' + JSON.stringify(ids); return this.ROOT_CACHE_KEY + 'course:' + JSON.stringify(ids);
} }
@ -297,15 +300,15 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID. If not defined, use current site. * @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<any[]>} Promise resolved with the courses. * @return {Promise<any[]>} Promise resolved with the courses.
*/ */
getCoursesByField(field?: string, value?: any, siteId?: string) : Promise<any[]> { getCoursesByField(field?: string, value?: any, siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let data = { const data = {
field: field || '', field: field || '',
value: field ? value : '' value: field ? value : ''
}, },
preSets = { preSets = {
cacheKey: this.getCoursesByFieldCacheKey(field, value) cacheKey: this.getCoursesByFieldCacheKey(field, value)
} };
return site.read('core_course_get_courses_by_field', data, preSets).then((courses) => { return site.read('core_course_get_courses_by_field', data, preSets).then((courses) => {
if (courses.courses) { if (courses.courses) {
@ -339,9 +342,10 @@ export class CoreCoursesProvider {
* @param {any} [value] The value to match. * @param {any} [value] The value to match.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getCoursesByFieldCacheKey(field?: string, value?: any) : string { protected getCoursesByFieldCacheKey(field?: string, value?: any): string {
field = field || ''; field = field || '';
value = field ? value : ''; value = field ? value : '';
return this.ROOT_CACHE_KEY + 'coursesbyfield:' + field + ':' + value; return this.ROOT_CACHE_KEY + 'coursesbyfield:' + field + ':' + value;
} }
@ -350,9 +354,8 @@ export class CoreCoursesProvider {
* *
* @return {boolean} Whether get courses by field is available. * @return {boolean} Whether get courses by field is available.
*/ */
isGetCoursesByFieldAvailable() : boolean { isGetCoursesByFieldAvailable(): boolean {
let currentSite = this.sitesProvider.getCurrentSite(); return this.sitesProvider.getCurrentSite().wsAvailable('core_course_get_courses_by_field');
return currentSite.wsAvailable('core_course_get_courses_by_field');
} }
/** /**
@ -362,13 +365,13 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<{navOptions: any, admOptions: any}>} Promise resolved with the options for each course. * @return {Promise<{navOptions: any, admOptions: any}>} Promise resolved with the options for each course.
*/ */
getCoursesAdminAndNavOptions(courseIds: number[], siteId?: string) : Promise<{navOptions: any, admOptions: any}> { getCoursesAdminAndNavOptions(courseIds: number[], siteId?: string): Promise<{ navOptions: any, admOptions: any }> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
// Get the list of courseIds to use based on the param. // Get the list of courseIds to use based on the param.
return this.getCourseIdsForAdminAndNavOptions(courseIds, siteId).then((courseIds) => { return this.getCourseIdsForAdminAndNavOptions(courseIds, siteId).then((courseIds) => {
let promises = [], const promises = [];
navOptions, let navOptions,
admOptions; admOptions;
// Get user navigation and administration options. // Get user navigation and administration options.
@ -387,7 +390,7 @@ export class CoreCoursesProvider {
})); }));
return Promise.all(promises).then(() => { return Promise.all(promises).then(() => {
return {navOptions: navOptions, admOptions: admOptions}; return { navOptions: navOptions, admOptions: admOptions };
}); });
}); });
} }
@ -397,7 +400,7 @@ export class CoreCoursesProvider {
* *
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getUserAdministrationOptionsCommonCacheKey() : string { protected getUserAdministrationOptionsCommonCacheKey(): string {
return this.ROOT_CACHE_KEY + 'administrationOptions:'; return this.ROOT_CACHE_KEY + 'administrationOptions:';
} }
@ -407,7 +410,7 @@ export class CoreCoursesProvider {
* @param {number[]} courseIds IDs of courses to get. * @param {number[]} courseIds IDs of courses to get.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getUserAdministrationOptionsCacheKey(courseIds: number[]) : string { protected getUserAdministrationOptionsCacheKey(courseIds: number[]): string {
return this.getUserAdministrationOptionsCommonCacheKey() + courseIds.join(','); return this.getUserAdministrationOptionsCommonCacheKey() + courseIds.join(',');
} }
@ -418,14 +421,14 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved with administration options for each course. * @return {Promise<any>} Promise resolved with administration options for each course.
*/ */
getUserAdministrationOptions(courseIds: number[], siteId?: string) : Promise<any> { getUserAdministrationOptions(courseIds: number[], siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let params = { const params = {
courseids: courseIds courseids: courseIds
}, },
preSets = { preSets = {
cacheKey: this.getUserAdministrationOptionsCacheKey(courseIds) cacheKey: this.getUserAdministrationOptionsCacheKey(courseIds)
} };
return site.read('core_course_get_user_administration_options', params, preSets).then((response) => { return site.read('core_course_get_user_administration_options', params, preSets).then((response) => {
// Format returned data. // Format returned data.
@ -440,7 +443,7 @@ export class CoreCoursesProvider {
* @param {number[]} courseIds IDs of courses to get. * @param {number[]} courseIds IDs of courses to get.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getUserNavigationOptionsCommonCacheKey() : string { protected getUserNavigationOptionsCommonCacheKey(): string {
return this.ROOT_CACHE_KEY + 'navigationOptions:'; return this.ROOT_CACHE_KEY + 'navigationOptions:';
} }
@ -449,7 +452,7 @@ export class CoreCoursesProvider {
* *
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getUserNavigationOptionsCacheKey(courseIds: number[]) : string { protected getUserNavigationOptionsCacheKey(courseIds: number[]): string {
return this.getUserNavigationOptionsCommonCacheKey() + courseIds.join(','); return this.getUserNavigationOptionsCommonCacheKey() + courseIds.join(',');
} }
@ -460,14 +463,14 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<any>} Promise resolved with navigation options for each course. * @return {Promise<any>} Promise resolved with navigation options for each course.
*/ */
getUserNavigationOptions(courseIds: number[], siteId?: string) : Promise<any> { getUserNavigationOptions(courseIds: number[], siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let params = { const params = {
courseids: courseIds courseids: courseIds
}, },
preSets = { preSets = {
cacheKey: this.getUserNavigationOptionsCacheKey(courseIds) cacheKey: this.getUserNavigationOptionsCacheKey(courseIds)
} };
return site.read('core_course_get_user_navigation_options', params, preSets).then((response) => { return site.read('core_course_get_user_navigation_options', params, preSets).then((response) => {
// Format returned data. // Format returned data.
@ -482,11 +485,11 @@ export class CoreCoursesProvider {
* @param {any[]} courses Navigation or administration options for each course. * @param {any[]} courses Navigation or administration options for each course.
* @return {any} Formatted options. * @return {any} Formatted options.
*/ */
protected formatUserAdminOrNavOptions(courses: any[]) : any { protected formatUserAdminOrNavOptions(courses: any[]): any {
let result = {}; const result = {};
courses.forEach((course) => { courses.forEach((course) => {
let options = {}; const options = {};
if (course.options) { if (course.options) {
course.options.forEach((option) => { course.options.forEach((option) => {
@ -509,14 +512,14 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site to get the courses from. If not defined, use current site. * @param {string} [siteId] Site to get the courses from. If not defined, use current site.
* @return {Promise<any>} Promise resolved with the course. * @return {Promise<any>} Promise resolved with the course.
*/ */
getUserCourse(id: number, preferCache?: boolean, siteId?: string) : Promise<any> { getUserCourse(id: number, preferCache?: boolean, siteId?: string): Promise<any> {
if (!id) { if (!id) {
return Promise.reject(null); return Promise.reject(null);
} }
return this.getUserCourses(preferCache, siteId).then((courses) => { return this.getUserCourses(preferCache, siteId).then((courses) => {
let course; let course;
for (let i in courses) { for (const i in courses) {
if (courses[i].id == id) { if (courses[i].id == id) {
course = courses[i]; course = courses[i];
break; break;
@ -534,10 +537,10 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site to get the courses from. If not defined, use current site. * @param {string} [siteId] Site to get the courses from. If not defined, use current site.
* @return {Promise<any[]>} Promise resolved with the courses. * @return {Promise<any[]>} Promise resolved with the courses.
*/ */
getUserCourses(preferCache?: boolean, siteId?: string) : Promise<any[]> { getUserCourses(preferCache?: boolean, siteId?: string): Promise<any[]> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let userId = site.getUserId(), const userId = site.getUserId(),
data = { data = {
userid: userId userid: userId
}, },
@ -555,7 +558,7 @@ export class CoreCoursesProvider {
* *
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getUserCoursesCacheKey() : string { protected getUserCoursesCacheKey(): string {
return this.ROOT_CACHE_KEY + 'usercourses'; return this.ROOT_CACHE_KEY + 'usercourses';
} }
@ -567,7 +570,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateCategories(categoryId: number, addSubcategories?: boolean, siteId?: string) : Promise<any> { invalidateCategories(categoryId: number, addSubcategories?: boolean, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getCategoriesCacheKey(categoryId, addSubcategories)); return site.invalidateWsCacheForKey(this.getCategoriesCacheKey(categoryId, addSubcategories));
}); });
@ -580,7 +583,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateCourse(id: number, siteId?: string) : Promise<any> { invalidateCourse(id: number, siteId?: string): Promise<any> {
return this.invalidateCourses([id], siteId); return this.invalidateCourses([id], siteId);
} }
@ -591,7 +594,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateCourseEnrolmentMethods(id: number, siteId?: string) : Promise<any> { invalidateCourseEnrolmentMethods(id: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getCourseEnrolmentMethodsCacheKey(id)); return site.invalidateWsCacheForKey(this.getCourseEnrolmentMethodsCacheKey(id));
}); });
@ -604,7 +607,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateCourseGuestEnrolmentInfo(instanceId: number, siteId?: string) : Promise<any> { invalidateCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getCourseGuestEnrolmentInfoCacheKey(instanceId)); return site.invalidateWsCacheForKey(this.getCourseGuestEnrolmentInfoCacheKey(instanceId));
}); });
@ -617,11 +620,11 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID to invalidate. If not defined, use current site. * @param {string} [siteId] Site ID to invalidate. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateCoursesAdminAndNavOptions(courseIds: number[], siteId?: string) : Promise<any> { invalidateCoursesAdminAndNavOptions(courseIds: number[], siteId?: string): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
return this.getCourseIdsForAdminAndNavOptions(courseIds, siteId).then((ids) => { return this.getCourseIdsForAdminAndNavOptions(courseIds, siteId).then((ids) => {
let promises = []; const promises = [];
promises.push(this.invalidateUserAdministrationOptionsForCourses(ids, siteId)); promises.push(this.invalidateUserAdministrationOptionsForCourses(ids, siteId));
promises.push(this.invalidateUserNavigationOptionsForCourses(ids, siteId)); promises.push(this.invalidateUserNavigationOptionsForCourses(ids, siteId));
@ -637,7 +640,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateCourses(ids: number[], siteId?: string) : Promise<any> { invalidateCourses(ids: number[], siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getCoursesCacheKey(ids)); return site.invalidateWsCacheForKey(this.getCoursesCacheKey(ids));
}); });
@ -651,7 +654,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site Id. If not defined, use current site. * @param {string} [siteId] Site Id. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateCoursesByField(field?: string, value?: any, siteId?: string) : Promise<any> { invalidateCoursesByField(field?: string, value?: any, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getCoursesByFieldCacheKey(field, value)); return site.invalidateWsCacheForKey(this.getCoursesByFieldCacheKey(field, value));
}); });
@ -663,7 +666,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID to invalidate. If not defined, use current site. * @param {string} [siteId] Site ID to invalidate. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateUserAdministrationOptions(siteId?: string) : Promise<any> { invalidateUserAdministrationOptions(siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKeyStartingWith(this.getUserAdministrationOptionsCommonCacheKey()); return site.invalidateWsCacheForKeyStartingWith(this.getUserAdministrationOptionsCommonCacheKey());
}); });
@ -676,7 +679,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID to invalidate. If not defined, use current site. * @param {string} [siteId] Site ID to invalidate. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateUserAdministrationOptionsForCourses(courseIds: number[], siteId?: string) : Promise<any> { invalidateUserAdministrationOptionsForCourses(courseIds: number[], siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getUserAdministrationOptionsCacheKey(courseIds)); return site.invalidateWsCacheForKey(this.getUserAdministrationOptionsCacheKey(courseIds));
}); });
@ -688,7 +691,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID to invalidate. If not defined, use current site. * @param {string} [siteId] Site ID to invalidate. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateUserCourses(siteId?: string) : Promise<any> { invalidateUserCourses(siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getUserCoursesCacheKey()); return site.invalidateWsCacheForKey(this.getUserCoursesCacheKey());
}); });
@ -700,7 +703,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID to invalidate. If not defined, use current site. * @param {string} [siteId] Site ID to invalidate. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateUserNavigationOptions(siteId?: string) : Promise<any> { invalidateUserNavigationOptions(siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKeyStartingWith(this.getUserNavigationOptionsCommonCacheKey()); return site.invalidateWsCacheForKeyStartingWith(this.getUserNavigationOptionsCommonCacheKey());
}); });
@ -713,7 +716,7 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID to invalidate. If not defined, use current site. * @param {string} [siteId] Site ID to invalidate. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateUserNavigationOptionsForCourses(courseIds: number[], siteId?: string) : Promise<any> { invalidateUserNavigationOptionsForCourses(courseIds: number[], siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getUserNavigationOptionsCacheKey(courseIds)); return site.invalidateWsCacheForKey(this.getUserNavigationOptionsCacheKey(courseIds));
}); });
@ -724,8 +727,9 @@ export class CoreCoursesProvider {
* *
* @return {boolean} Whether guest WS is available. * @return {boolean} Whether guest WS is available.
*/ */
isGuestWSAvailable() : boolean { isGuestWSAvailable(): boolean {
let currentSite = this.sitesProvider.getCurrentSite(); const currentSite = this.sitesProvider.getCurrentSite();
return currentSite && currentSite.wsAvailable('enrol_guest_get_instance_info'); return currentSite && currentSite.wsAvailable('enrol_guest_get_instance_info');
} }
@ -738,21 +742,22 @@ export class CoreCoursesProvider {
* @param {string} [siteId] Site ID. If not defined, use current site. * @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<{total: number, courses: any[]}>} Promise resolved with the courses and the total of matches. * @return {Promise<{total: number, courses: any[]}>} Promise resolved with the courses and the total of matches.
*/ */
search(text: string, page = 0, perPage?: number, siteId?: string) : Promise<{total: number, courses: any[]}> { search(text: string, page: number = 0, perPage?: number, siteId?: string): Promise<{ total: number, courses: any[] }> {
perPage = perPage || CoreCoursesProvider.SEARCH_PER_PAGE; perPage = perPage || CoreCoursesProvider.SEARCH_PER_PAGE;
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let params = { const params = {
criterianame: 'search', criterianame: 'search',
criteriavalue: text, criteriavalue: text,
page: page, page: page,
perpage: perPage perpage: perPage
}, preSets = { },
preSets = {
getFromCache: false getFromCache: false
} };
return site.read('core_course_search_courses', params, preSets).then((response) => { return site.read('core_course_search_courses', params, preSets).then((response) => {
return {total: response.total, courses: response.courses}; return { total: response.total, courses: response.courses };
}); });
}); });
} }
@ -767,18 +772,19 @@ export class CoreCoursesProvider {
* @return {Promise<any>} Promise resolved if the user is enrolled. If the password is invalid, the promise is rejected * @return {Promise<any>} Promise resolved if the user is enrolled. If the password is invalid, the promise is rejected
* with an object with code = CoreCoursesProvider.ENROL_INVALID_KEY. * with an object with code = CoreCoursesProvider.ENROL_INVALID_KEY.
*/ */
selfEnrol(courseId: number, password = '', instanceId?: number, siteId?: string) : Promise<any> { selfEnrol(courseId: number, password: string = '', instanceId?: number, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let params: any = { const params: any = {
courseid: courseId, courseid: courseId,
password: password password: password
} };
if (instanceId) { if (instanceId) {
params.instanceid = instanceId; params.instanceid = instanceId;
} }
return site.write('enrol_self_enrol_user', params).then((response) : any => { return site.write('enrol_self_enrol_user', params).then((response): any => {
if (response) { if (response) {
if (response.status) { if (response.status) {
return true; return true;
@ -786,18 +792,19 @@ export class CoreCoursesProvider {
let message; let message;
response.warnings.forEach((warning) => { response.warnings.forEach((warning) => {
// Invalid password warnings. // Invalid password warnings.
if (warning.warningcode == '2' || warning.warningcode == '3' || warning.warningcode == '4') { if (warning.warningcode == '2' || warning.warningcode == '3' || warning.warningcode == '4') {
message = warning.message; message = warning.message;
} }
}); });
if (message) { if (message) {
return Promise.reject({code: CoreCoursesProvider.ENROL_INVALID_KEY, message: message}); return Promise.reject({ code: CoreCoursesProvider.ENROL_INVALID_KEY, message: message });
} else { } else {
return Promise.reject(response.warnings[0]); return Promise.reject(response.warnings[0]);
} }
} }
} }
return Promise.reject(null); return Promise.reject(null);
}); });
}); });

View File

@ -26,14 +26,14 @@ export class CoreCoursesMainMenuHandler implements CoreMainMenuHandler {
priority = 1100; priority = 1100;
isOverviewEnabled: boolean; isOverviewEnabled: boolean;
constructor(private coursesProvider: CoreCoursesProvider, private myOverviewProvider: CoreCoursesMyOverviewProvider) {} constructor(private coursesProvider: CoreCoursesProvider, private myOverviewProvider: CoreCoursesMyOverviewProvider) { }
/** /**
* Check if the handler is enabled on a site level. * Check if the handler is enabled on a site level.
* *
* @return {boolean} Whether or not the handler is enabled on a site level. * @return {boolean | Promise<boolean>} Whether or not the handler is enabled on a site level.
*/ */
isEnabled(): boolean|Promise<boolean> { isEnabled(): boolean | Promise<boolean> {
// Check if my overview is enabled. // Check if my overview is enabled.
return this.myOverviewProvider.isEnabled().then((enabled) => { return this.myOverviewProvider.isEnabled().then((enabled) => {
this.isOverviewEnabled = enabled; this.isOverviewEnabled = enabled;

View File

@ -39,10 +39,10 @@ export class CoreCoursesMyOverviewLinkHandler extends CoreContentLinksHandlerBas
* @param {number} [courseId] Course ID related to the URL. Optional but recommended. * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions. * @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
*/ */
getActions(siteIds: string[], url: string, params: any, courseId?: number) : getActions(siteIds: string[], url: string, params: any, courseId?: number):
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return [{ return [{
action: (siteId, navCtrl?) => { action: (siteId, navCtrl?): void => {
// Always use redirect to make it the new history root (to avoid "loops" in history). // Always use redirect to make it the new history root (to avoid "loops" in history).
this.loginHelper.redirect('CoreCoursesMyOverviewPage', undefined, siteId); this.loginHelper.redirect('CoreCoursesMyOverviewPage', undefined, siteId);
} }

View File

@ -22,11 +22,11 @@ import * as moment from 'moment';
*/ */
@Injectable() @Injectable()
export class CoreCoursesMyOverviewProvider { export class CoreCoursesMyOverviewProvider {
public static EVENTS_LIMIT = 20; static EVENTS_LIMIT = 20;
public static EVENTS_LIMIT_PER_COURSE = 10; static EVENTS_LIMIT_PER_COURSE = 10;
protected ROOT_CACHE_KEY = 'myoverview:'; protected ROOT_CACHE_KEY = 'myoverview:';
constructor(private sitesProvider: CoreSitesProvider) {} constructor(private sitesProvider: CoreSitesProvider) { }
/** /**
* Get calendar action events for the given course. * Get calendar action events for the given course.
@ -36,11 +36,11 @@ export class CoreCoursesMyOverviewProvider {
* @param {string} [siteId] Site ID. If not defined, use current site. * @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<{events: any[], canLoadMore: number}>} Promise resolved when the info is retrieved. * @return {Promise<{events: any[], canLoadMore: number}>} Promise resolved when the info is retrieved.
*/ */
getActionEventsByCourse(courseId: number, afterEventId?: number, siteId?: string) : getActionEventsByCourse(courseId: number, afterEventId?: number, siteId?: string):
Promise<{events: any[], canLoadMore: number}> { Promise<{ events: any[], canLoadMore: number }> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let time = moment().subtract(14, 'days').unix(), // Check two weeks ago. const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
data: any = { data: any = {
timesortfrom: time, timesortfrom: time,
courseid: courseId, courseid: courseId,
@ -54,10 +54,11 @@ export class CoreCoursesMyOverviewProvider {
data.aftereventid = afterEventId; data.aftereventid = afterEventId;
} }
return site.read('core_calendar_get_action_events_by_course', data, preSets).then((courseEvents) : any => { return site.read('core_calendar_get_action_events_by_course', data, preSets).then((courseEvents): any => {
if (courseEvents && courseEvents.events) { if (courseEvents && courseEvents.events) {
return this.treatCourseEvents(courseEvents, time); return this.treatCourseEvents(courseEvents, time);
} }
return Promise.reject(null); return Promise.reject(null);
}); });
}); });
@ -69,7 +70,7 @@ export class CoreCoursesMyOverviewProvider {
* @param {number} courseId Only events in this course. * @param {number} courseId Only events in this course.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getActionEventsByCourseCacheKey(courseId: number) : string { protected getActionEventsByCourseCacheKey(courseId: number): string {
return this.getActionEventsByCoursesCacheKey() + ':' + courseId; return this.getActionEventsByCoursesCacheKey() + ':' + courseId;
} }
@ -80,9 +81,10 @@ export class CoreCoursesMyOverviewProvider {
* @param {string} [siteId] Site ID. If not defined, use current site. * @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<{[s: string]: {events: any[], canLoadMore: number}}>} Promise resolved when the info is retrieved. * @return {Promise<{[s: string]: {events: any[], canLoadMore: number}}>} Promise resolved when the info is retrieved.
*/ */
getActionEventsByCourses(courseIds: number[], siteId?: string) : Promise<{[s: string]: {events: any[], canLoadMore: number}}> { getActionEventsByCourses(courseIds: number[], siteId?: string): Promise<{ [s: string]:
{ events: any[], canLoadMore: number } }> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let time = moment().subtract(14, 'days').unix(), // Check two weeks ago. const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
data = { data = {
timesortfrom: time, timesortfrom: time,
courseids: courseIds, courseids: courseIds,
@ -92,9 +94,9 @@ export class CoreCoursesMyOverviewProvider {
cacheKey: this.getActionEventsByCoursesCacheKey() cacheKey: this.getActionEventsByCoursesCacheKey()
}; };
return site.read('core_calendar_get_action_events_by_courses', data, preSets).then((events) : any => { return site.read('core_calendar_get_action_events_by_courses', data, preSets).then((events): any => {
if (events && events.groupedbycourse) { if (events && events.groupedbycourse) {
let courseEvents = {}; const courseEvents = {};
events.groupedbycourse.forEach((course) => { events.groupedbycourse.forEach((course) => {
courseEvents[course.courseid] = this.treatCourseEvents(course, time); courseEvents[course.courseid] = this.treatCourseEvents(course, time);
@ -113,7 +115,7 @@ export class CoreCoursesMyOverviewProvider {
* *
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getActionEventsByCoursesCacheKey() : string { protected getActionEventsByCoursesCacheKey(): string {
return this.ROOT_CACHE_KEY + 'bycourse'; return this.ROOT_CACHE_KEY + 'bycourse';
} }
@ -124,9 +126,9 @@ export class CoreCoursesMyOverviewProvider {
* @param {string} [siteId] Site ID. If not defined, use current site. * @param {string} [siteId] Site ID. If not defined, use current site.
* @return {Promise<{events: any[], canLoadMore: number}>} Promise resolved when the info is retrieved. * @return {Promise<{events: any[], canLoadMore: number}>} Promise resolved when the info is retrieved.
*/ */
getActionEventsByTimesort(afterEventId: number, siteId?: string) : Promise<{events: any[], canLoadMore: number}> { getActionEventsByTimesort(afterEventId: number, siteId?: string): Promise<{ events: any[], canLoadMore: number }> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
let time = moment().subtract(14, 'days').unix(), // Check two weeks ago. const time = moment().subtract(14, 'days').unix(), // Check two weeks ago.
data: any = { data: any = {
timesortfrom: time, timesortfrom: time,
limitnum: CoreCoursesMyOverviewProvider.EVENTS_LIMIT limitnum: CoreCoursesMyOverviewProvider.EVENTS_LIMIT
@ -141,9 +143,9 @@ export class CoreCoursesMyOverviewProvider {
data.aftereventid = afterEventId; data.aftereventid = afterEventId;
} }
return site.read('core_calendar_get_action_events_by_timesort', data, preSets).then((events) : any => { return site.read('core_calendar_get_action_events_by_timesort', data, preSets).then((events): any => {
if (events && events.events) { if (events && events.events) {
let canLoadMore = events.events.length >= data.limitnum ? events.lastid : undefined; const canLoadMore = events.events.length >= data.limitnum ? events.lastid : undefined;
// Filter events by time in case it uses cache. // Filter events by time in case it uses cache.
events = events.events.filter((element) => { events = events.events.filter((element) => {
@ -155,6 +157,7 @@ export class CoreCoursesMyOverviewProvider {
canLoadMore: canLoadMore canLoadMore: canLoadMore
}; };
} }
return Promise.reject(null); return Promise.reject(null);
}); });
}); });
@ -165,7 +168,7 @@ export class CoreCoursesMyOverviewProvider {
* *
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getActionEventsByTimesortPrefixCacheKey() : string { protected getActionEventsByTimesortPrefixCacheKey(): string {
return this.ROOT_CACHE_KEY + 'bytimesort:'; return this.ROOT_CACHE_KEY + 'bytimesort:';
} }
@ -176,9 +179,10 @@ export class CoreCoursesMyOverviewProvider {
* @param {number} [limit] Limit num of the call. * @param {number} [limit] Limit num of the call.
* @return {string} Cache key. * @return {string} Cache key.
*/ */
protected getActionEventsByTimesortCacheKey(afterEventId?: number, limit?: number) : string { protected getActionEventsByTimesortCacheKey(afterEventId?: number, limit?: number): string {
afterEventId = afterEventId || 0; afterEventId = afterEventId || 0;
limit = limit || 0; limit = limit || 0;
return this.getActionEventsByTimesortPrefixCacheKey() + afterEventId + ':' + limit; return this.getActionEventsByTimesortPrefixCacheKey() + afterEventId + ':' + limit;
} }
@ -188,7 +192,7 @@ export class CoreCoursesMyOverviewProvider {
* @param {string} [siteId] Site ID to invalidate. If not defined, use current site. * @param {string} [siteId] Site ID to invalidate. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateActionEventsByCourses(siteId?: string) : Promise<any> { invalidateActionEventsByCourses(siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKeyStartingWith(this.getActionEventsByCoursesCacheKey()); return site.invalidateWsCacheForKeyStartingWith(this.getActionEventsByCoursesCacheKey());
}); });
@ -200,7 +204,7 @@ export class CoreCoursesMyOverviewProvider {
* @param {string} [siteId] Site ID to invalidate. If not defined, use current site. * @param {string} [siteId] Site ID to invalidate. If not defined, use current site.
* @return {Promise<any>} Promise resolved when the data is invalidated. * @return {Promise<any>} Promise resolved when the data is invalidated.
*/ */
invalidateActionEventsByTimesort(siteId?: string) : Promise<any> { invalidateActionEventsByTimesort(siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKeyStartingWith(this.getActionEventsByTimesortPrefixCacheKey()); return site.invalidateWsCacheForKeyStartingWith(this.getActionEventsByTimesortPrefixCacheKey());
}); });
@ -212,7 +216,7 @@ export class CoreCoursesMyOverviewProvider {
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @return {Promise<boolean>} Promise resolved with true if available, resolved with false or rejected otherwise. * @return {Promise<boolean>} Promise resolved with true if available, resolved with false or rejected otherwise.
*/ */
isAvailable(siteId?: string) : Promise<boolean> { isAvailable(siteId?: string): Promise<boolean> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
return site.wsAvailable('core_calendar_get_action_events_by_courses'); return site.wsAvailable('core_calendar_get_action_events_by_courses');
}); });
@ -224,8 +228,9 @@ export class CoreCoursesMyOverviewProvider {
* @param {CoreSite} [site] Site. If not defined, use current site. * @param {CoreSite} [site] Site. If not defined, use current site.
* @return {boolean} Whether it's disabled. * @return {boolean} Whether it's disabled.
*/ */
isDisabledInSite(site?: CoreSite) : boolean { isDisabledInSite(site?: CoreSite): boolean {
site = site || this.sitesProvider.getCurrentSite(); site = site || this.sitesProvider.getCurrentSite();
return site.isFeatureDisabled('$mmSideMenuDelegate_mmaMyOverview'); return site.isFeatureDisabled('$mmSideMenuDelegate_mmaMyOverview');
} }
@ -234,12 +239,13 @@ export class CoreCoursesMyOverviewProvider {
* *
* @return {Promise<boolean>} Promise resolved with true if enabled, resolved with false otherwise. * @return {Promise<boolean>} Promise resolved with true if enabled, resolved with false otherwise.
*/ */
isEnabled() : Promise<boolean> { isEnabled(): Promise<boolean> {
if (!this.isDisabledInSite()) { if (!this.isDisabledInSite()) {
return this.isAvailable().catch(() => { return this.isAvailable().catch(() => {
return false; return false;
}); });
} }
return Promise.resolve(false); return Promise.resolve(false);
} }
@ -250,8 +256,8 @@ export class CoreCoursesMyOverviewProvider {
* @param {number} timeFrom Current time to filter events from. * @param {number} timeFrom Current time to filter events from.
* @return {{events: any[], canLoadMore: number}} Object with course events and last loaded event id if more can be loaded. * @return {{events: any[], canLoadMore: number}} Object with course events and last loaded event id if more can be loaded.
*/ */
protected treatCourseEvents(course: any, timeFrom: number) : {events: any[], canLoadMore: number} { protected treatCourseEvents(course: any, timeFrom: number): { events: any[], canLoadMore: number } {
let canLoadMore : number = const canLoadMore: number =
course.events.length >= CoreCoursesMyOverviewProvider.EVENTS_LIMIT_PER_COURSE ? course.lastid : undefined; course.events.length >= CoreCoursesMyOverviewProvider.EVENTS_LIMIT_PER_COURSE ? course.lastid : undefined;
// Filter events by time in case it uses cache. // Filter events by time in case it uses cache.

View File

@ -29,7 +29,7 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
protected isLinux: boolean; protected isLinux: boolean;
constructor(appProvider: CoreAppProvider, private fileProvider: CoreFileProvider, private urlUtils: CoreUrlUtilsProvider, constructor(appProvider: CoreAppProvider, private fileProvider: CoreFileProvider, private urlUtils: CoreUrlUtilsProvider,
private url: string, target?: string, options = '') { private url: string, target?: string, options: string = '') {
super(url, target, options); super(url, target, options);
if (!appProvider.isDesktop()) { if (!appProvider.isDesktop()) {
@ -44,8 +44,8 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
let width = 800, let width = 800,
height = 600, height = 600,
display, display;
bwOptions: any = {}; const bwOptions: any = {};
if (screen) { if (screen) {
display = this.screen.getPrimaryDisplay(); display = this.screen.getPrimaryDisplay();
@ -74,7 +74,7 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
if (this.isLinux && this.isSSO) { if (this.isLinux && this.isSSO) {
// SSO in Linux. Simulate it's an iOS device so we can retrieve the launch URL. // SSO in Linux. Simulate it's an iOS device so we can retrieve the launch URL.
// This is needed because custom URL scheme is not supported in Linux. // This is needed because custom URL scheme is not supported in Linux.
let userAgent = 'Mozilla/5.0 (iPad) AppleWebKit/603.3.8 (KHTML, like Gecko) Mobile/14G60'; const userAgent = 'Mozilla/5.0 (iPad) AppleWebKit/603.3.8 (KHTML, like Gecko) Mobile/14G60';
this.window.webContents.setUserAgent(userAgent); this.window.webContents.setUserAgent(userAgent);
} }
} }
@ -82,7 +82,7 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
/** /**
* Close the window. * Close the window.
*/ */
close() : void { close(): void {
this.window.close(); this.window.close();
} }
@ -92,8 +92,8 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
* @param {any} details Details of the script to run, specifying either a file or code key. * @param {any} details Details of the script to run, specifying either a file or code key.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
executeScript(details: any) : Promise<any> { executeScript(details: any): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject): void => {
if (details.code) { if (details.code) {
this.window.webContents.executeJavaScript(details.code, false, resolve); this.window.webContents.executeJavaScript(details.code, false, resolve);
} else if (details.file) { } else if (details.file) {
@ -112,11 +112,11 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
* @param {number} [retry=0] Retry number. * @param {number} [retry=0] Retry number.
* @return {Promise<string>} Promise resolved with the launch URL. * @return {Promise<string>} Promise resolved with the launch URL.
*/ */
protected getLaunchUrl(retry = 0) : Promise<string> { protected getLaunchUrl(retry: number = 0): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject): void => {
// Execute Javascript to retrieve the launch link. // Execute Javascript to retrieve the launch link.
let jsCode = 'var el = document.querySelector("#launchapp"); el && el.href;', const jsCode = 'var el = document.querySelector("#launchapp"); el && el.href;';
found = false; let found = false;
this.window.webContents.executeJavaScript(jsCode).then((launchUrl) => { this.window.webContents.executeJavaScript(jsCode).then((launchUrl) => {
found = true; found = true;
@ -140,7 +140,7 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
/** /**
* Hide the window. * Hide the window.
*/ */
hide() : void { hide(): void {
this.window.hide(); this.window.hide();
} }
@ -150,8 +150,8 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
* @param {any} details Details of the CSS to insert, specifying either a file or code key. * @param {any} details Details of the CSS to insert, specifying either a file or code key.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
insertCSS(details: any) : Promise<any> { insertCSS(details: any): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject): void => {
if (details.code) { if (details.code) {
this.window.webContents.insertCSS(details.code); this.window.webContents.insertCSS(details.code);
resolve(); resolve();
@ -173,18 +173,20 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
* @return {Observable<InAppBrowserEvent>} Observable that will listen to the event on subscribe, and will stop listening * @return {Observable<InAppBrowserEvent>} Observable that will listen to the event on subscribe, and will stop listening
* to the event on unsubscribe. * to the event on unsubscribe.
*/ */
on(name: string) : Observable<InAppBrowserEvent> { on(name: string): Observable<InAppBrowserEvent> {
// Create the observable. // Create the observable.
return new Observable<InAppBrowserEvent>((observer: Observer<InAppBrowserEvent>) => { return new Observable<InAppBrowserEvent>((observer: Observer<InAppBrowserEvent>): any => {
// Helper functions to handle events. // Helper functions to handle events.
let received = (event, url) => { const received = (event, url): void => {
try { try {
event.url = url || this.window.getURL(); event.url = url || this.window.getURL();
event.type = name; event.type = name;
observer.next(event); observer.next(event);
} catch(ex) {} } catch (ex) {
// Ignore errors.
}
}, },
finishLoad = (event) => { finishLoad = (event): void => {
// Check if user is back to launch page. // Check if user is back to launch page.
if (this.urlUtils.removeUrlParams(this.url) == this.urlUtils.removeUrlParams(this.window.getURL())) { if (this.urlUtils.removeUrlParams(this.url) == this.urlUtils.removeUrlParams(this.window.getURL())) {
// The launch page was loaded. Search for the launch link. // The launch page was loaded. Search for the launch link.
@ -203,7 +205,6 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
if (this.isLinux && this.isSSO) { if (this.isLinux && this.isSSO) {
// Linux doesn't support custom URL Schemes. Check if launch page is loaded. // Linux doesn't support custom URL Schemes. Check if launch page is loaded.
// listeners[callback].push(finishLoad);
this.window.webContents.on('did-finish-load', finishLoad); this.window.webContents.on('did-finish-load', finishLoad);
} }
break; break;
@ -218,9 +219,10 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
case 'exit': case 'exit':
this.window.on('close', received); this.window.on('close', received);
break; break;
default:
} }
return () => { return (): void => {
// Unsubscribing. We need to remove the listeners. // Unsubscribing. We need to remove the listeners.
switch (name) { switch (name) {
case 'loadstart': case 'loadstart':
@ -239,6 +241,7 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
case 'exit': case 'exit':
this.window.removeListener('close', received); this.window.removeListener('close', received);
break; break;
default:
} }
}; };
}); });
@ -247,7 +250,7 @@ export class InAppBrowserObjectMock extends InAppBrowserObject {
/** /**
* Show the window. * Show the window.
*/ */
show() : void { show(): void {
this.window.show(); this.window.show();
} }
} }

View File

@ -34,7 +34,7 @@ export class SQLiteDBMock extends SQLiteDB {
* *
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
close() : Promise<any> { close(): Promise<any> {
// WebSQL databases aren't closed. // WebSQL databases aren't closed.
return Promise.resolve(); return Promise.resolve();
} }
@ -44,11 +44,11 @@ export class SQLiteDBMock extends SQLiteDB {
* *
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
emptyDatabase() : Promise<any> { emptyDatabase(): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject): void => {
this.db.transaction((tx) => { this.db.transaction((tx) => {
// Query all tables from sqlite_master that we have created and can modify. // Query all tables from sqlite_master that we have created and can modify.
let args = [], const args = [],
query = `SELECT * FROM sqlite_master query = `SELECT * FROM sqlite_master
WHERE name NOT LIKE 'sqlite\\_%' escape '\\' AND name NOT LIKE '\\_%' escape '\\'`; WHERE name NOT LIKE 'sqlite\\_%' escape '\\' AND name NOT LIKE '\\_%' escape '\\'`;
@ -56,16 +56,17 @@ export class SQLiteDBMock extends SQLiteDB {
if (result.rows.length <= 0) { if (result.rows.length <= 0) {
// No tables to delete, stop. // No tables to delete, stop.
resolve(); resolve();
return; return;
} }
// Drop all the tables. // Drop all the tables.
let promises = []; const promises = [];
for (let i = 0; i < result.rows.length; i++) { for (let i = 0; i < result.rows.length; i++) {
promises.push(new Promise((resolve, reject) => { promises.push(new Promise((resolve, reject): void => {
// Drop the table. // Drop the table.
let name = JSON.stringify(result.rows.item(i).name); const name = JSON.stringify(result.rows.item(i).name);
tx.executeSql('DROP TABLE ' + name, [], resolve, reject); tx.executeSql('DROP TABLE ' + name, [], resolve, reject);
})); }));
} }
@ -85,8 +86,8 @@ export class SQLiteDBMock extends SQLiteDB {
* @param {any[]} params Query parameters. * @param {any[]} params Query parameters.
* @return {Promise<any>} Promise resolved with the result. * @return {Promise<any>} Promise resolved with the result.
*/ */
execute(sql: string, params?: any[]) : Promise<any> { execute(sql: string, params?: any[]): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject): void => {
// With WebSQL, all queries must be run in a transaction. // With WebSQL, all queries must be run in a transaction.
this.db.transaction((tx) => { this.db.transaction((tx) => {
tx.executeSql(sql, params, (tx, results) => { tx.executeSql(sql, params, (tx, results) => {
@ -106,15 +107,15 @@ export class SQLiteDBMock extends SQLiteDB {
* @param {any[]} sqlStatements SQL statements to execute. * @param {any[]} sqlStatements SQL statements to execute.
* @return {Promise<any>} Promise resolved with the result. * @return {Promise<any>} Promise resolved with the result.
*/ */
executeBatch(sqlStatements: any[]) : Promise<any> { executeBatch(sqlStatements: any[]): Promise<any> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject): void => {
// Create a transaction to execute the queries. // Create a transaction to execute the queries.
this.db.transaction((tx) => { this.db.transaction((tx) => {
let promises = []; const promises = [];
// Execute all the queries. Each statement can be a string or an array. // Execute all the queries. Each statement can be a string or an array.
sqlStatements.forEach((statement) => { sqlStatements.forEach((statement) => {
promises.push(new Promise((resolve, reject) => { promises.push(new Promise((resolve, reject): void => {
let query, let query,
params; params;
@ -142,7 +143,7 @@ export class SQLiteDBMock extends SQLiteDB {
*/ */
init(): void { init(): void {
// This DB is for desktop apps, so use a big size to be sure it isn't filled. // This DB is for desktop apps, so use a big size to be sure it isn't filled.
this.db = (<any>window).openDatabase(this.name, '1.0', this.name, 500 * 1024 * 1024); this.db = (<any> window).openDatabase(this.name, '1.0', this.name, 500 * 1024 * 1024);
this.promise = Promise.resolve(); this.promise = Promise.resolve();
} }
@ -151,7 +152,7 @@ export class SQLiteDBMock extends SQLiteDB {
* *
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
open() : Promise<any> { open(): Promise<any> {
// WebSQL databases can't closed, so the open method isn't needed. // WebSQL databases can't closed, so the open method isn't needed.
return Promise.resolve(); return Promise.resolve();
} }

View File

@ -73,21 +73,21 @@ import { CoreInitDelegate } from '../../providers/init';
{ {
provide: Camera, provide: Camera,
deps: [CoreAppProvider, CoreEmulatorCaptureHelperProvider], deps: [CoreAppProvider, CoreEmulatorCaptureHelperProvider],
useFactory: (appProvider: CoreAppProvider, captureHelper: CoreEmulatorCaptureHelperProvider) => { useFactory: (appProvider: CoreAppProvider, captureHelper: CoreEmulatorCaptureHelperProvider): Camera => {
return appProvider.isMobile() ? new Camera() : new CameraMock(captureHelper); return appProvider.isMobile() ? new Camera() : new CameraMock(captureHelper);
} }
}, },
{ {
provide: Clipboard, provide: Clipboard,
deps: [CoreAppProvider], deps: [CoreAppProvider],
useFactory: (appProvider: CoreAppProvider) => { useFactory: (appProvider: CoreAppProvider): Clipboard => {
return appProvider.isMobile() ? new Clipboard() : new ClipboardMock(appProvider); return appProvider.isMobile() ? new Clipboard() : new ClipboardMock(appProvider);
} }
}, },
{ {
provide: File, provide: File,
deps: [CoreAppProvider, CoreTextUtilsProvider], deps: [CoreAppProvider, CoreTextUtilsProvider],
useFactory: (appProvider: CoreAppProvider, textUtils: CoreTextUtilsProvider) => { useFactory: (appProvider: CoreAppProvider, textUtils: CoreTextUtilsProvider): File => {
// Use platform instead of CoreAppProvider to prevent circular dependencies. // Use platform instead of CoreAppProvider to prevent circular dependencies.
return appProvider.isMobile() ? new File() : new FileMock(appProvider, textUtils); return appProvider.isMobile() ? new File() : new FileMock(appProvider, textUtils);
} }
@ -95,7 +95,7 @@ import { CoreInitDelegate } from '../../providers/init';
{ {
provide: FileTransfer, provide: FileTransfer,
deps: [CoreAppProvider, CoreFileProvider], deps: [CoreAppProvider, CoreFileProvider],
useFactory: (appProvider: CoreAppProvider, fileProvider: CoreFileProvider) => { useFactory: (appProvider: CoreAppProvider, fileProvider: CoreFileProvider): FileTransfer => {
// Use platform instead of CoreAppProvider to prevent circular dependencies. // Use platform instead of CoreAppProvider to prevent circular dependencies.
return appProvider.isMobile() ? new FileTransfer() : new FileTransferMock(appProvider, fileProvider); return appProvider.isMobile() ? new FileTransfer() : new FileTransferMock(appProvider, fileProvider);
} }
@ -103,14 +103,15 @@ import { CoreInitDelegate } from '../../providers/init';
{ {
provide: Globalization, provide: Globalization,
deps: [CoreAppProvider], deps: [CoreAppProvider],
useFactory: (appProvider: CoreAppProvider) => { useFactory: (appProvider: CoreAppProvider): Globalization => {
return appProvider.isMobile() ? new Globalization() : new GlobalizationMock(appProvider); return appProvider.isMobile() ? new Globalization() : new GlobalizationMock(appProvider);
} }
}, },
{ {
provide: InAppBrowser, provide: InAppBrowser,
deps: [CoreAppProvider, CoreFileProvider, CoreUrlUtilsProvider], deps: [CoreAppProvider, CoreFileProvider, CoreUrlUtilsProvider],
useFactory: (appProvider: CoreAppProvider, fileProvider: CoreFileProvider, urlUtils: CoreUrlUtilsProvider) => { useFactory: (appProvider: CoreAppProvider, fileProvider: CoreFileProvider, urlUtils: CoreUrlUtilsProvider)
: InAppBrowser => {
return !appProvider.isDesktop() ? new InAppBrowser() : new InAppBrowserMock(appProvider, fileProvider, urlUtils); return !appProvider.isDesktop() ? new InAppBrowser() : new InAppBrowserMock(appProvider, fileProvider, urlUtils);
} }
}, },
@ -118,7 +119,7 @@ import { CoreInitDelegate } from '../../providers/init';
{ {
provide: LocalNotifications, provide: LocalNotifications,
deps: [CoreAppProvider, CoreUtilsProvider], deps: [CoreAppProvider, CoreUtilsProvider],
useFactory: (appProvider: CoreAppProvider, utils: CoreUtilsProvider) => { useFactory: (appProvider: CoreAppProvider, utils: CoreUtilsProvider): LocalNotifications => {
// Use platform instead of CoreAppProvider to prevent circular dependencies. // Use platform instead of CoreAppProvider to prevent circular dependencies.
return appProvider.isMobile() ? new LocalNotifications() : new LocalNotificationsMock(appProvider, utils); return appProvider.isMobile() ? new LocalNotifications() : new LocalNotificationsMock(appProvider, utils);
} }
@ -126,14 +127,14 @@ import { CoreInitDelegate } from '../../providers/init';
{ {
provide: MediaCapture, provide: MediaCapture,
deps: [CoreAppProvider, CoreEmulatorCaptureHelperProvider], deps: [CoreAppProvider, CoreEmulatorCaptureHelperProvider],
useFactory: (appProvider: CoreAppProvider, captureHelper: CoreEmulatorCaptureHelperProvider) => { useFactory: (appProvider: CoreAppProvider, captureHelper: CoreEmulatorCaptureHelperProvider): MediaCapture => {
return appProvider.isMobile() ? new MediaCapture() : new MediaCaptureMock(captureHelper); return appProvider.isMobile() ? new MediaCapture() : new MediaCaptureMock(captureHelper);
} }
}, },
{ {
provide: Network, provide: Network,
deps: [Platform], deps: [Platform],
useFactory: (platform: Platform) => { useFactory: (platform: Platform): Network => {
// Use platform instead of CoreAppProvider to prevent circular dependencies. // Use platform instead of CoreAppProvider to prevent circular dependencies.
return platform.is('cordova') ? new Network() : new NetworkMock(); return platform.is('cordova') ? new Network() : new NetworkMock();
} }
@ -144,7 +145,7 @@ import { CoreInitDelegate } from '../../providers/init';
{ {
provide: Zip, provide: Zip,
deps: [CoreAppProvider, File, CoreMimetypeUtilsProvider, CoreTextUtilsProvider], deps: [CoreAppProvider, File, CoreMimetypeUtilsProvider, CoreTextUtilsProvider],
useFactory: (appProvider: CoreAppProvider, file: File, mimeUtils: CoreMimetypeUtilsProvider) => { useFactory: (appProvider: CoreAppProvider, file: File, mimeUtils: CoreMimetypeUtilsProvider): Zip => {
// Use platform instead of CoreAppProvider to prevent circular dependencies. // Use platform instead of CoreAppProvider to prevent circular dependencies.
return appProvider.isMobile() ? new Zip() : new ZipMock(file, mimeUtils); return appProvider.isMobile() ? new Zip() : new ZipMock(file, mimeUtils);
} }
@ -153,7 +154,7 @@ import { CoreInitDelegate } from '../../providers/init';
}) })
export class CoreEmulatorModule { export class CoreEmulatorModule {
constructor(appProvider: CoreAppProvider, initDelegate: CoreInitDelegate, helper: CoreEmulatorHelperProvider) { constructor(appProvider: CoreAppProvider, initDelegate: CoreInitDelegate, helper: CoreEmulatorHelperProvider) {
let win = <any>window; // Convert the "window" to "any" type to be able to use non-standard properties. const win = <any> window; // Convert the "window" to "any" type to be able to use non-standard properties.
// Emulate Custom URL Scheme plugin in desktop apps. // Emulate Custom URL Scheme plugin in desktop apps.
if (appProvider.isDesktop()) { if (appProvider.isDesktop()) {

Some files were not shown because too many files have changed in this diff Show More