From b09cdae3cd39adecbc9fd0be4f9e7149749fc851 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Mon, 11 Dec 2017 11:38:13 +0100 Subject: [PATCH] MOBILE-2302 courses: Implement CoreCoursesProvider --- src/app/app.module.ts | 4 +- src/core/courses/courses.module.ts | 27 + src/core/courses/providers/courses.ts | 811 ++++++++++++++++++++++++++ 3 files changed, 841 insertions(+), 1 deletion(-) create mode 100644 src/core/courses/courses.module.ts create mode 100644 src/core/courses/providers/courses.ts diff --git a/src/app/app.module.ts b/src/app/app.module.ts index bdea0e895..7bd5875e5 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -55,6 +55,7 @@ import { CorePluginFileDelegate } from '../providers/plugin-file-delegate'; import { CoreLoginModule } from '../core/login/login.module'; import { CoreMainMenuModule } from '../core/mainmenu/mainmenu.module'; +import { CoreCoursesModule } from '../core/courses/courses.module'; // For translate loader. AoT requires an exported function for factories. export function createTranslateLoader(http: HttpClient) { @@ -81,7 +82,8 @@ export function createTranslateLoader(http: HttpClient) { }), CoreEmulatorModule, CoreLoginModule, - CoreMainMenuModule + CoreMainMenuModule, + CoreCoursesModule ], bootstrap: [IonicApp], entryComponents: [ diff --git a/src/core/courses/courses.module.ts b/src/core/courses/courses.module.ts new file mode 100644 index 000000000..96e18c1ec --- /dev/null +++ b/src/core/courses/courses.module.ts @@ -0,0 +1,27 @@ +// (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 { NgModule } from '@angular/core'; +import { CoreCoursesProvider } from './providers/courses'; + +@NgModule({ + declarations: [ + ], + imports: [ + ], + providers: [ + CoreCoursesProvider + ] +}) +export class CoreCoursesModule {} diff --git a/src/core/courses/providers/courses.ts b/src/core/courses/providers/courses.ts new file mode 100644 index 000000000..fb2cd80ee --- /dev/null +++ b/src/core/courses/providers/courses.ts @@ -0,0 +1,811 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; +import { CoreLoggerProvider } from '../../../providers/logger'; +import { CoreSitesProvider } from '../../../providers/sites'; +import { CoreSite } from '../../../classes/site'; + +/** + * Service that provides some features regarding lists of courses and categories. + */ +@Injectable() +export class CoreCoursesProvider { + public static SEARCH_PER_PAGE = 20; + public static ENROL_INVALID_KEY = 'CoreCoursesEnrolInvalidKey'; + protected logger; + + constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider) { + this.logger = logger.getInstance('CoreCoursesProvider'); + } + + /** + * Get categories. They can be filtered by id. + * + * @param {number} categoryId Category ID to get. + * @param {boolean} [addSubcategories] If it should add subcategories to the list. + * @param {string} [siteId] Site to get the courses from. If not defined, use current site. + * @return {Promise} Promise resolved with the categories. + */ + getCategories(categoryId: number, addSubcategories?: boolean, siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + // Get parent when id is the root category. + let criteriaKey = categoryId == 0 ? 'parent' : 'id', + data = { + criteria: [ + { key: criteriaKey, value: categoryId } + ], + addsubcategories: addSubcategories ? 1 : 0 + }, + preSets = { + cacheKey: this.getCategoriesCacheKey(categoryId, addSubcategories) + } + + return site.read('core_course_get_categories', data, preSets); + }); + } + + /** + * Get cache key for get categories methods WS call. + * + * @param {number} categoryId Category ID to get. + * @param {boolean} [addSubcategories] If add subcategories to the list. + * @return {string} Cache key. + */ + protected getCategoriesCacheKey(categoryId: number, addSubcategories?: boolean) : string { + return this.getRootCacheKey() + 'categories:' + categoryId + ':' + !!addSubcategories; + } + + /** + * Given a list of course IDs to get course options, return the list of courseIds to use. + * + * @param {number[]} courseIds Course IDs. + * @param {string} [siteId] Site Id. If not defined, use current site. + * @return {Promise} Promise resolved with the list of course IDs. + */ + protected getCourseIdsForOptions(courseIds: number[], siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + const siteHomeId = site.getSiteHomeId(); + + if (courseIds.length == 1) { + // Only 1 course, check if it belongs to the user courses. If so, use all user courses. + return this.getUserCourses(true, siteId).then((courses) => { + let courseId = courseIds[0], + useAllCourses = false; + + if (courseId == siteHomeId) { + // It's site home, use all courses. + useAllCourses = true; + } else { + for (let i = 0; i < courses.length; i++) { + if (courses[i].id == courseId) { + useAllCourses = true; + break; + } + } + } + + if (useAllCourses) { + // User is enrolled, retrieve all the courses. + courseIds = courses.map((course) => { + return course.id; + }); + + // Always add the site home ID. + courseIds.push(siteHomeId); + } + + return courseIds; + }).catch(() => { + // Ignore errors. + return courseIds; + }); + } else { + return courseIds; + } + }); + } + + /** + * Get the root cache key for the WS calls related to courses. + * + * @return {string} Root cache key. + */ + protected getRootCacheKey() : string { + return 'mmCourses:'; + } + + /** + * Check if My Courses is disabled in a certain site. + * + * @param {string} [siteId] Site Id. If not defined, use current site. + * @return {Promise} Promise resolved with true if disabled, rejected or resolved with false otherwise. + */ + isMyCoursesDisabled(siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return this.isMyCoursesDisabledInSite(site); + }); + } + + /** + * Check if My Courses is disabled in a certain site. + * + * @param {CoreSite} [site] Site. If not defined, use current site. + * @return {boolean} Whether it's disabled. + */ + isMyCoursesDisabledInSite(site: CoreSite) : boolean { + site = site || this.sitesProvider.getCurrentSite(); + return site.isFeatureDisabled('$mmSideMenuDelegate_mmCourses'); + } + + /** + * Check if Search Courses is disabled in a certain site. + * + * @param {string} [siteId] Site Id. If not defined, use current site. + * @return {Promise} Promise resolved with true if disabled, rejected or resolved with false otherwise. + */ + isSearchCoursesDisabled(siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return this.isSearchCoursesDisabledInSite(site); + }); + } + + /** + * Check if Search Courses is disabled in a certain site. + * + * @param {CoreSite} [site] Site. If not defined, use current site. + * @return {boolean} Whether it's disabled. + */ + isSearchCoursesDisabledInSite(site: CoreSite) : boolean { + site = site || this.sitesProvider.getCurrentSite(); + return site.isFeatureDisabled('$mmCoursesDelegate_search'); + } + + /** + * Get course. + * + * @param {number} id ID of the course to get. + * @param {string} [siteId] Site to get the courses from. If not defined, use current site. + * @return {Promise} Promise resolved with the course. + */ + getCourse(id: number, siteId?: string) : Promise { + return this.getCourses([id], siteId).then((courses) => { + if (courses && courses.length > 0) { + return courses[0]; + } + return Promise.reject(null); + }); + } + + /** + * Get the enrolment methods from a course. + * + * @param {number} id ID of the course. + * @param {string} [siteId] Site ID. If not defined, use current site. + * @return {Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + let params = { + courseid: id + }, + preSets = { + cacheKey: this.getCourseEnrolmentMethodsCacheKey(id) + } + + return site.read('core_enrol_get_course_enrolment_methods', params, preSets); + }); + } + + /** + * Get cache key for get course enrolment methods WS call. + * + * @param {number} id Course ID. + * @return {string} Cache key. + */ + protected getCourseEnrolmentMethodsCacheKey(id: number) : string { + return this.getRootCacheKey() + 'enrolmentmethods:' + id; + } + + /** + * Get info from a course guest enrolment method. + * + * @param {number} instanceId Guest instance ID. + * @param {string} [siteId] Site ID. If not defined, use current site. + * @return {Promise} Promise resolved when the info is retrieved. + */ + getCourseGuestEnrolmentInfo(instanceId: number, siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + let params = { + instanceid: instanceId + }, + preSets = { + cacheKey: this.getCourseGuestEnrolmentInfoCacheKey(instanceId) + } + + return site.read('enrol_guest_get_instance_info', params, preSets).then((response) => { + return response.instanceinfo; + }); + }); + } + + /** + * Get cache key for get course guest enrolment methods WS call. + * + * @param {number} instanceId Guest instance ID. + * @return {string} Cache key. + */ + protected getCourseGuestEnrolmentInfoCacheKey(instanceId: number) : string { + return this.getRootCacheKey() + 'guestinfo:' + instanceId; + } + + /** + * Get courses. + * Warning: if the user doesn't have permissions to view some of the courses passed the WS call will fail. + * The user must be able to view ALL the courses passed. + * + * @param {number[]} ids List of IDs of the courses to get. + * @param {string} [siteId] Site to get the courses from. If not defined, use current site. + * @return {Promise} Promise resolved with the courses. + */ + getCourses(ids: number[], siteId?: string) : Promise { + if (!Array.isArray(ids)) { + return Promise.reject(null); + } else if (ids.length === 0) { + return Promise.resolve([]); + } + + return this.sitesProvider.getSite(siteId).then((site) => { + let data = { + options: { + ids: ids + } + }, + preSets = { + cacheKey: this.getCoursesCacheKey(ids) + } + + return site.read('core_course_get_courses', data, preSets); + }); + } + + /** + * Get cache key for get courses WS call. + * + * @param {number[]} ids Courses IDs. + * @return {string} Cache key. + */ + protected getCoursesCacheKey(ids: number[]) : string { + return this.getRootCacheKey() + 'course:' + JSON.stringify(ids); + } + + /** + * Get courses. They can be filtered by field. + * + * @param {string} [field] The field to search. Can be left empty for all courses or: + * id: course id. + * ids: comma separated course ids. + * shortname: course short name. + * idnumber: course id number. + * category: category id the course belongs to. + * @param {any} [value] The value to match. + * @param {string} [siteId] Site ID. If not defined, use current site. + * @return {Promise} Promise resolved with the courses. + */ + getCoursesByField(field?: string, value?: any, siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + let data = { + field: field || '', + value: field ? value : '' + }, + preSets = { + cacheKey: this.getCoursesByFieldCacheKey(field, value) + } + + return site.read('core_course_get_courses_by_field', data, preSets).then((courses) => { + if (courses.courses) { + // Courses will be sorted using sortorder if avalaible. + return courses.courses.sort((a, b) => { + if (typeof a.sortorder == 'undefined' && typeof b.sortorder == 'undefined') { + return b.id - a.id; + } + + if (typeof a.sortorder == 'undefined') { + return 1; + } + + if (typeof b.sortorder == 'undefined') { + return -1; + } + + return a.sortorder - b.sortorder; + }); + } + + return Promise.reject(null); + }); + }); + } + + /** + * Get cache key for get courses WS call. + * + * @param {string} [field] The field to search. + * @param {any} [value] The value to match. + * @return {string} Cache key. + */ + protected getCoursesByFieldCacheKey(field?: string, value?: any) : string { + field = field || ''; + value = field ? value : ''; + return this.getRootCacheKey() + 'coursesbyfield:' + field + ':' + value; + } + + /** + * Check if get courses by field WS is available. + * + * @return {boolean} Whether get courses by field is available. + */ + isGetCoursesByFieldAvailable() : boolean { + let currentSite = this.sitesProvider.getCurrentSite(); + return currentSite.wsAvailable('core_course_get_courses_by_field'); + } + + /** + * Get the navigation and administration options for the given courses. + * + * @param {number[]} courseIds IDs of courses to get. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise<{navOptions: any, admOptions: any}>} Promise resolved with the options for each course. + */ + getCoursesOptions(courseIds: number[], siteId?: string) : Promise<{navOptions: any, admOptions: any}> { + siteId = siteId || this.sitesProvider.getCurrentSiteId(); + + // Get the list of courseIds to use based on the param. + return this.getCourseIdsForOptions(courseIds, siteId).then((courseIds) => { + let promises = [], + navOptions, + admOptions; + + // Get user navigation and administration options. + promises.push(this.getUserNavigationOptions(courseIds, siteId).catch(() => { + // Couldn't get it, return empty options. + return {}; + }).then((options) => { + navOptions = options; + })); + + promises.push(this.getUserAdministrationOptions(courseIds, siteId).catch(() => { + // Couldn't get it, return empty options. + return {}; + }).then((options) => { + admOptions = options; + })); + + return Promise.all(promises).then(() => { + return {navOptions: navOptions, admOptions: admOptions}; + }); + }); + } + + /** + * Get the common part of the cache keys for user administration options WS calls. + * + * @return {string} Cache key. + */ + protected getUserAdministrationOptionsCommonCacheKey() : string { + return this.getRootCacheKey() + 'administrationOptions:'; + } + + /** + * Get cache key for get user administration options WS call. + * + * @param {number[]} courseIds IDs of courses to get. + * @return {string} Cache key. + */ + protected getUserAdministrationOptionsCacheKey(courseIds: number[]) : string { + return this.getUserAdministrationOptionsCommonCacheKey() + courseIds.join(','); + } + + /** + * Get user administration options for a set of courses. + * + * @param {number[]} courseIds IDs of courses to get. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with administration options for each course. + */ + getUserAdministrationOptions(courseIds: number[], siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + let params = { + courseids: courseIds + }, + preSets = { + cacheKey: this.getUserAdministrationOptionsCacheKey(courseIds) + } + + return site.read('core_course_get_user_administration_options', params, preSets).then((response) => { + // Format returned data. + return this.formatUserOptions(response.courses); + }); + }); + } + + /** + * Get the common part of the cache keys for user navigation options WS calls. + * + * @param {number[]} courseIds IDs of courses to get. + * @return {string} Cache key. + */ + protected getUserNavigationOptionsCommonCacheKey() : string { + return this.getRootCacheKey() + 'navigationOptions:'; + } + + /** + * Get cache key for get user navigation options WS call. + * + * @return {string} Cache key. + */ + protected getUserNavigationOptionsCacheKey(courseIds: number[]) : string { + return this.getUserNavigationOptionsCommonCacheKey() + courseIds.join(','); + } + + /** + * Get user navigation options for a set of courses. + * + * @param {number[]} courseIds IDs of courses to get. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with navigation options for each course. + */ + getUserNavigationOptions(courseIds: number[], siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + let params = { + courseids: courseIds + }, + preSets = { + cacheKey: this.getUserNavigationOptionsCacheKey(courseIds) + } + + return site.read('core_course_get_user_navigation_options', params, preSets).then((response) => { + // Format returned data. + return this.formatUserOptions(response.courses); + }); + }); + } + + /** + * Format user navigation or administration options. + * + * @param {any[]} courses Navigation or administration options for each course. + * @return {any} Formatted options. + */ + protected formatUserOptions(courses: any[]) : any { + let result = {}; + + courses.forEach((course) => { + let options = {}; + + if (course.options) { + course.options.forEach((option) => { + options[option.name] = option.available; + }); + } + + result[course.id] = options; + }); + + return result; + } + + /** + * Get a course the user is enrolled in. This function relies on getUserCourses. + * preferCache=true will try to speed up the response, but the data returned might not be updated. + * + * @param {number} id ID of the course to get. + * @param {boolean} [preferCache] True if shouldn't call WS if data is cached, false otherwise. + * @param {string} [siteId] Site to get the courses from. If not defined, use current site. + * @return {Promise} Promise resolved with the course. + */ + getUserCourse(id: number, preferCache?: boolean, siteId?: string) : Promise { + if (!id) { + return Promise.reject(null); + } + + return this.getUserCourses(preferCache, siteId).then((courses) => { + let course; + for (let i in courses) { + if (courses[i].id == id) { + course = courses[i]; + break; + } + } + + return course ? course : Promise.reject(null); + }); + } + + /** + * Get user courses. + * + * @param {boolean} [preferCache] True if shouldn't call WS if data is cached, false otherwise. + * @param {string} [siteId] Site to get the courses from. If not defined, use current site. + * @return {Promise} Promise resolved with the courses. + */ + getUserCourses(preferCache?: boolean, siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + + let userId = site.getUserId(), + data = { + userid: userId + }, + preSets = { + cacheKey: this.getUserCoursesCacheKey(), + omitExpires: !!preferCache + }; + + return site.read('core_enrol_get_users_courses', data, preSets); + }); + } + + /** + * Get cache key for get user courses WS call. + * + * @return {string} Cache key. + */ + protected getUserCoursesCacheKey() : string { + return this.getRootCacheKey() + 'usercourses'; + } + + /** + * Invalidates get categories WS call. + * + * @param {number} categoryId Category ID to get. + * @param {boolean} [addSubcategories] If it should add subcategories to the list. + * @param {string} [siteId] Site Id. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateCategories(categoryId: number, addSubcategories?: boolean, siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getCategoriesCacheKey(categoryId, addSubcategories)); + }); + } + + /** + * Invalidates get course WS call. + * + * @param {number} id Course ID. + * @param {string} [siteId] Site Id. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateCourse(id: number, siteId?: string) : Promise { + return this.invalidateCourses([id], siteId); + } + + /** + * Invalidates get course enrolment methods WS call. + * + * @param {number} id Course ID. + * @param {string} [siteId] Site Id. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateCourseEnrolmentMethods(id: number, siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getCourseEnrolmentMethodsCacheKey(id)); + }); + } + + /** + * Invalidates get course guest enrolment info WS call. + * + * @param {number} instanceId Guest instance ID. + * @param {string} [siteId] Site Id. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateCourseGuestEnrolmentInfo(instanceId: number, siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getCourseGuestEnrolmentInfoCacheKey(instanceId)); + }); + } + + /** + * Invalidates the navigation and administration options for the given courses. + * + * @param {number[]} courseIds IDs of courses to get. + * @param {string} [siteId] Site ID to invalidate. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateCoursesOptions(courseIds: number[], siteId?: string) : Promise { + siteId = siteId || this.sitesProvider.getCurrentSiteId(); + + return this.getCourseIdsForOptions(courseIds, siteId).then((ids) => { + let promises = []; + + promises.push(this.invalidateUserAdministrationOptionsForCourses(ids, siteId)); + promises.push(this.invalidateUserNavigationOptionsForCourses(ids, siteId)); + + return Promise.all(promises); + }); + } + + /** + * Invalidates get courses WS call. + * + * @param {number[]} ids Courses IDs. + * @param {string} [siteId] Site Id. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateCourses(ids: number[], siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getCoursesCacheKey(ids)); + }); + } + + /** + * Invalidates get courses by field WS call. + * + * @param {string} [field] See getCoursesByField for info. + * @param {any} [value] The value to match. + * @param {string} [siteId] Site Id. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateCoursesByField(field?: string, value?: any, siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getCoursesByFieldCacheKey(field, value)); + }); + } + + /** + * Invalidates all user administration options. + * + * @param {string} [siteId] Site ID to invalidate. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateUserAdministrationOptions(siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKeyStartingWith(this.getUserAdministrationOptionsCommonCacheKey()); + }); + } + + /** + * Invalidates user administration options for certain courses. + * + * @param {number[]} courseIds IDs of courses. + * @param {string} [siteId] Site ID to invalidate. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateUserAdministrationOptionsForCourses(courseIds: number[], siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getUserAdministrationOptionsCacheKey(courseIds)); + }); + } + + /** + * Invalidates get user courses WS call. + * + * @param {string} [siteId] Site ID to invalidate. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateUserCourses(siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getUserCoursesCacheKey()); + }); + } + + /** + * Invalidates all user navigation options. + * + * @param {string} [siteId] Site ID to invalidate. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateUserNavigationOptions(siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKeyStartingWith(this.getUserNavigationOptionsCommonCacheKey()); + }); + } + + /** + * Invalidates user navigation options for certain courses. + * + * @param {number[]} courseIds IDs of courses. + * @param {string} [siteId] Site ID to invalidate. If not defined, use current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateUserNavigationOptionsForCourses(courseIds: number[], siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getUserNavigationOptionsCacheKey(courseIds)); + }); + } + + /** + * Check if WS to retrieve guest enrolment data is available. + * + * @return {boolean} Whether guest WS is available. + */ + isGuestWSAvailable() : boolean { + let currentSite = this.sitesProvider.getCurrentSite(); + return currentSite && currentSite.wsAvailable('enrol_guest_get_instance_info'); + } + + /** + * Search courses. + * + * @param {string} text Text to search. + * @param {number} [page=0] Page to get. + * @param {number} [perPage] Number of courses per page. Defaults to CoreCoursesProvider.SEARCH_PER_PAGE. + * @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. + */ + search(text: string, page = 0, perPage?: number, siteId?: string) : Promise<{total: number, courses: any[]}> { + perPage = perPage || CoreCoursesProvider.SEARCH_PER_PAGE; + + return this.sitesProvider.getSite(siteId).then((site) => { + let params = { + criterianame: 'search', + criteriavalue: text, + page: page, + perpage: perPage + }, preSets = { + getFromCache: false + } + + return site.read('core_course_search_courses', params, preSets).then((response) => { + return {total: response.total, courses: response.courses}; + }); + }); + } + + /** + * Self enrol current user in a certain course. + * + * @param {number} courseId Course ID. + * @param {string} [password] Password to use. + * @param {number} [instanceId] Enrol instance ID. + * @param {string} [siteId] Site ID. If not defined, use current site. + * @return {Promise} 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. + */ + selfEnrol(courseId: number, password = '', instanceId?: number, siteId?: string) : Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + + let params: any = { + courseid: courseId, + password: password + } + if (instanceId) { + params.instanceid = instanceId; + } + + return site.write('enrol_self_enrol_user', params).then((response) : any => { + if (response) { + if (response.status) { + return true; + } else if (response.warnings && response.warnings.length) { + let message; + response.warnings.forEach((warning) => { + // Invalid password warnings. + if (warning.warningcode == '2' || warning.warningcode == '3' || warning.warningcode == '4') { + message = warning.message; + } + }); + + if (message) { + return Promise.reject({code: CoreCoursesProvider.ENROL_INVALID_KEY, message: message}); + } else { + return Promise.reject(response.warnings[0]); + } + } + } + return Promise.reject(null); + }); + }); + } +}