2
0

268 lines
11 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// (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 { TranslateService } from '@ngx-translate/core';
import { CoreSitesProvider } from '../../../providers/sites';
import { CoreDomUtilsProvider } from '../../../providers/utils/dom';
import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '../../contentlinks/providers/delegate';
import { CoreLoginHelperProvider } from '../../login/providers/helper';
import { CoreCourseProvider } from '../../course/providers/course';
import { CoreCoursesProvider } from './courses';
/**
* Handler to treat links to course view or enrol (except site home).
*/
@Injectable()
export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
name = 'CoreCoursesCourseLinkHandler';
pattern = /((\/enrol\/index\.php)|(\/course\/enrol\.php)|(\/course\/view\.php)).*([\?\&]id=\d+)/;
protected waitStart = 0;
constructor(private sitesProvider: CoreSitesProvider, private coursesProvider: CoreCoursesProvider,
private loginHelper: CoreLoginHelperProvider, private domUtils: CoreDomUtilsProvider,
private translate: TranslateService, private courseProvider: CoreCourseProvider) {
super();
}
/**
* Get the list of actions for a link (url).
*
* @param {string[]} siteIds List of sites the URL belongs to.
* @param {string} url The URL to treat.
* @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.
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
*/
getActions(siteIds: string[], url: string, params: any, courseId?: number) :
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> {
courseId = parseInt(params.id, 10);
let sectionId = params.sectionid ? parseInt(params.sectionid, 10) : null,
sectionNumber = typeof params.section != 'undefined' ? parseInt(params.section, 10) : NaN,
pageParams: any = {
course: {id: courseId},
sectionId: sectionId || null
};
if (!isNaN(sectionNumber)) {
pageParams.sectionNumber = sectionNumber;
}
return [{
action: (siteId, navCtrl?) => {
siteId = siteId || this.sitesProvider.getCurrentSiteId();
if (siteId == this.sitesProvider.getCurrentSiteId()) {
this.actionEnrol(courseId, url, pageParams).catch(() => {
// Ignore errors.
});
} else {
// Use redirect to make the course the new history root (to avoid "loops" in history).
this.loginHelper.redirect('CoreCourseSectionPage', pageParams, siteId);
}
}
}];
}
/**
* Check if the handler is enabled for a certain site (site + user) and a URL.
* If not defined, defaults to true.
*
* @param {string} siteId The site ID.
* @param {string} url The URL to treat.
* @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.
* @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> {
courseId = parseInt(params.id, 10);
if (!courseId) {
return false;
}
// Get the course id of Site Home.
return this.sitesProvider.getSiteHomeId(siteId).then((siteHomeId) => {
return courseId != siteHomeId;
});
}
/**
* Action to perform when an enrol link is clicked.
*
* @param {number} courseId Course ID.
* @param {string} url Treated URL.
* @param {any} pageParams Params to send to the new page.
* @return {Promise<any>} Promise resolved when done.
*/
protected actionEnrol(courseId: number, url: string, pageParams: any) : Promise<any> {
let modal = this.domUtils.showModalLoading(),
isEnrolUrl = !!url.match(/(\/enrol\/index\.php)|(\/course\/enrol\.php)/);
// Check if user is enrolled in the course.
return this.coursesProvider.getUserCourse(courseId).catch(() => {
// User is not enrolled in the course. Check if can self enrol.
return this.canSelfEnrol(courseId).then(() => {
modal.dismiss();
// The user can self enrol. If it's not a enrolment URL we'll ask for confirmation.
let promise = isEnrolUrl ? Promise.resolve() :
this.domUtils.showConfirm(this.translate.instant('core.courses.confirmselfenrol'));
return promise.then(() => {
// Enrol URL or user confirmed.
return this.selfEnrol(courseId).catch((error) => {
if (error) {
this.domUtils.showErrorModal(error);
}
return Promise.reject(null);
});
}, () => {
// User cancelled. Check if the user can view the course contents (guest access or similar).
return this.courseProvider.getSections(courseId, false, true);
});
}, (error) => {
// Can't self enrol. Check if the user can view the course contents (guest access or similar).
return this.courseProvider.getSections(courseId, false, true).catch(() => {
// Error. Show error message and allow the user to open the link in browser.
modal.dismiss();
if (error) {
error = error.message || error.error || error.content || error.body || error;
}
if (!error) {
error = this.translate.instant('core.courses.notenroled');
}
let body = this.translate.instant('core.twoparagraphs',
{p1: error, p2: this.translate.instant('core.confirmopeninbrowser')});
this.domUtils.showConfirm(body).then(() => {
this.sitesProvider.getCurrentSite().openInBrowserWithAutoLogin(url);
}).catch(() => {
// User cancelled.
});
return Promise.reject(null);
});
});
}).then(() => {
modal.dismiss();
// Use redirect to make the course the new history root (to avoid "loops" in history).
this.loginHelper.redirect('CoreCourseSectionPage', pageParams, this.sitesProvider.getCurrentSiteId());
});
}
/**
* Check if a user can be "automatically" self enrolled in a course.
*
* @param {number} courseId Course ID.
* @return {Promise<any>} Promise resolved if user can be enrolled in a course, rejected otherwise.
*/
protected canSelfEnrol(courseId: number) : Promise<any> {
// Check that the course has self enrolment enabled.
return this.coursesProvider.getCourseEnrolmentMethods(courseId).then((methods) => {
let isSelfEnrolEnabled = false,
instances = 0;
methods.forEach((method) => {
if (method.type == 'self' && method.status) {
isSelfEnrolEnabled = true;
instances++;
}
});
if (!isSelfEnrolEnabled || instances != 1) {
// Self enrol not enabled or more than one instance.
return Promise.reject(null);
}
});
}
/**
* Try to self enrol a user in a course.
*
* @param {number} courseId Course ID.
* @param {string} [password] Password.
* @return {Promise<any>} Promise resolved when the user is enrolled, rejected otherwise.
*/
protected selfEnrol(courseId: number, password?: string) : Promise<any> {
const modal = this.domUtils.showModalLoading();
return this.coursesProvider.selfEnrol(courseId, password).then(() => {
// Success self enrolling the user, invalidate the courses list.
return this.coursesProvider.invalidateUserCourses().catch(() => {
// Ignore errors.
}).then(() => {
// Sometimes the list of enrolled courses takes a while to be updated. Wait for it.
return this.waitForEnrolled(courseId, true).finally(() => {
modal.dismiss();
});
});
}).catch((error) => {
modal.dismiss();
if (error && error.code === CoreCoursesProvider.ENROL_INVALID_KEY) {
// Invalid password. Allow the user to input password.
let title = this.translate.instant('core.courses.selfenrolment'),
body = ' ', // Empty message.
placeholder = this.translate.instant('core.courses.password');
if (typeof password != 'undefined') {
// The user attempted a password. Show an error message.
this.domUtils.showErrorModal(error.message);
}
return this.domUtils.showPrompt(body, title, placeholder).then((password) => {
return this.selfEnrol(courseId, password);
});
} else {
return Promise.reject(error);
}
});
}
/**
* Wait for the user to be enrolled in a course.
*
* @param {number} courseId The course ID.
* @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(courseId: number, first?: boolean) : Promise<any> {
if (first) {
this.waitStart = Date.now();
}
// Check if user is enrolled in the course.
return this.coursesProvider.invalidateUserCourses().catch(() => {
// Ignore errors.
}).then(() => {
return this.coursesProvider.getUserCourse(courseId);
}).catch(() => {
// Not enrolled, wait a bit and try again.
if (Date.now() - this.waitStart > 60000) {
// Max time reached, stop.
return;
}
return new Promise((resolve, reject) => {
setTimeout(() => {
this.waitForEnrolled(courseId).then(resolve);
}, 5000);
});
});
}
}