parent
47af5a252e
commit
b975e6f0d8
|
@ -237,6 +237,14 @@
|
||||||
"addon.coursecompletion.requirement": "block_completionstatus",
|
"addon.coursecompletion.requirement": "block_completionstatus",
|
||||||
"addon.coursecompletion.status": "moodle",
|
"addon.coursecompletion.status": "moodle",
|
||||||
"addon.coursecompletion.viewcoursereport": "completion",
|
"addon.coursecompletion.viewcoursereport": "completion",
|
||||||
|
"addon.enrol_guest.guestaccess_withoutpassword": "enrol_guest",
|
||||||
|
"addon.enrol_guest.guestaccess_withpassword": "enrol_guest",
|
||||||
|
"addon.enrol_guest.passwordinvalid": "enrol_guest",
|
||||||
|
"addon.enrol_self.confirmselfenrol": "local_moodlemobileapp",
|
||||||
|
"addon.enrol_self.errorselfenrol": "local_moodlemobileapp",
|
||||||
|
"addon.enrol_self.nopassword": "enrol_self",
|
||||||
|
"addon.enrol_self.password": "enrol_self",
|
||||||
|
"addon.enrol_self.pluginname": "enrol_self",
|
||||||
"addon.messageoutput_airnotifier.processorsettingsdesc": "local_moodlemobileapp",
|
"addon.messageoutput_airnotifier.processorsettingsdesc": "local_moodlemobileapp",
|
||||||
"addon.messageoutput_airnotifier.pushdisabledwarning": "local_moodlemobileapp",
|
"addon.messageoutput_airnotifier.pushdisabledwarning": "local_moodlemobileapp",
|
||||||
"addon.messages.acceptandaddcontact": "message",
|
"addon.messages.acceptandaddcontact": "message",
|
||||||
|
@ -1590,9 +1598,6 @@
|
||||||
"core.course.errordownloadingsection": "local_moodlemobileapp",
|
"core.course.errordownloadingsection": "local_moodlemobileapp",
|
||||||
"core.course.errorgetmodule": "local_moodlemobileapp",
|
"core.course.errorgetmodule": "local_moodlemobileapp",
|
||||||
"core.course.failed": "completion",
|
"core.course.failed": "completion",
|
||||||
"core.course.guestaccess": "enrol_guest/pluginname",
|
|
||||||
"core.course.guestaccess_passwordinvalid": "enrol_guest/passwordinvalid",
|
|
||||||
"core.course.guestaccess_withpassword": "enrol_guest",
|
|
||||||
"core.course.hiddenfromstudents": "moodle",
|
"core.course.hiddenfromstudents": "moodle",
|
||||||
"core.course.hiddenoncoursepage": "moodle",
|
"core.course.hiddenoncoursepage": "moodle",
|
||||||
"core.course.highlighted": "moodle",
|
"core.course.highlighted": "moodle",
|
||||||
|
@ -1623,7 +1628,6 @@
|
||||||
"core.coursedetails": "moodle",
|
"core.coursedetails": "moodle",
|
||||||
"core.coursenogroups": "local_moodlemobileapp",
|
"core.coursenogroups": "local_moodlemobileapp",
|
||||||
"core.courses.addtofavourites": "block_myoverview",
|
"core.courses.addtofavourites": "block_myoverview",
|
||||||
"core.courses.allowguests": "enrol_guest",
|
|
||||||
"core.courses.aria:coursecategory": "course",
|
"core.courses.aria:coursecategory": "course",
|
||||||
"core.courses.aria:coursename": "course",
|
"core.courses.aria:coursename": "course",
|
||||||
"core.courses.aria:courseprogress": "block_myoverview",
|
"core.courses.aria:courseprogress": "block_myoverview",
|
||||||
|
@ -1633,7 +1637,6 @@
|
||||||
"core.courses.cannotretrievemorecategories": "local_moodlemobileapp",
|
"core.courses.cannotretrievemorecategories": "local_moodlemobileapp",
|
||||||
"core.courses.categories": "moodle",
|
"core.courses.categories": "moodle",
|
||||||
"core.courses.completeenrolmentbrowser": "local_moodlemobileapp",
|
"core.courses.completeenrolmentbrowser": "local_moodlemobileapp",
|
||||||
"core.courses.confirmselfenrol": "local_moodlemobileapp",
|
|
||||||
"core.courses.courses": "moodle",
|
"core.courses.courses": "moodle",
|
||||||
"core.courses.downloadcourses": "local_moodlemobileapp",
|
"core.courses.downloadcourses": "local_moodlemobileapp",
|
||||||
"core.courses.enrolme": "local_moodlemobileapp",
|
"core.courses.enrolme": "local_moodlemobileapp",
|
||||||
|
@ -1641,7 +1644,6 @@
|
||||||
"core.courses.errorloadcourses": "local_moodlemobileapp",
|
"core.courses.errorloadcourses": "local_moodlemobileapp",
|
||||||
"core.courses.errorloadplugins": "local_moodlemobileapp",
|
"core.courses.errorloadplugins": "local_moodlemobileapp",
|
||||||
"core.courses.errorsearching": "local_moodlemobileapp",
|
"core.courses.errorsearching": "local_moodlemobileapp",
|
||||||
"core.courses.errorselfenrol": "local_moodlemobileapp",
|
|
||||||
"core.courses.favourite": "course",
|
"core.courses.favourite": "course",
|
||||||
"core.courses.filtermycourses": "local_moodlemobileapp",
|
"core.courses.filtermycourses": "local_moodlemobileapp",
|
||||||
"core.courses.frontpage": "admin",
|
"core.courses.frontpage": "admin",
|
||||||
|
@ -1663,7 +1665,6 @@
|
||||||
"core.courses.search": "moodle",
|
"core.courses.search": "moodle",
|
||||||
"core.courses.searchcourses": "moodle",
|
"core.courses.searchcourses": "moodle",
|
||||||
"core.courses.searchcoursesadvice": "local_moodlemobileapp",
|
"core.courses.searchcoursesadvice": "local_moodlemobileapp",
|
||||||
"core.courses.selfenrolment": "local_moodlemobileapp",
|
|
||||||
"core.courses.show": "block_myoverview",
|
"core.courses.show": "block_myoverview",
|
||||||
"core.courses.showonlyenrolled": "local_moodlemobileapp",
|
"core.courses.showonlyenrolled": "local_moodlemobileapp",
|
||||||
"core.courses.therearecourses": "moodle",
|
"core.courses.therearecourses": "moodle",
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { AddonBlogModule } from './blog/blog.module';
|
||||||
import { AddonCalendarModule } from './calendar/calendar.module';
|
import { AddonCalendarModule } from './calendar/calendar.module';
|
||||||
import { AddonCompetencyModule } from './competency/competency.module';
|
import { AddonCompetencyModule } from './competency/competency.module';
|
||||||
import { AddonCourseCompletionModule } from './coursecompletion/coursecompletion.module';
|
import { AddonCourseCompletionModule } from './coursecompletion/coursecompletion.module';
|
||||||
|
import { AddonEnrolModule } from './enrol/enrol.module';
|
||||||
import { AddonFilterModule } from './filter/filter.module';
|
import { AddonFilterModule } from './filter/filter.module';
|
||||||
import { AddonMessageOutputModule } from './messageoutput/messageoutput.module';
|
import { AddonMessageOutputModule } from './messageoutput/messageoutput.module';
|
||||||
import { AddonMessagesModule } from './messages/messages.module';
|
import { AddonMessagesModule } from './messages/messages.module';
|
||||||
|
@ -42,6 +43,7 @@ import { AddonUserProfileFieldModule } from './userprofilefield/userprofilefield
|
||||||
AddonCalendarModule,
|
AddonCalendarModule,
|
||||||
AddonCompetencyModule,
|
AddonCompetencyModule,
|
||||||
AddonCourseCompletionModule,
|
AddonCourseCompletionModule,
|
||||||
|
AddonEnrolModule,
|
||||||
AddonFilterModule,
|
AddonFilterModule,
|
||||||
AddonMessageOutputModule,
|
AddonMessageOutputModule,
|
||||||
AddonMessagesModule,
|
AddonMessagesModule,
|
||||||
|
|
|
@ -50,8 +50,8 @@ export class AddonCompetencyCourseOptionHandlerService implements CoreCourseOpti
|
||||||
accessData: CoreCourseAccess,
|
accessData: CoreCourseAccess,
|
||||||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
if (accessData && accessData.type === CoreCourseProvider.ACCESS_GUEST) {
|
||||||
return false; // Not enabled for guests.
|
return false; // Not enabled for guest access.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (navOptions && navOptions.competencies !== undefined) {
|
if (navOptions && navOptions.competencies !== undefined) {
|
||||||
|
|
|
@ -43,8 +43,8 @@ export class AddonCourseCompletionCourseOptionHandlerService implements CoreCour
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
async isEnabledForCourse(courseId: number, accessData: CoreCourseAccess): Promise<boolean> {
|
async isEnabledForCourse(courseId: number, accessData: CoreCourseAccess): Promise<boolean> {
|
||||||
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
if (accessData && accessData.type === CoreCourseProvider.ACCESS_GUEST) {
|
||||||
return false; // Not enabled for guests.
|
return false; // Not enabled for guest access.
|
||||||
}
|
}
|
||||||
|
|
||||||
const courseEnabled = await AddonCourseCompletion.isPluginViewEnabledForCourse(courseId);
|
const courseEnabled = await AddonCourseCompletion.isPluginViewEnabledForCourse(courseId);
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { AddonEnrolFeeModule } from './fee/fee.module';
|
||||||
|
import { AddonEnrolGuestModule } from './guest/guest.module';
|
||||||
|
import { AddonEnrolPaypalModule } from './paypal/paypal.module';
|
||||||
|
import { AddonEnrolSelfModule } from './self/self.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
AddonEnrolFeeModule,
|
||||||
|
AddonEnrolGuestModule,
|
||||||
|
AddonEnrolPaypalModule,
|
||||||
|
AddonEnrolSelfModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonEnrolModule {}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
import { AddonEnrolFeeHandler } from './services/enrol-handler';
|
||||||
|
import { CoreEnrolDelegate } from '@features/enrol/services/enrol-delegate';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
useValue: () => {
|
||||||
|
CoreEnrolDelegate.registerHandler(AddonEnrolFeeHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonEnrolFeeModule {}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { CoreEnrolAction, CoreEnrolHandler } from '@features/enrol/services/enrol-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enrol handler.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonEnrolFeeHandlerService implements CoreEnrolHandler {
|
||||||
|
|
||||||
|
name = 'AddonEnrolFee';
|
||||||
|
type = 'fee';
|
||||||
|
enrolmentAction = CoreEnrolAction.BROWSER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AddonEnrolFeeHandler = makeSingleton(AddonEnrolFeeHandlerService);
|
|
@ -0,0 +1,30 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
import { AddonEnrolGuestHandler } from './services/enrol-handler';
|
||||||
|
import { CoreEnrolDelegate } from '@features/enrol/services/enrol-delegate';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
useValue: () => {
|
||||||
|
CoreEnrolDelegate.registerHandler(AddonEnrolGuestHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonEnrolGuestModule {}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"guestaccess_withpassword": "Guest access requires password",
|
||||||
|
"guestaccess_withoutpassword": "Guest access",
|
||||||
|
"passwordinvalid": "Incorrect access password, please try again"
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { CoreEnrolAction, CoreEnrolGuestHandler, CoreEnrolInfoIcon } from '@features/enrol/services/enrol-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { AddonEnrolGuest } from './guest';
|
||||||
|
import { CorePasswordModalResponse } from '@components/password-modal/password-modal';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreWSError } from '@classes/errors/wserror';
|
||||||
|
import { CoreEnrol, CoreEnrolEnrolmentMethod } from '@features/enrol/services/enrol';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enrol handler.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonEnrolGuestHandlerService implements CoreEnrolGuestHandler {
|
||||||
|
|
||||||
|
name = 'AddonEnrolGuest';
|
||||||
|
type = 'guest';
|
||||||
|
enrolmentAction = <CoreEnrolAction.GUEST> CoreEnrolAction.GUEST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async getInfoIcons(courseId: number): Promise<CoreEnrolInfoIcon[]> {
|
||||||
|
const guestEnrolments = await CoreEnrol.getSupportedCourseEnrolmentMethods(courseId, { type: this.type });
|
||||||
|
|
||||||
|
for (const guestEnrolment of guestEnrolments) {
|
||||||
|
const info = await AddonEnrolGuest.getGuestEnrolmentInfo(guestEnrolment.id);
|
||||||
|
// Don't allow guest access if it requires a password if not supported.
|
||||||
|
if (!info.passwordrequired) {
|
||||||
|
return [{
|
||||||
|
label: 'addon.enrol_guest.guestaccess_withoutpassword',
|
||||||
|
icon: 'fas-unlock',
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
|
return [{
|
||||||
|
label: 'addon.enrol_guest.guestaccess_withpassword',
|
||||||
|
icon: 'fas-key',
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async canAccess(method: CoreEnrolEnrolmentMethod): Promise<boolean> {
|
||||||
|
const info = await AddonEnrolGuest.getGuestEnrolmentInfo(method.id);
|
||||||
|
|
||||||
|
return info.status && (!info.passwordrequired || AddonEnrolGuest.isValidateGuestAccessPasswordAvailable());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async validateAccess(method: CoreEnrolEnrolmentMethod): Promise<boolean> {
|
||||||
|
const info = await AddonEnrolGuest.getGuestEnrolmentInfo(method.id);
|
||||||
|
|
||||||
|
if (!info.status) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.passwordrequired) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AddonEnrolGuest.isValidateGuestAccessPasswordAvailable()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validatePassword = async (password: string): Promise<CorePasswordModalResponse> => {
|
||||||
|
const modal = await CoreDomUtils.showModalLoading('core.loading', true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await AddonEnrolGuest.validateGuestAccessPassword(method.id, password);
|
||||||
|
|
||||||
|
let error = response.hint;
|
||||||
|
if (!response.validated && !error) {
|
||||||
|
error = 'addon.enrol_guest.passwordinvalid';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
password, validated: response.validated, error,
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
modal.dismiss();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await CoreDomUtils.promptPassword<CorePasswordModalResponse>({
|
||||||
|
title: method.name,
|
||||||
|
validator: validatePassword,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.validated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof CoreWSError) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancelled, return
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async invalidate(method: CoreEnrolEnrolmentMethod): Promise<void> {
|
||||||
|
return AddonEnrolGuest.invalidateGuestEnrolmentInfo(method.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AddonEnrolGuestHandler = makeSingleton(AddonEnrolGuestHandlerService);
|
|
@ -0,0 +1,158 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { CoreSiteWSPreSets, CoreSite } from '@classes/site';
|
||||||
|
import { CoreEnrolEnrolmentInfo } from '@features/enrol/services/enrol';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreWSExternalWarning } from '@services/ws';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides some features to manage guest enrolment.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonEnrolGuestService {
|
||||||
|
|
||||||
|
protected static readonly ROOT_CACHE_KEY = 'AddonEnrolGuest:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get info from a course guest enrolment method.
|
||||||
|
*
|
||||||
|
* @param instanceId Guest instance ID.
|
||||||
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
|
* @returns Promise resolved when the info is retrieved.
|
||||||
|
*/
|
||||||
|
async getGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<AddonEnrolGuestInfo> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
const params: AddonEnrolGuestGetInstanceInfoWSParams = {
|
||||||
|
instanceid: instanceId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
cacheKey: this.getGuestEnrolmentInfoCacheKey(instanceId),
|
||||||
|
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response =
|
||||||
|
await site.read<AddonEnrolGuestGetInstanceInfoWSResponse>('enrol_guest_get_instance_info', params, preSets);
|
||||||
|
|
||||||
|
return response.instanceinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for get course guest enrolment methods WS call.
|
||||||
|
*
|
||||||
|
* @param instanceId Guest instance ID.
|
||||||
|
* @returns Cache key.
|
||||||
|
*/
|
||||||
|
protected getGuestEnrolmentInfoCacheKey(instanceId: number): string {
|
||||||
|
return AddonEnrolGuestService.ROOT_CACHE_KEY + instanceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates get course guest enrolment info WS call.
|
||||||
|
*
|
||||||
|
* @param instanceId Guest instance ID.
|
||||||
|
* @param siteId Site Id. If not defined, use current site.
|
||||||
|
* @returns Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
async invalidateGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
site.invalidateWsCacheForKey(this.getGuestEnrolmentInfoCacheKey(instanceId)),
|
||||||
|
site.invalidateWsCacheForKey(`mmCourses:guestinfo:${instanceId}`), // @todo Remove after 4.3 release.
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if guest password validation WS is available on the current site.
|
||||||
|
*
|
||||||
|
* @returns Whether guest password validation WS is available.
|
||||||
|
*/
|
||||||
|
isValidateGuestAccessPasswordAvailable(): boolean {
|
||||||
|
return CoreSites.wsAvailableInCurrentSite('enrol_guest_validate_password');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform password validation of guess access.
|
||||||
|
*
|
||||||
|
* @param enrolmentInstanceId Instance id of guest enrolment plugin.
|
||||||
|
* @param password Course Password.
|
||||||
|
* @returns Wether the password is valid.
|
||||||
|
*/
|
||||||
|
async validateGuestAccessPassword(
|
||||||
|
enrolmentInstanceId: number,
|
||||||
|
password: string,
|
||||||
|
): Promise<AddonEnrolGuestValidatePasswordWSResponse> {
|
||||||
|
const site = CoreSites.getCurrentSite();
|
||||||
|
|
||||||
|
if (!site) {
|
||||||
|
return {
|
||||||
|
validated: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const params: AddonEnrolGuestValidatePasswordWSParams = {
|
||||||
|
instanceid: enrolmentInstanceId,
|
||||||
|
password,
|
||||||
|
};
|
||||||
|
|
||||||
|
return await site.write<AddonEnrolGuestValidatePasswordWSResponse>('enrol_guest_validate_password', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonEnrolGuest = makeSingleton(AddonEnrolGuestService);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of enrol_guest_get_instance_info WS.
|
||||||
|
*/
|
||||||
|
type AddonEnrolGuestGetInstanceInfoWSParams = {
|
||||||
|
instanceid: number; // Instance id of guest enrolment plugin.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by enrol_guest_get_instance_info WS.
|
||||||
|
*/
|
||||||
|
export type AddonEnrolGuestGetInstanceInfoWSResponse = {
|
||||||
|
instanceinfo: AddonEnrolGuestInfo;
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Course guest enrolment method.
|
||||||
|
*/
|
||||||
|
export type AddonEnrolGuestInfo = CoreEnrolEnrolmentInfo & {
|
||||||
|
passwordrequired: boolean; // Is a password required?
|
||||||
|
status: boolean; // Is the enrolment enabled?
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of enrol_guest_validate_password WS.
|
||||||
|
*/
|
||||||
|
type AddonEnrolGuestValidatePasswordWSParams = {
|
||||||
|
instanceid: number; // instance id of guest enrolment plugin
|
||||||
|
password: string; // the course password
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by enrol_guest_get_instance_info WS.
|
||||||
|
*/
|
||||||
|
export type AddonEnrolGuestValidatePasswordWSResponse = {
|
||||||
|
validated: boolean; // Whether the password was successfully validated
|
||||||
|
hint?: string; // Password hint (if enabled)
|
||||||
|
warnings?: CoreWSExternalWarning[];
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
import { AddonEnrolPaypalHandler } from './services/enrol-handler';
|
||||||
|
import { CoreEnrolDelegate } from '@features/enrol/services/enrol-delegate';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
useValue: () => {
|
||||||
|
CoreEnrolDelegate.registerHandler(AddonEnrolPaypalHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonEnrolPaypalModule {}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { CoreEnrolAction, CoreEnrolHandler } from '@features/enrol/services/enrol-delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enrol handler.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonEnrolPaypalHandlerService implements CoreEnrolHandler {
|
||||||
|
|
||||||
|
name = 'AddonEnrolPaypal';
|
||||||
|
type = 'paypal';
|
||||||
|
enrolmentAction = CoreEnrolAction.BROWSER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AddonEnrolPaypalHandler = makeSingleton(AddonEnrolPaypalHandlerService);
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"pluginname": "Self enrolment",
|
||||||
|
"confirmselfenrol": "Are you sure you want to enrol yourself in this course?",
|
||||||
|
"errorselfenrol": "An error occurred while self enrolling.",
|
||||||
|
"nopassword": "No enrolment key required.",
|
||||||
|
"password": "Enrolment key"
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { APP_INITIALIZER, NgModule } from '@angular/core';
|
||||||
|
import { AddonEnrolSelfHandler } from './services/enrol-handler';
|
||||||
|
import { CoreEnrolDelegate } from '@features/enrol/services/enrol-delegate';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
multi: true,
|
||||||
|
useValue: () => {
|
||||||
|
CoreEnrolDelegate.registerHandler(AddonEnrolSelfHandler.instance);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AddonEnrolSelfModule {}
|
|
@ -0,0 +1,176 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { CoreEnrolAction, CoreEnrolSelfHandler, CoreEnrolInfoIcon } from '@features/enrol/services/enrol-delegate';
|
||||||
|
import { Translate, makeSingleton } from '@singletons';
|
||||||
|
import { AddonEnrolSelf } from './self';
|
||||||
|
import { CorePasswordModalResponse } from '@components/password-modal/password-modal';
|
||||||
|
import { CoreCoursesProvider } from '@features/courses/services/courses';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreEnrol, CoreEnrolEnrolmentMethod } from '@features/enrol/services/enrol';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enrol handler.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonEnrolSelfHandlerService implements CoreEnrolSelfHandler {
|
||||||
|
|
||||||
|
name = 'AddonEnrolSelf';
|
||||||
|
type = 'self';
|
||||||
|
enrolmentAction = <CoreEnrolAction.SELF> CoreEnrolAction.SELF;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async isEnabled(): Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async getInfoIcons(courseId: number): Promise<CoreEnrolInfoIcon[]> {
|
||||||
|
const selfEnrolments = await CoreEnrol.getSupportedCourseEnrolmentMethods(courseId, { type: this.type });
|
||||||
|
let passwordRequired = false;
|
||||||
|
let noPasswordRequired = false;
|
||||||
|
|
||||||
|
for (const selfEnrolment of selfEnrolments) {
|
||||||
|
const info = await AddonEnrolSelf.getSelfEnrolmentInfo(selfEnrolment.id);
|
||||||
|
// Don't allow self access if it requires a password if not supported.
|
||||||
|
if (!info.enrolpassword) {
|
||||||
|
noPasswordRequired = true;
|
||||||
|
} else {
|
||||||
|
passwordRequired = true;
|
||||||
|
}
|
||||||
|
if (noPasswordRequired && passwordRequired) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const icons: CoreEnrolInfoIcon[] = [];
|
||||||
|
if (noPasswordRequired) {
|
||||||
|
icons.push({
|
||||||
|
label: 'addon.enrol_self.pluginname',
|
||||||
|
icon: 'fas-right-to-bracket',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passwordRequired) {
|
||||||
|
icons.push({
|
||||||
|
label: 'addon.enrol_self.pluginname',
|
||||||
|
icon: 'fas-key',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return icons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async enrol(method: CoreEnrolEnrolmentMethod): Promise<boolean> {
|
||||||
|
const info = await AddonEnrolSelf.getSelfEnrolmentInfo(method.id);
|
||||||
|
// Don't allow self access if it requires a password if not supported.
|
||||||
|
if (!info.enrolpassword) {
|
||||||
|
try {
|
||||||
|
await CoreDomUtils.showConfirm(
|
||||||
|
Translate.instant('addon.enrol_self.confirmselfenrol') + '<br>' +
|
||||||
|
Translate.instant('addon.enrol_self.nopassword'),
|
||||||
|
method.name,
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// User cancelled.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await this.performEnrol(method);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Self enrol in a course.
|
||||||
|
*
|
||||||
|
* @param method Enrolment method
|
||||||
|
* @returns Promise resolved when self enrolled.
|
||||||
|
*/
|
||||||
|
protected async performEnrol(method: CoreEnrolEnrolmentMethod): Promise<boolean> {
|
||||||
|
const validatePassword = async (password = ''): Promise<CorePasswordModalResponse> => {
|
||||||
|
const modal = await CoreDomUtils.showModalLoading('core.loading', true);
|
||||||
|
|
||||||
|
const response: CorePasswordModalResponse = {
|
||||||
|
password,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
response.validated = await AddonEnrolSelf.selfEnrol(method.courseid, password, method.id);
|
||||||
|
} catch (error) {
|
||||||
|
if (error && error.errorcode === CoreCoursesProvider.ENROL_INVALID_KEY) {
|
||||||
|
response.validated = false;
|
||||||
|
response.error = error.message;
|
||||||
|
} else {
|
||||||
|
CoreDomUtils.showErrorModalDefault(error, 'addon.enrol_self.errorselfenrol', true);
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
modal.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
};
|
||||||
|
|
||||||
|
let response: CorePasswordModalResponse | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
response = await validatePassword();
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.validated) {
|
||||||
|
try {
|
||||||
|
const response = await CoreDomUtils.promptPassword({
|
||||||
|
validator: validatePassword,
|
||||||
|
title: method.name,
|
||||||
|
placeholder: 'addon.enrol_self.password',
|
||||||
|
submit: 'core.courses.enrolme',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.validated) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Cancelled, return
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async invalidate(method: CoreEnrolEnrolmentMethod): Promise<void> {
|
||||||
|
return AddonEnrolSelf.invalidateSelfEnrolmentInfo(method.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AddonEnrolSelfHandler = makeSingleton(AddonEnrolSelfHandlerService);
|
|
@ -0,0 +1,152 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { CoreWSError } from '@classes/errors/wserror';
|
||||||
|
import { CoreSiteWSPreSets, CoreSite } from '@classes/site';
|
||||||
|
import { CoreCoursesProvider } from '@features/courses/services/courses';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreStatusWithWarningsWSResponse } from '@services/ws';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides some features to manage self enrolment.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class AddonEnrolSelfService {
|
||||||
|
|
||||||
|
protected static readonly ROOT_CACHE_KEY = 'AddonEnrolSelf:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get info from a course self enrolment method.
|
||||||
|
*
|
||||||
|
* @param instanceId Self instance ID.
|
||||||
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
|
* @returns Promise resolved when the info is retrieved.
|
||||||
|
*/
|
||||||
|
async getSelfEnrolmentInfo(instanceId: number, siteId?: string): Promise<AddonEnrolSelfGetInstanceInfoWSResponse> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
const params: AddonEnrolSelfGetInstanceInfoWSParams = {
|
||||||
|
instanceid: instanceId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
cacheKey: this.getSelfEnrolmentInfoCacheKey(instanceId),
|
||||||
|
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||||
|
};
|
||||||
|
|
||||||
|
return await site.read<AddonEnrolSelfGetInstanceInfoWSResponse>('enrol_self_get_instance_info', params, preSets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for get course self enrolment methods WS call.
|
||||||
|
*
|
||||||
|
* @param instanceId Self instance ID.
|
||||||
|
* @returns Cache key.
|
||||||
|
*/
|
||||||
|
protected getSelfEnrolmentInfoCacheKey(instanceId: number): string {
|
||||||
|
return AddonEnrolSelfService.ROOT_CACHE_KEY + instanceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates get course self enrolment info WS call.
|
||||||
|
*
|
||||||
|
* @param instanceId Self instance ID.
|
||||||
|
* @param siteId Site Id. If not defined, use current site.
|
||||||
|
* @returns Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
async invalidateSelfEnrolmentInfo(instanceId: number, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
await site.invalidateWsCacheForKey(this.getSelfEnrolmentInfoCacheKey(instanceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Self enrol current user in a certain course.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @param password Password to use.
|
||||||
|
* @param instanceId Enrol instance ID.
|
||||||
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
|
* @returns Promise resolved if the user is enrolled. If the password is invalid, the promise is rejected
|
||||||
|
* with an object with errorcode = CoreCoursesProvider.ENROL_INVALID_KEY.
|
||||||
|
*/
|
||||||
|
async selfEnrol(courseId: number, password: string = '', instanceId?: number, siteId?: string): Promise<boolean> {
|
||||||
|
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
const params: AddonEnrolSelfEnrolUserWSParams = {
|
||||||
|
courseid: courseId,
|
||||||
|
password: password,
|
||||||
|
};
|
||||||
|
if (instanceId) {
|
||||||
|
params.instanceid = instanceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await site.write<CoreStatusWithWarningsWSResponse>('enrol_self_enrol_user', params);
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
throw Error('WS enrol_self_enrol_user failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.warnings && response.warnings.length) {
|
||||||
|
// Invalid password warnings.
|
||||||
|
const warning = response.warnings.find((warning) =>
|
||||||
|
warning.warningcode == '2' || warning.warningcode == '3' || warning.warningcode == '4');
|
||||||
|
|
||||||
|
if (warning) {
|
||||||
|
throw new CoreWSError({ errorcode: CoreCoursesProvider.ENROL_INVALID_KEY, message: warning.message });
|
||||||
|
} else {
|
||||||
|
throw new CoreWSError(response.warnings[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Error('WS enrol_self_enrol_user failed without warnings');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
export const AddonEnrolSelf = makeSingleton(AddonEnrolSelfService);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of enrol_self_get_instance_info WS.
|
||||||
|
*/
|
||||||
|
type AddonEnrolSelfGetInstanceInfoWSParams = {
|
||||||
|
instanceid: number; // Instance id of self enrolment plugin.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by enrol_self_get_instance_info WS.
|
||||||
|
*/
|
||||||
|
export type AddonEnrolSelfGetInstanceInfoWSResponse = {
|
||||||
|
id: number; // Id of course enrolment instance.
|
||||||
|
courseid: number; // Id of course.
|
||||||
|
type: string; // Type of enrolment plugin.
|
||||||
|
name: string; // Name of enrolment plugin.
|
||||||
|
status: string; // Status of enrolment plugin.
|
||||||
|
enrolpassword?: string; // Password required for enrolment.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of enrol_self_enrol_user WS.
|
||||||
|
*/
|
||||||
|
type AddonEnrolSelfEnrolUserWSParams = {
|
||||||
|
courseid: number; // Id of the course.
|
||||||
|
password?: string; // Enrolment key.
|
||||||
|
instanceid?: number; // Instance id of self enrolment plugin.
|
||||||
|
};
|
|
@ -47,8 +47,8 @@ export class AddonNotesCourseOptionHandlerService implements CoreCourseOptionsHa
|
||||||
accessData: CoreCourseAccess,
|
accessData: CoreCourseAccess,
|
||||||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
if (accessData && accessData.type === CoreCourseProvider.ACCESS_GUEST) {
|
||||||
return false; // Not enabled for guests.
|
return false; // Not enabled for guest access.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (navOptions && navOptions.notes !== undefined) {
|
if (navOptions && navOptions.notes !== undefined) {
|
||||||
|
|
|
@ -61,7 +61,12 @@ export class CorePasswordModalComponent {
|
||||||
ModalController.dismiss(response);
|
ModalController.dismiss(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof response.error === 'string') {
|
||||||
this.error = response.error;
|
this.error = response.error;
|
||||||
|
} else if (response.error) {
|
||||||
|
ModalController.dismiss(response.error);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -76,7 +76,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
@Input() initialSectionId?: number; // The section to load first (by ID).
|
@Input() initialSectionId?: number; // The section to load first (by ID).
|
||||||
@Input() initialSectionNumber?: number; // The section to load first (by number).
|
@Input() initialSectionNumber?: number; // The section to load first (by number).
|
||||||
@Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.
|
@Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.
|
||||||
@Input() isGuest?: boolean; // If user is accessing as a guest.
|
@Input() isGuest?: boolean; // If user is accessing using an ACCESS_GUEST enrolment method.
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@ViewChildren(CoreDynamicComponent) dynamicComponents?: QueryList<CoreDynamicComponent<any>>;
|
@ViewChildren(CoreDynamicComponent) dynamicComponents?: QueryList<CoreDynamicComponent<any>>;
|
||||||
|
|
|
@ -35,9 +35,6 @@
|
||||||
"errordownloadingsection": "Error downloading section.",
|
"errordownloadingsection": "Error downloading section.",
|
||||||
"errorgetmodule": "Error getting activity data.",
|
"errorgetmodule": "Error getting activity data.",
|
||||||
"failed": "Failed",
|
"failed": "Failed",
|
||||||
"guestaccess_passwordinvalid": "Incorrect access password, please try again",
|
|
||||||
"guestaccess_withpassword": "Guest access requires password",
|
|
||||||
"guestaccess": "Guest access",
|
|
||||||
"hiddenfromstudents": "Hidden from students",
|
"hiddenfromstudents": "Hidden from students",
|
||||||
"hiddenoncoursepage": "Available but not shown on course page",
|
"hiddenoncoursepage": "Available but not shown on course page",
|
||||||
"highlighted": "Highlighted",
|
"highlighted": "Highlighted",
|
||||||
|
|
|
@ -138,26 +138,18 @@
|
||||||
<ion-label>{{item.data.title | translate }}</ion-label>
|
<ion-label>{{item.data.title | translate }}</ion-label>
|
||||||
</ion-button>
|
</ion-button>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ng-container *ngIf="!isEnrolled">
|
||||||
<ion-button expand="block" (click)="enrolMe()" *ngIf="!isEnrolled && (selfEnrolInstances.length > 0 || otherEnrolments)"
|
<ion-button expand="block" (click)="enrolMe()" *ngIf="selfEnrolInstances.length || hasBrowserEnrolments" class="ion-text-wrap">
|
||||||
class="ion-text-wrap">
|
|
||||||
{{ 'core.courses.enrolme' | translate }}
|
{{ 'core.courses.enrolme' | translate }}
|
||||||
</ion-button>
|
</ion-button>
|
||||||
|
|
||||||
<ion-card class="core-info-card ion-text-wrap" *ngIf="!isEnrolled && !selfEnrolInstances.length && !otherEnrolments">
|
<ion-card class="core-info-card ion-text-wrap" *ngIf="!selfEnrolInstances.length && !hasBrowserEnrolments">
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
|
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
|
||||||
<ion-label>{{ 'core.courses.notenrollable' | translate }}</ion-label>
|
<ion-label>{{ 'core.courses.notenrollable' | translate }}</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-card>
|
</ion-card>
|
||||||
|
</ng-container>
|
||||||
<ion-card class="core-info-card ion-text-wrap" *ngIf="!isEnrolled && useGuestAccess && guestAccessPasswordRequired">
|
|
||||||
<ion-item>
|
|
||||||
<ion-icon name="fas-key" slot="start" aria-hidden="true"></ion-icon>
|
|
||||||
<ion-label>{{ 'core.course.guestaccess_withpassword' | translate }}</ion-label>
|
|
||||||
</ion-item>
|
|
||||||
</ion-card>
|
|
||||||
|
|
||||||
<ion-button (click)="openCourse()" *ngIf="!isModal && canAccessCourse" expand="block" fill="outline" class="ion-text-wrap">
|
<ion-button (click)="openCourse()" *ngIf="!isModal && canAccessCourse" expand="block" fill="outline" class="ion-text-wrap">
|
||||||
<ion-icon name="fas-eye" slot="start" aria-hidden="true"></ion-icon>
|
<ion-icon name="fas-eye" slot="start" aria-hidden="true"></ion-icon>
|
||||||
{{ 'core.course.viewcourse' | translate }}
|
{{ 'core.course.viewcourse' | translate }}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import { CoreSites } from '@services/sites';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import {
|
import {
|
||||||
CoreCourseCustomField,
|
CoreCourseCustomField,
|
||||||
CoreCourseEnrolmentMethod,
|
|
||||||
CoreCourses,
|
CoreCourses,
|
||||||
CoreCourseSearchedData,
|
CoreCourseSearchedData,
|
||||||
CoreCoursesProvider,
|
CoreCoursesProvider,
|
||||||
|
@ -37,13 +36,12 @@ import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '@features/course
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { CoreColors } from '@singletons/colors';
|
import { CoreColors } from '@singletons/colors';
|
||||||
import { CorePath } from '@singletons/path';
|
import { CorePath } from '@singletons/path';
|
||||||
import { CorePromisedValue } from '@classes/promised-value';
|
|
||||||
import { CorePlatform } from '@services/platform';
|
import { CorePlatform } from '@services/platform';
|
||||||
import { CorePasswordModalResponse } from '@components/password-modal/password-modal';
|
|
||||||
import { CoreTime } from '@singletons/time';
|
import { CoreTime } from '@singletons/time';
|
||||||
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
||||||
|
import { CoreEnrolHelper } from '@features/enrol/services/enrol-helper';
|
||||||
const ENROL_BROWSER_METHODS = ['fee', 'paypal'];
|
import { CoreEnrolDelegate } from '@features/enrol/services/enrol-delegate';
|
||||||
|
import { CoreEnrolEnrolmentMethod } from '@features/enrol/services/enrol';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that shows the summary of a course including buttons to enrol and other available options.
|
* Page that shows the summary of a course including buttons to enrol and other available options.
|
||||||
|
@ -61,21 +59,21 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
||||||
@ViewChild('courseThumb') courseThumb?: ElementRef;
|
@ViewChild('courseThumb') courseThumb?: ElementRef;
|
||||||
|
|
||||||
isEnrolled = false;
|
isEnrolled = false;
|
||||||
|
|
||||||
canAccessCourse = true;
|
canAccessCourse = true;
|
||||||
selfEnrolInstances: CoreCourseEnrolmentMethod[] = [];
|
useGuestAccess = false;
|
||||||
otherEnrolments = false;
|
|
||||||
|
selfEnrolInstances: CoreEnrolEnrolmentMethod[] = [];
|
||||||
|
guestEnrolInstances: CoreEnrolEnrolmentMethod[] = [];
|
||||||
|
hasBrowserEnrolments = false;
|
||||||
dataLoaded = false;
|
dataLoaded = false;
|
||||||
isModal = false;
|
isModal = false;
|
||||||
contactsExpanded = false;
|
contactsExpanded = false;
|
||||||
useGuestAccess = false;
|
|
||||||
guestAccessPasswordRequired = false;
|
|
||||||
courseUrl = '';
|
courseUrl = '';
|
||||||
progress?: number;
|
progress?: number;
|
||||||
courseMenuHandlers: CoreCourseOptionsMenuHandlerToDisplay[] = [];
|
courseMenuHandlers: CoreCourseOptionsMenuHandlerToDisplay[] = [];
|
||||||
|
|
||||||
protected actionSheet?: HTMLIonActionSheetElement;
|
protected actionSheet?: HTMLIonActionSheetElement;
|
||||||
protected guestInstanceId = new CorePromisedValue<number | undefined>();
|
|
||||||
protected courseData = new CorePromisedValue<CoreCourseSummaryData | undefined>();
|
|
||||||
protected waitStart = 0;
|
protected waitStart = 0;
|
||||||
protected enrolUrl = '';
|
protected enrolUrl = '';
|
||||||
protected pageDestroyed = false;
|
protected pageDestroyed = false;
|
||||||
|
@ -143,40 +141,14 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
||||||
await this.getCourse();
|
await this.getCourse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the user can access as guest.
|
|
||||||
*
|
|
||||||
* @returns Promise resolved if can access as guest, rejected otherwise. Resolve param indicates if
|
|
||||||
* password is required for guest access.
|
|
||||||
*/
|
|
||||||
protected async canAccessAsGuest(): Promise<boolean> {
|
|
||||||
const guestInstanceId = await this.guestInstanceId;
|
|
||||||
if (guestInstanceId === undefined) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const info = await CoreCourses.getCourseGuestEnrolmentInfo(guestInstanceId);
|
|
||||||
|
|
||||||
// Don't allow guest access if it requires a password if not supported.
|
|
||||||
this.guestAccessPasswordRequired = info.passwordrequired;
|
|
||||||
|
|
||||||
return info.status && (!info.passwordrequired || CoreCourses.isValidateGuestAccessPasswordAvailable());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience function to get course. We use this to determine if a user can see the course or not.
|
* Convenience function to get course. We use this to determine if a user can see the course or not.
|
||||||
*
|
*
|
||||||
* @param refresh If it's refreshing content.
|
* @param refresh If it's refreshing content.
|
||||||
*/
|
*/
|
||||||
protected async getCourse(refresh = false): Promise<void> {
|
protected async getCourse(refresh = false): Promise<void> {
|
||||||
this.otherEnrolments = false;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await this.getCourseData();
|
||||||
this.getEnrolmentMethods(),
|
|
||||||
this.getCourseData(),
|
|
||||||
this.loadCourseExtraData(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
this.logView();
|
this.logView();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -201,40 +173,13 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
||||||
this.dataLoaded = true;
|
this.dataLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get course enrolment methods.
|
|
||||||
*/
|
|
||||||
protected async getEnrolmentMethods(): Promise<void> {
|
|
||||||
this.selfEnrolInstances = [];
|
|
||||||
this.guestInstanceId.reset();
|
|
||||||
|
|
||||||
const enrolmentMethods = await CoreCourses.getCourseEnrolmentMethods(this.courseId);
|
|
||||||
|
|
||||||
enrolmentMethods.forEach((method) => {
|
|
||||||
if (!CoreUtils.isTrueOrOne(method.status)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method.type === 'self') {
|
|
||||||
this.selfEnrolInstances.push(method);
|
|
||||||
} else if (method.type === 'guest') {
|
|
||||||
this.guestInstanceId.resolve(method.id);
|
|
||||||
} else {
|
|
||||||
// Other enrolments that comes from that WS should need user action.
|
|
||||||
this.otherEnrolments = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!this.guestInstanceId.isSettled()) {
|
|
||||||
// No guest instance found.
|
|
||||||
this.guestInstanceId.resolve(undefined);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get course data.
|
* Get course data.
|
||||||
*/
|
*/
|
||||||
protected async getCourseData(): Promise<void> {
|
protected async getCourseData(): Promise<void> {
|
||||||
|
this.canAccessCourse = false;
|
||||||
|
this.useGuestAccess = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if user is enrolled in the course.
|
// Check if user is enrolled in the course.
|
||||||
try {
|
try {
|
||||||
|
@ -250,40 +195,51 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
||||||
this.canAccessCourse = true;
|
this.canAccessCourse = true;
|
||||||
this.useGuestAccess = false;
|
this.useGuestAccess = false;
|
||||||
} catch {
|
} catch {
|
||||||
// The user is not an admin/manager. Check if we can provide guest access to the course.
|
// Ignore errors.
|
||||||
this.canAccessCourse = await this.canAccessAsGuest();
|
|
||||||
this.useGuestAccess = this.canAccessCourse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.courseData.resolve(this.course);
|
const courseByField = await CoreUtils.ignoreErrors(CoreCourses.getCourseByField('id', this.courseId));
|
||||||
|
if (courseByField) {
|
||||||
|
if (this.course) {
|
||||||
|
this.course.customfields = courseByField.customfields;
|
||||||
|
this.course.contacts = courseByField.contacts;
|
||||||
|
this.course.displayname = courseByField.displayname;
|
||||||
|
this.course.categoryname = courseByField.categoryname;
|
||||||
|
this.course.overviewfiles = courseByField.overviewfiles;
|
||||||
|
} else {
|
||||||
|
this.course = courseByField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.getEnrolmentInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load some extra data for the course.
|
* Get course enrolment methods.
|
||||||
*/
|
*/
|
||||||
protected async loadCourseExtraData(): Promise<void> {
|
protected async getEnrolmentInfo(): Promise<void> {
|
||||||
try {
|
if (this.isEnrolled) {
|
||||||
const courseByField = await CoreCourses.getCourseByField('id', this.courseId);
|
return;
|
||||||
const courseData = await this.courseData;
|
|
||||||
|
|
||||||
if (courseData) {
|
|
||||||
courseData.customfields = courseByField.customfields;
|
|
||||||
courseData.contacts = courseByField.contacts;
|
|
||||||
courseData.displayname = courseByField.displayname;
|
|
||||||
courseData.categoryname = courseByField.categoryname;
|
|
||||||
courseData.overviewfiles = courseByField.overviewfiles;
|
|
||||||
} else {
|
|
||||||
this.course = courseByField;
|
|
||||||
this.courseData.resolve(courseByField);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// enrollmentmethods contains ALL enrolment methods including manual.
|
const enrolByType = await CoreEnrolHelper.getEnrolmentsByType(this.courseId);
|
||||||
if (!this.isEnrolled && courseByField.enrollmentmethods?.some((method) => ENROL_BROWSER_METHODS.includes(method))) {
|
|
||||||
this.otherEnrolments = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch {
|
this.hasBrowserEnrolments = enrolByType.hasBrowser;
|
||||||
// Ignore errors.
|
this.selfEnrolInstances = enrolByType.self;
|
||||||
|
this.guestEnrolInstances = enrolByType.guest;
|
||||||
|
|
||||||
|
if (!this.canAccessCourse) {
|
||||||
|
// The user is not an admin/manager. Check if we can provide guest access to the course.
|
||||||
|
const promises = this.guestEnrolInstances.map(async (method) => {
|
||||||
|
const canAccess = await CoreEnrolDelegate.canAccess(method);
|
||||||
|
if (canAccess) {
|
||||||
|
this.canAccessCourse = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
this.useGuestAccess = this.canAccessCourse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,6 +258,33 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
||||||
await CoreCourseOptionsDelegate.getMenuHandlersToDisplay(this.course, refresh, this.useGuestAccess);
|
await CoreCourseOptionsDelegate.getMenuHandlersToDisplay(this.course, refresh, this.useGuestAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates if the user has access to the course and opens it.
|
||||||
|
*
|
||||||
|
* @param enrolMethod The enrolment method.
|
||||||
|
* @param replaceCurrentPage If current place should be replaced in the navigation stack.
|
||||||
|
*/
|
||||||
|
async validateAccessAndOpen(enrolMethod: CoreEnrolEnrolmentMethod, replaceCurrentPage: boolean): Promise<void> {
|
||||||
|
if (!this.canAccessCourse || !this.course || this.isModal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let validated = false;
|
||||||
|
try {
|
||||||
|
validated = await CoreEnrolDelegate.validateAccess(enrolMethod);
|
||||||
|
} catch {
|
||||||
|
this.refreshData();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreCourseHelper.openCourse(this.course, { params: { isGuest: this.useGuestAccess }, replace: replaceCurrentPage });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open the course.
|
* Open the course.
|
||||||
*
|
*
|
||||||
|
@ -312,51 +295,35 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const guestInstanceId = await this.guestInstanceId;
|
const hasAccess = await CoreCourseHelper.userHasAccessToCourse(this.courseId);
|
||||||
if (this.useGuestAccess && this.guestAccessPasswordRequired && guestInstanceId) {
|
if (!hasAccess && this.guestEnrolInstances.length) {
|
||||||
// Check if the user has access to the course as guest with a previous sent password.
|
if (this.guestEnrolInstances.length == 1) {
|
||||||
let validated = await CoreCourseHelper.userHasAccessToCourse(this.courseId);
|
this.validateAccessAndOpen(this.guestEnrolInstances[0], replaceCurrentPage);
|
||||||
|
|
||||||
if (!validated) {
|
return;
|
||||||
try {
|
|
||||||
type ValidatorResponse = CorePasswordModalResponse & { cancel?: boolean };
|
|
||||||
const validatePassword = async (password: string): Promise<ValidatorResponse> => {
|
|
||||||
try {
|
|
||||||
const response = await CoreCourses.validateGuestAccessPassword(guestInstanceId, password);
|
|
||||||
|
|
||||||
validated = response.validated;
|
|
||||||
let error = response.hint;
|
|
||||||
if (!validated && !error) {
|
|
||||||
error = 'core.course.guestaccess_passwordinvalid';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const buttons: ActionSheetButton[] = this.guestEnrolInstances.map((enrolMethod) => ({
|
||||||
password, validated, error,
|
text: enrolMethod.name,
|
||||||
};
|
handler: (): void => {
|
||||||
} catch {
|
this.validateAccessAndOpen(enrolMethod, replaceCurrentPage);
|
||||||
this.refreshData();
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
return {
|
buttons.push({
|
||||||
password,
|
text: Translate.instant('core.cancel'),
|
||||||
cancel: true,
|
role: 'cancel',
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const response = await CoreDomUtils.promptPassword<ValidatorResponse>({
|
|
||||||
title: 'core.course.guestaccess',
|
|
||||||
validator: validatePassword,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.validated || response.cancel) {
|
this.actionSheet = await ActionSheetController.create({
|
||||||
|
header: Translate.instant('core.course.viewcourse'),
|
||||||
|
buttons: buttons,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.actionSheet.present();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
// Cancelled, return
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CoreCourseHelper.openCourse(this.course, { params: { isGuest: this.useGuestAccess }, replace: replaceCurrentPage });
|
CoreCourseHelper.openCourse(this.course, { params: { isGuest: this.useGuestAccess }, replace: replaceCurrentPage });
|
||||||
}
|
}
|
||||||
|
@ -389,75 +356,23 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirm user to Self enrol in course.
|
* Self enrol in a course.
|
||||||
*
|
*
|
||||||
* @param enrolMethod The enrolment method.
|
* @param enrolMethod The enrolment method.
|
||||||
*/
|
*/
|
||||||
async selfEnrolConfirm(enrolMethod: CoreCourseEnrolmentMethod): Promise<void> {
|
async selfEnrolInCourse(enrolMethod: CoreEnrolEnrolmentMethod): Promise<void> {
|
||||||
|
let enrolled = false;
|
||||||
try {
|
try {
|
||||||
await CoreDomUtils.showConfirm(Translate.instant('core.courses.confirmselfenrol'), enrolMethod.name);
|
enrolled = await CoreEnrolDelegate.enrol(enrolMethod);
|
||||||
|
|
||||||
this.selfEnrolInCourse(enrolMethod.id);
|
|
||||||
} catch {
|
} catch {
|
||||||
// User cancelled.
|
this.refreshData();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Self enrol in a course.
|
|
||||||
*
|
|
||||||
* @param instanceId The instance ID.
|
|
||||||
* @returns Promise resolved when self enrolled.
|
|
||||||
*/
|
|
||||||
async selfEnrolInCourse(instanceId: number): Promise<void> {
|
|
||||||
const validatePassword = async (password = ''): Promise<CorePasswordModalResponse> => {
|
|
||||||
const response: CorePasswordModalResponse = {
|
|
||||||
password,
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
response.validated = await CoreCourses.selfEnrol(this.courseId, password, instanceId);
|
|
||||||
} catch (error) {
|
|
||||||
if (error && error.errorcode === CoreCoursesProvider.ENROL_INVALID_KEY) {
|
|
||||||
response.validated = false;
|
|
||||||
response.error = error.message;
|
|
||||||
} else {
|
|
||||||
CoreDomUtils.showErrorModalDefault(error, 'core.courses.errorselfenrol', true);
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return response;
|
|
||||||
};
|
|
||||||
|
|
||||||
const modal = await CoreDomUtils.showModalLoading('core.loading', true);
|
|
||||||
let response: CorePasswordModalResponse | undefined;
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = await validatePassword();
|
|
||||||
} catch {
|
|
||||||
return;
|
|
||||||
} finally {
|
|
||||||
modal.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.validated) {
|
|
||||||
try {
|
|
||||||
const response = await CoreDomUtils.promptPassword({
|
|
||||||
validator: validatePassword,
|
|
||||||
title: 'core.courses.selfenrolment',
|
|
||||||
placeholder: 'core.courses.password',
|
|
||||||
submit: 'core.courses.enrolme',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.validated) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch {
|
|
||||||
// Cancelled, return
|
if (!enrolled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh data.
|
// Refresh data.
|
||||||
this.isEnrolled = true;
|
this.isEnrolled = true;
|
||||||
|
@ -488,12 +403,18 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
||||||
|
|
||||||
promises.push(CoreCourses.invalidateUserCourses());
|
promises.push(CoreCourses.invalidateUserCourses());
|
||||||
promises.push(CoreCourses.invalidateCourse(this.courseId));
|
promises.push(CoreCourses.invalidateCourse(this.courseId));
|
||||||
promises.push(CoreCourses.invalidateCourseEnrolmentMethods(this.courseId));
|
|
||||||
promises.push(CoreCourseOptionsDelegate.clearAndInvalidateCoursesOptions(this.courseId));
|
promises.push(CoreCourseOptionsDelegate.clearAndInvalidateCoursesOptions(this.courseId));
|
||||||
promises.push(CoreCourses.invalidateCoursesByField('id', this.courseId));
|
promises.push(CoreCourses.invalidateCoursesByField('id', this.courseId));
|
||||||
if (this.guestInstanceId.value) {
|
|
||||||
promises.push(CoreCourses.invalidateCourseGuestEnrolmentInfo(this.guestInstanceId.value));
|
promises.push(CoreCourses.invalidateCourseEnrolmentMethods(this.courseId));
|
||||||
}
|
|
||||||
|
this.selfEnrolInstances.forEach((method) => {
|
||||||
|
promises.push(CoreEnrolDelegate.invalidate(method));
|
||||||
|
});
|
||||||
|
|
||||||
|
this.guestEnrolInstances.forEach((method) => {
|
||||||
|
promises.push(CoreEnrolDelegate.invalidate(method));
|
||||||
|
});
|
||||||
|
|
||||||
await Promise.all(promises).finally(() => this.getCourse()).finally(() => {
|
await Promise.all(promises).finally(() => this.getCourse()).finally(() => {
|
||||||
refresher?.complete();
|
refresher?.complete();
|
||||||
|
@ -587,13 +508,13 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
||||||
* Open enrol action sheet.
|
* Open enrol action sheet.
|
||||||
*/
|
*/
|
||||||
async enrolMe(): Promise<void> {
|
async enrolMe(): Promise<void> {
|
||||||
if (this.selfEnrolInstances.length == 1 && !this.otherEnrolments) {
|
if (this.selfEnrolInstances.length == 1 && !this.hasBrowserEnrolments) {
|
||||||
this.selfEnrolConfirm(this.selfEnrolInstances[0]);
|
this.selfEnrolInCourse(this.selfEnrolInstances[0]);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.selfEnrolInstances.length == 0 && this.otherEnrolments) {
|
if (this.selfEnrolInstances.length == 0 && this.hasBrowserEnrolments) {
|
||||||
this.browserEnrol();
|
this.browserEnrol();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -602,11 +523,11 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
||||||
const buttons: ActionSheetButton[] = this.selfEnrolInstances.map((enrolMethod) => ({
|
const buttons: ActionSheetButton[] = this.selfEnrolInstances.map((enrolMethod) => ({
|
||||||
text: enrolMethod.name,
|
text: enrolMethod.name,
|
||||||
handler: (): void => {
|
handler: (): void => {
|
||||||
this.selfEnrolConfirm(enrolMethod);
|
this.selfEnrolInCourse(enrolMethod);
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (this.otherEnrolments) {
|
if (this.hasBrowserEnrolments) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
text: Translate.instant('core.courses.completeenrolmentbrowser'),
|
text: Translate.instant('core.courses.completeenrolmentbrowser'),
|
||||||
handler: (): void => {
|
handler: (): void => {
|
||||||
|
|
|
@ -74,6 +74,8 @@ import { CoreCourseWithImageAndColor } from '@features/courses/services/courses-
|
||||||
import { CoreCourseSummaryPage } from '../pages/course-summary/course-summary.page';
|
import { CoreCourseSummaryPage } from '../pages/course-summary/course-summary.page';
|
||||||
import { CoreRemindersPushNotificationData } from '@features/reminders/services/reminders';
|
import { CoreRemindersPushNotificationData } from '@features/reminders/services/reminders';
|
||||||
import { CoreLocalNotifications } from '@services/local-notifications';
|
import { CoreLocalNotifications } from '@services/local-notifications';
|
||||||
|
import { AddonEnrolGuest } from '@addons/enrol/guest/services/guest';
|
||||||
|
import { CoreEnrol } from '@features/enrol/services/enrol';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefetch info of a module.
|
* Prefetch info of a module.
|
||||||
|
@ -622,19 +624,16 @@ export class CoreCourseHelperProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if guest access is enabled.
|
// Check if guest access is enabled.
|
||||||
const enrolmentMethods = await CoreCourses.getCourseEnrolmentMethods(courseId, siteId);
|
const enrolmentMethods = await CoreEnrol.getSupportedCourseEnrolmentMethods(courseId, { type: 'guest', siteId });
|
||||||
|
if (!enrolmentMethods) {
|
||||||
const method = enrolmentMethods.find((method) => method.type === 'guest');
|
|
||||||
|
|
||||||
if (!method) {
|
|
||||||
return { guestAccess: false };
|
return { guestAccess: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
const info = await CoreCourses.getCourseGuestEnrolmentInfo(method.id);
|
const info = await AddonEnrolGuest.getGuestEnrolmentInfo(enrolmentMethods[0].id);
|
||||||
|
|
||||||
// Don't allow guest access if it requires a password and it's available.
|
// Don't allow guest access if it requires a password and it's available.
|
||||||
return {
|
return {
|
||||||
guestAccess: info.status && (!info.passwordrequired || CoreCourses.isValidateGuestAccessPasswordAvailable()),
|
guestAccess: info.status && (!info.passwordrequired || AddonEnrolGuest.isValidateGuestAccessPasswordAvailable()),
|
||||||
passwordRequired: info.passwordrequired,
|
passwordRequired: info.passwordrequired,
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -2151,7 +2150,7 @@ export type CoreCoursePrefetchCourseOptions = {
|
||||||
sections?: CoreCourseWSSection[]; // List of course sections.
|
sections?: CoreCourseWSSection[]; // List of course sections.
|
||||||
courseHandlers?: CoreCourseOptionsHandlerToDisplay[]; // List of course handlers.
|
courseHandlers?: CoreCourseOptionsHandlerToDisplay[]; // List of course handlers.
|
||||||
menuHandlers?: CoreCourseOptionsMenuHandlerToDisplay[]; // List of course menu handlers.
|
menuHandlers?: CoreCourseOptionsMenuHandlerToDisplay[]; // List of course menu handlers.
|
||||||
isGuest?: boolean; // Whether the user is guest.
|
isGuest?: boolean; // Whether the user is using an ACCESS_GUEST enrolment method.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -357,7 +357,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
||||||
*
|
*
|
||||||
* @param course The course object.
|
* @param course The course object.
|
||||||
* @param refresh True if it should refresh the list.
|
* @param refresh True if it should refresh the list.
|
||||||
* @param isGuest Whether it's guest.
|
* @param isGuest Whether user is using an ACCESS_GUEST enrolment method.
|
||||||
* @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
* @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||||
* @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
* @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||||
* @returns Promise resolved with array of handlers.
|
* @returns Promise resolved with array of handlers.
|
||||||
|
@ -379,7 +379,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
||||||
*
|
*
|
||||||
* @param course The course object.
|
* @param course The course object.
|
||||||
* @param refresh True if it should refresh the list.
|
* @param refresh True if it should refresh the list.
|
||||||
* @param isGuest Whether it's guest.
|
* @param isGuest Whether user is using an ACCESS_GUEST enrolment method.
|
||||||
* @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
* @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||||
* @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
* @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||||
* @returns Promise resolved with array of handlers.
|
* @returns Promise resolved with array of handlers.
|
||||||
|
@ -402,7 +402,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
||||||
* @param menu If true, gets menu handlers; false, gets tab handlers
|
* @param menu If true, gets menu handlers; false, gets tab handlers
|
||||||
* @param course The course object.
|
* @param course The course object.
|
||||||
* @param refresh True if it should refresh the list.
|
* @param refresh True if it should refresh the list.
|
||||||
* @param isGuest Whether it's guest.
|
* @param isGuest Whether user is using an ACCESS_GUEST enrolment method.
|
||||||
* @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
* @param navOptions Course navigation options for current user. See CoreCoursesProvider.getUserNavigationOptions.
|
||||||
* @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
* @param admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||||
* @returns Promise resolved with array of handlers.
|
* @returns Promise resolved with array of handlers.
|
||||||
|
|
|
@ -61,7 +61,6 @@ Feature: Test basic usage of guest access course in app
|
||||||
And I press "Course 1" in the app
|
And I press "Course 1" in the app
|
||||||
Then I should find "Course summary" in the app
|
Then I should find "Course summary" in the app
|
||||||
And I should find "Course" in the app
|
And I should find "Course" in the app
|
||||||
And I should find "Guest access requires password" in the app
|
|
||||||
|
|
||||||
When I press "View course" "ion-button" in the app
|
When I press "View course" "ion-button" in the app
|
||||||
And I set the following fields to these values in the app:
|
And I set the following fields to these values in the app:
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
|
|
||||||
<span *ngIf="(layout == 'list' || layout == 'listwithenrol') && !isEnrolled" class="core-course-enrol-icons">
|
<span *ngIf="(layout == 'list' || layout == 'listwithenrol') && !isEnrolled" class="core-course-enrol-icons">
|
||||||
<ion-icon *ngFor="let icon of enrolmentIcons" color="medium" [name]="icon.icon" [title]="icon.label | translate"
|
<ion-icon *ngFor="let icon of enrolmentIcons" color="medium" [name]="icon.icon" [title]="icon.label | translate"
|
||||||
[attr.aria-label]="icon.label | translate">
|
[attr.aria-label]="icon.label | translate" [ngClass]="[icon.className]">
|
||||||
</ion-icon>
|
</ion-icon>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { CoreEventCourseStatusChanged, CoreEventObserver, CoreEvents } from '@si
|
||||||
import { CoreCourseListItem, CoreCourses, CoreCoursesProvider } from '../../services/courses';
|
import { CoreCourseListItem, CoreCourses, CoreCoursesProvider } from '../../services/courses';
|
||||||
import { CoreCoursesHelper, CoreEnrolledCourseDataWithExtraInfoAndOptions } from '../../services/courses-helper';
|
import { CoreCoursesHelper, CoreEnrolledCourseDataWithExtraInfoAndOptions } from '../../services/courses-helper';
|
||||||
import { CoreCoursesCourseOptionsMenuComponent } from '../course-options-menu/course-options-menu';
|
import { CoreCoursesCourseOptionsMenuComponent } from '../course-options-menu/course-options-menu';
|
||||||
|
import { CoreEnrolHelper } from '@features/enrol/services/enrol-helper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This directive is meant to display an item for a list of courses.
|
* This directive is meant to display an item for a list of courses.
|
||||||
|
@ -98,33 +99,7 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
|
||||||
this.initPrefetchCourse();
|
this.initPrefetchCourse();
|
||||||
|
|
||||||
} else if ('enrollmentmethods' in this.course) {
|
} else if ('enrollmentmethods' in this.course) {
|
||||||
this.enrolmentIcons = [];
|
this.enrolmentIcons = await CoreEnrolHelper.getEnrolmentIcons(this.course.enrollmentmethods, this.course.id);
|
||||||
|
|
||||||
this.course.enrollmentmethods.forEach((instance) => {
|
|
||||||
if (instance === 'self') {
|
|
||||||
this.enrolmentIcons.push({
|
|
||||||
label: 'core.courses.selfenrolment',
|
|
||||||
icon: 'fas-key',
|
|
||||||
});
|
|
||||||
} else if (instance === 'guest') {
|
|
||||||
this.enrolmentIcons.push({
|
|
||||||
label: 'core.courses.allowguests',
|
|
||||||
icon: 'fas-unlock',
|
|
||||||
});
|
|
||||||
} else if (instance === 'paypal' || instance === 'fee') {
|
|
||||||
this.enrolmentIcons.push({
|
|
||||||
label: 'core.courses.otherenrolments',
|
|
||||||
icon: 'fas-up-right-from-square',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.enrolmentIcons.length == 0) {
|
|
||||||
this.enrolmentIcons.push({
|
|
||||||
label: 'core.courses.notenrollable',
|
|
||||||
icon: 'fas-lock',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"addtofavourites": "Star this course",
|
"addtofavourites": "Star this course",
|
||||||
"allowguests": "This course allows guest users to enter",
|
|
||||||
"aria:coursecategory": "Course category",
|
"aria:coursecategory": "Course category",
|
||||||
"aria:coursename": "Course name",
|
"aria:coursename": "Course name",
|
||||||
"aria:courseprogress": "Course progress:",
|
"aria:courseprogress": "Course progress:",
|
||||||
|
@ -10,7 +9,6 @@
|
||||||
"cannotretrievemorecategories": "Categories deeper than level {{$a}} cannot be retrieved.",
|
"cannotretrievemorecategories": "Categories deeper than level {{$a}} cannot be retrieved.",
|
||||||
"categories": "Course categories",
|
"categories": "Course categories",
|
||||||
"completeenrolmentbrowser": "Complete enrolment in browser",
|
"completeenrolmentbrowser": "Complete enrolment in browser",
|
||||||
"confirmselfenrol": "Are you sure you want to enrol yourself in this course?",
|
|
||||||
"courses": "Courses",
|
"courses": "Courses",
|
||||||
"downloadcourses": "Download all courses",
|
"downloadcourses": "Download all courses",
|
||||||
"enrolme": "Enrol me",
|
"enrolme": "Enrol me",
|
||||||
|
@ -18,7 +16,6 @@
|
||||||
"errorloadcourses": "An error occurred while loading courses.",
|
"errorloadcourses": "An error occurred while loading courses.",
|
||||||
"errorloadplugins": "The plugins required by this course could not be loaded correctly. Please reload the app to try again.",
|
"errorloadplugins": "The plugins required by this course could not be loaded correctly. Please reload the app to try again.",
|
||||||
"errorsearching": "An error occurred while searching.",
|
"errorsearching": "An error occurred while searching.",
|
||||||
"errorselfenrol": "An error occurred while self enrolling.",
|
|
||||||
"favourite": "Starred course",
|
"favourite": "Starred course",
|
||||||
"filtermycourses": "Filter my courses",
|
"filtermycourses": "Filter my courses",
|
||||||
"frontpage": "Site home",
|
"frontpage": "Site home",
|
||||||
|
@ -40,7 +37,6 @@
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
"searchcourses": "Search courses",
|
"searchcourses": "Search courses",
|
||||||
"searchcoursesadvice": "You can use the search courses button to find courses to access as a guest or enrol yourself in courses that allow it.",
|
"searchcoursesadvice": "You can use the search courses button to find courses to access as a guest or enrol yourself in courses that allow it.",
|
||||||
"selfenrolment": "Self enrolment",
|
|
||||||
"show": "Restore to view",
|
"show": "Restore to view",
|
||||||
"showonlyenrolled": "Show only my courses",
|
"showonlyenrolled": "Show only my courses",
|
||||||
"therearecourses": "There are {{$a}} courses",
|
"therearecourses": "There are {{$a}} courses",
|
||||||
|
|
|
@ -16,13 +16,15 @@ import { Injectable } from '@angular/core';
|
||||||
import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites';
|
import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites';
|
||||||
import { CoreSite, CoreSiteWSPreSets, WSObservable } from '@classes/site';
|
import { CoreSite, CoreSiteWSPreSets, WSObservable } from '@classes/site';
|
||||||
import { makeSingleton } from '@singletons';
|
import { makeSingleton } from '@singletons';
|
||||||
import { CoreStatusWithWarningsWSResponse, CoreWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws';
|
import { CoreWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws';
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEvents } from '@singletons/events';
|
||||||
import { CoreWSError } from '@classes/errors/wserror';
|
|
||||||
import { CoreCourseAnyCourseDataWithExtraInfoAndOptions, CoreCourseWithImageAndColor } from './courses-helper';
|
import { CoreCourseAnyCourseDataWithExtraInfoAndOptions, CoreCourseWithImageAndColor } from './courses-helper';
|
||||||
import { asyncObservable, firstValueFrom, ignoreErrors, zipIncludingComplete } from '@/core/utils/rxjs';
|
import { asyncObservable, firstValueFrom, ignoreErrors, zipIncludingComplete } from '@/core/utils/rxjs';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
|
import { AddonEnrolGuest, AddonEnrolGuestInfo } from '@addons/enrol/guest/services/guest';
|
||||||
|
import { AddonEnrolSelf } from '@addons/enrol/self/services/self';
|
||||||
|
import { CoreEnrol, CoreEnrolEnrolmentInfo, CoreEnrolEnrolmentMethod } from '@features/enrol/services/enrol';
|
||||||
|
|
||||||
const ROOT_CACHE_KEY = 'mmCourses:';
|
const ROOT_CACHE_KEY = 'mmCourses:';
|
||||||
|
|
||||||
|
@ -309,33 +311,13 @@ export class CoreCoursesProvider {
|
||||||
/**
|
/**
|
||||||
* Get the enrolment methods from a course.
|
* Get the enrolment methods from a course.
|
||||||
*
|
*
|
||||||
* @param id ID of the course.
|
* @param courseId ID of the course.
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @returns Promise resolved with the methods.
|
* @returns Promise resolved with the methods.
|
||||||
|
* @deprecated since 4.3. Use CoreEnrol.getSupportedCourseEnrolmentMethods instead.
|
||||||
*/
|
*/
|
||||||
async getCourseEnrolmentMethods(id: number, siteId?: string): Promise<CoreCourseEnrolmentMethod[]> {
|
async getCourseEnrolmentMethods(courseId: number, siteId?: string): Promise<CoreEnrolEnrolmentMethod[]> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
return CoreEnrol.getSupportedCourseEnrolmentMethods(courseId, { siteId });
|
||||||
|
|
||||||
const params: CoreEnrolGetCourseEnrolmentMethodsWSParams = {
|
|
||||||
courseid: id,
|
|
||||||
};
|
|
||||||
const preSets = {
|
|
||||||
cacheKey: this.getCourseEnrolmentMethodsCacheKey(id),
|
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
|
||||||
};
|
|
||||||
|
|
||||||
return site.read<CoreEnrolGetCourseEnrolmentMethodsWSResponse>
|
|
||||||
('core_enrol_get_course_enrolment_methods', params, preSets);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get cache key for get course enrolment methods WS call.
|
|
||||||
*
|
|
||||||
* @param id Course ID.
|
|
||||||
* @returns Cache key.
|
|
||||||
*/
|
|
||||||
protected getCourseEnrolmentMethodsCacheKey(id: number): string {
|
|
||||||
return ROOT_CACHE_KEY + 'enrolmentmethods:' + id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -344,70 +326,10 @@ export class CoreCoursesProvider {
|
||||||
* @param instanceId Guest instance ID.
|
* @param instanceId Guest instance ID.
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @returns Promise resolved when the info is retrieved.
|
* @returns Promise resolved when the info is retrieved.
|
||||||
|
* @deprecated since 4.3 use AddonEnrolGuest.getCourseGuestEnrolmentInfo instead.
|
||||||
*/
|
*/
|
||||||
async getCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<CoreCourseEnrolmentGuestMethod> {
|
async getCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<AddonEnrolGuestInfo> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
return AddonEnrolGuest.getGuestEnrolmentInfo(instanceId, siteId);
|
||||||
const params: EnrolGuestGetInstanceInfoWSParams = {
|
|
||||||
instanceid: instanceId,
|
|
||||||
};
|
|
||||||
const preSets: CoreSiteWSPreSets = {
|
|
||||||
cacheKey: this.getCourseGuestEnrolmentInfoCacheKey(instanceId),
|
|
||||||
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
|
||||||
};
|
|
||||||
const response = await site.read<EnrolGuestGetInstanceInfoWSResponse>('enrol_guest_get_instance_info', params, preSets);
|
|
||||||
|
|
||||||
return response.instanceinfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get cache key for get course guest enrolment methods WS call.
|
|
||||||
*
|
|
||||||
* @param instanceId Guest instance ID.
|
|
||||||
* @returns Cache key.
|
|
||||||
*/
|
|
||||||
protected getCourseGuestEnrolmentInfoCacheKey(instanceId: number): string {
|
|
||||||
return ROOT_CACHE_KEY + 'guestinfo:' + instanceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if guest password validation WS is available on the current site.
|
|
||||||
*
|
|
||||||
* @returns Whether guest password validation WSget courses by field is available.
|
|
||||||
*/
|
|
||||||
isValidateGuestAccessPasswordAvailable(): boolean {
|
|
||||||
return CoreSites.wsAvailableInCurrentSite('enrol_guest_validate_password');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform password validation of guess access.
|
|
||||||
*
|
|
||||||
* @param enrolmentInstanceId Instance id of guest enrolment plugin.
|
|
||||||
* @param password Course Password.
|
|
||||||
* @returns Wether the password is valid.
|
|
||||||
*/
|
|
||||||
async validateGuestAccessPassword(
|
|
||||||
enrolmentInstanceId: number,
|
|
||||||
password: string,
|
|
||||||
): Promise<EnrolGuestValidatePasswordWSResponse> {
|
|
||||||
const site = CoreSites.getCurrentSite();
|
|
||||||
|
|
||||||
if (!site) {
|
|
||||||
return {
|
|
||||||
validated: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const preSets: CoreSiteWSPreSets = {
|
|
||||||
getFromCache: false,
|
|
||||||
saveToCache: false,
|
|
||||||
emergencyCache: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const params: EnrolGuestValidatePasswordWSParams = {
|
|
||||||
instanceid: enrolmentInstanceId,
|
|
||||||
password,
|
|
||||||
};
|
|
||||||
|
|
||||||
return await site.read<EnrolGuestValidatePasswordWSResponse>('enrol_guest_validate_password', params, preSets);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1116,14 +1038,13 @@ export class CoreCoursesProvider {
|
||||||
/**
|
/**
|
||||||
* Invalidates get course enrolment methods WS call.
|
* Invalidates get course enrolment methods WS call.
|
||||||
*
|
*
|
||||||
* @param id Course ID.
|
* @param courseId Course ID.
|
||||||
* @param siteId Site Id. If not defined, use current site.
|
* @param siteId Site Id. If not defined, use current site.
|
||||||
* @returns Promise resolved when the data is invalidated.
|
* @returns Promise resolved when the data is invalidated.
|
||||||
|
* @deprecated since 4.3, use CoreEnrol.invalidateCourseEnrolmentMethods instead.
|
||||||
*/
|
*/
|
||||||
async invalidateCourseEnrolmentMethods(id: number, siteId?: string): Promise<void> {
|
async invalidateCourseEnrolmentMethods(courseId: number, siteId?: string): Promise<void> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
return CoreEnrol.invalidateCourseEnrolmentMethods(courseId, siteId);
|
||||||
|
|
||||||
await site.invalidateWsCacheForKey(this.getCourseEnrolmentMethodsCacheKey(id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1132,11 +1053,10 @@ export class CoreCoursesProvider {
|
||||||
* @param instanceId Guest instance ID.
|
* @param instanceId Guest instance ID.
|
||||||
* @param siteId Site Id. If not defined, use current site.
|
* @param siteId Site Id. If not defined, use current site.
|
||||||
* @returns Promise resolved when the data is invalidated.
|
* @returns Promise resolved when the data is invalidated.
|
||||||
|
* @deprecated since 4.3, use CoreEnrolDelegate.invalidate instead.
|
||||||
*/
|
*/
|
||||||
async invalidateCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<void> {
|
async invalidateCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<void> {
|
||||||
const site = await CoreSites.getSite(siteId);
|
return AddonEnrolGuest.invalidateGuestEnrolmentInfo(instanceId, siteId);
|
||||||
|
|
||||||
await site.invalidateWsCacheForKey(this.getCourseGuestEnrolmentInfoCacheKey(instanceId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1270,16 +1190,6 @@ export class CoreCoursesProvider {
|
||||||
await site.invalidateWsCacheForKey(this.getUserNavigationOptionsCacheKey(courseIds));
|
await site.invalidateWsCacheForKey(this.getUserNavigationOptionsCacheKey(courseIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if WS to retrieve guest enrolment data is available.
|
|
||||||
*
|
|
||||||
* @returns Whether guest WS is available.
|
|
||||||
* @deprecated since app 3.9.5
|
|
||||||
*/
|
|
||||||
isGuestWSAvailable(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report a dashboard or my courses page view event.
|
* Report a dashboard or my courses page view event.
|
||||||
*
|
*
|
||||||
|
@ -1339,42 +1249,10 @@ export class CoreCoursesProvider {
|
||||||
* @param siteId Site ID. If not defined, use current site.
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
* @returns Promise resolved if the user is enrolled. If the password is invalid, the promise is rejected
|
* @returns Promise resolved if the user is enrolled. If the password is invalid, the promise is rejected
|
||||||
* with an object with errorcode = CoreCoursesProvider.ENROL_INVALID_KEY.
|
* with an object with errorcode = CoreCoursesProvider.ENROL_INVALID_KEY.
|
||||||
|
* @deprecated since 4.3, use CoreEnrolDelegate.enrol instead.
|
||||||
*/
|
*/
|
||||||
async selfEnrol(courseId: number, password: string = '', instanceId?: number, siteId?: string): Promise<boolean> {
|
async selfEnrol(courseId: number, password: string = '', instanceId?: number, siteId?: string): Promise<boolean> {
|
||||||
|
return AddonEnrolSelf.selfEnrol(courseId, password, instanceId, siteId);
|
||||||
const site = await CoreSites.getSite(siteId);
|
|
||||||
|
|
||||||
const params: EnrolSelfEnrolUserWSParams = {
|
|
||||||
courseid: courseId,
|
|
||||||
password: password,
|
|
||||||
};
|
|
||||||
if (instanceId) {
|
|
||||||
params.instanceid = instanceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await site.write<CoreStatusWithWarningsWSResponse>('enrol_self_enrol_user', params);
|
|
||||||
|
|
||||||
if (!response) {
|
|
||||||
throw Error('WS enrol_self_enrol_user failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.status) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.warnings && response.warnings.length) {
|
|
||||||
// Invalid password warnings.
|
|
||||||
const warning = response.warnings.find((warning) =>
|
|
||||||
warning.warningcode == '2' || warning.warningcode == '3' || warning.warningcode == '4');
|
|
||||||
|
|
||||||
if (warning) {
|
|
||||||
throw new CoreWSError({ errorcode: CoreCoursesProvider.ENROL_INVALID_KEY, message: warning.message });
|
|
||||||
} else {
|
|
||||||
throw new CoreWSError(response.warnings[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Error('WS enrol_self_enrol_user failed without warnings');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1823,50 +1701,19 @@ export type CoreCourseUserAdminOrNavOptionIndexed = {
|
||||||
boolean; // Whether the option is available or not.
|
boolean; // Whether the option is available or not.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Params of core_enrol_get_course_enrolment_methods WS.
|
|
||||||
*/
|
|
||||||
type CoreEnrolGetCourseEnrolmentMethodsWSParams = {
|
|
||||||
courseid: number; // Course id.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data returned by core_enrol_get_course_enrolment_methods WS.
|
|
||||||
*/
|
|
||||||
type CoreEnrolGetCourseEnrolmentMethodsWSResponse = CoreCourseEnrolmentMethod[];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Course enrolment basic info.
|
* Course enrolment basic info.
|
||||||
|
*
|
||||||
|
* @deprecated since 4.3 use CoreEnrolEnrolmentInfo instead.
|
||||||
*/
|
*/
|
||||||
export type CoreCourseEnrolmentInfo = {
|
export type CoreCourseEnrolmentInfo = CoreEnrolEnrolmentInfo;
|
||||||
id: number; // Id of course enrolment instance.
|
|
||||||
courseid: number; // Id of course.
|
|
||||||
type: string; // Type of enrolment plugin.
|
|
||||||
name: string; // Name of enrolment plugin.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Course enrolment method.
|
* Course enrolment method.
|
||||||
|
*
|
||||||
|
* @deprecated since 4.3 use CoreEnrolEnrolmentMethod instead.
|
||||||
*/
|
*/
|
||||||
export type CoreCourseEnrolmentMethod = CoreCourseEnrolmentInfo & {
|
export type CoreCourseEnrolmentMethod = CoreEnrolEnrolmentMethod;
|
||||||
wsfunction?: string; // Webservice function to get more information.
|
|
||||||
status: string; // Status of enrolment plugin. True if successful, else error message or false.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Params of enrol_guest_get_instance_info WS.
|
|
||||||
*/
|
|
||||||
type EnrolGuestGetInstanceInfoWSParams = {
|
|
||||||
instanceid: number; // Instance id of guest enrolment plugin.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data returned by enrol_guest_get_instance_info WS.
|
|
||||||
*/
|
|
||||||
export type EnrolGuestGetInstanceInfoWSResponse = {
|
|
||||||
instanceinfo: CoreCourseEnrolmentGuestMethod;
|
|
||||||
warnings?: CoreWSExternalWarning[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Params of core_course_get_recent_courses WS.
|
* Params of core_course_get_recent_courses WS.
|
||||||
|
@ -1888,23 +1735,6 @@ export type CoreCourseGetRecentCoursesOptions = CoreSitesCommonWSOptions & {
|
||||||
sort?: string; // Sort string.
|
sort?: string; // Sort string.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Course guest enrolment method.
|
|
||||||
*/
|
|
||||||
export type CoreCourseEnrolmentGuestMethod = CoreCourseEnrolmentInfo & {
|
|
||||||
passwordrequired: boolean; // Is a password required?
|
|
||||||
status: boolean; // Is the enrolment enabled?
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Params of enrol_self_enrol_user WS.
|
|
||||||
*/
|
|
||||||
type EnrolSelfEnrolUserWSParams = {
|
|
||||||
courseid: number; // Id of the course.
|
|
||||||
password?: string; // Enrolment key.
|
|
||||||
instanceid?: number; // Instance id of self enrolment plugin.
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Params of core_course_set_favourite_courses WS.
|
* Params of core_course_set_favourite_courses WS.
|
||||||
*/
|
*/
|
||||||
|
@ -1928,23 +1758,6 @@ export type CoreCourseAnyCourseDataWithOptions = CoreCourseAnyCourseData & {
|
||||||
admOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
admOptions?: CoreCourseUserAdminOrNavOptionIndexed;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Params of enrol_guest_validate_password WS.
|
|
||||||
*/
|
|
||||||
type EnrolGuestValidatePasswordWSParams = {
|
|
||||||
instanceid: number; // instance id of guest enrolment plugin
|
|
||||||
password: string; // the course password
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Data returned by enrol_guest_get_instance_info WS.
|
|
||||||
*/
|
|
||||||
export type EnrolGuestValidatePasswordWSResponse = {
|
|
||||||
validated: boolean; // Whether the password was successfully validated
|
|
||||||
hint?: string; // Password hint (if enabled)
|
|
||||||
warnings?: CoreWSExternalWarning[];
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Params of core_my_view_page WS.
|
* Params of core_my_view_page WS.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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, Type } from '@angular/core';
|
||||||
|
|
||||||
|
import { CoreEnrolDelegateService } from './services/enrol-delegate';
|
||||||
|
|
||||||
|
export const CORE_ENROL_SERVICES: Type<unknown>[] = [
|
||||||
|
CoreEnrolDelegateService,
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({})
|
||||||
|
export class CoreEnrolModule {}
|
|
@ -0,0 +1,240 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||||
|
import { makeSingleton } from '@singletons';
|
||||||
|
import { CoreEnrolEnrolmentMethod } from './enrol';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enrolment actions.
|
||||||
|
*/
|
||||||
|
export enum CoreEnrolAction {
|
||||||
|
BROWSER = 'browser', // User should use the browser to enrol. Ie. paypal
|
||||||
|
SELF = 'self', // User can enrol himself or herself.
|
||||||
|
GUEST = 'guest', // User can view the course without enrolling, like guest enrolment.
|
||||||
|
NOT_SUPPORTED = 'not_supported', // Enrolment method is not supported by the app.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that all enrolment plugins must implement.
|
||||||
|
*/
|
||||||
|
export interface CoreEnrolHandler extends CoreDelegateHandler {
|
||||||
|
/**
|
||||||
|
* Name of the enrol the handler supports. E.g. 'self'.
|
||||||
|
*/
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to take when enroling.
|
||||||
|
*/
|
||||||
|
enrolmentAction: CoreEnrolAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data needed to render the icon.
|
||||||
|
*
|
||||||
|
* @param courseId Course Id.
|
||||||
|
* @returns Icons data.
|
||||||
|
*/
|
||||||
|
getInfoIcons?(courseId: number): Promise<CoreEnrolInfoIcon[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates the enrolment info.
|
||||||
|
*
|
||||||
|
* @param method Course enrolment method.
|
||||||
|
* @returns Promise resolved when done
|
||||||
|
*/
|
||||||
|
invalidate?(method: CoreEnrolEnrolmentMethod): Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that all self enrolment plugins must implement.
|
||||||
|
*/
|
||||||
|
export interface CoreEnrolSelfHandler extends CoreEnrolHandler {
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
enrolmentAction: CoreEnrolAction.SELF;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enrols the user and returns if it has been enrolled or not.
|
||||||
|
*
|
||||||
|
* @param method Course enrolment method.
|
||||||
|
* @returns If the user has been enrolled.
|
||||||
|
*/
|
||||||
|
enrol(method: CoreEnrolEnrolmentMethod): Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that all guest enrolment plugins must implement.
|
||||||
|
*/
|
||||||
|
export interface CoreEnrolGuestHandler extends CoreEnrolHandler {
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
enrolmentAction: CoreEnrolAction.GUEST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user can access to the course.
|
||||||
|
*
|
||||||
|
* @param method Course enrolment method.
|
||||||
|
* @returns Whether the user can access.
|
||||||
|
*/
|
||||||
|
canAccess(method: CoreEnrolEnrolmentMethod): Promise<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the access to a course
|
||||||
|
*
|
||||||
|
* @param method Course enrolment method.
|
||||||
|
* @returns Whether the user has validated the access to the course.
|
||||||
|
*/
|
||||||
|
validateAccess(method: CoreEnrolEnrolmentMethod): Promise<boolean>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data needed to render a enrolment icons. It's returned by the handler.
|
||||||
|
*/
|
||||||
|
export interface CoreEnrolInfoIcon {
|
||||||
|
label: string;
|
||||||
|
icon: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate to register enrol handlers.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class CoreEnrolDelegateService extends CoreDelegate<CoreEnrolHandler> {
|
||||||
|
|
||||||
|
protected handlerNameProperty = 'type';
|
||||||
|
protected featurePrefix = 'CoreEnrolDelegate_';
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super('CoreEnrolDelegate', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an enrolment plugin is supported.
|
||||||
|
*
|
||||||
|
* @param methodType Enrol method type.
|
||||||
|
* @returns Whether it's supported.
|
||||||
|
*/
|
||||||
|
isEnrolSupported(methodType: string): boolean {
|
||||||
|
return this.hasHandler(methodType, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enrolment action.
|
||||||
|
*
|
||||||
|
* @param methodType Enrol method type.
|
||||||
|
* @returns The enrolment action to take.
|
||||||
|
*/
|
||||||
|
getEnrolmentAction(methodType: string): CoreEnrolAction {
|
||||||
|
const handler = this.getHandler(methodType, false);
|
||||||
|
if (!handler) {
|
||||||
|
return CoreEnrolAction.NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler.enrolmentAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the enrol icon for a certain enrolment method.
|
||||||
|
*
|
||||||
|
* @param methodType The methodType to get the icon.
|
||||||
|
* @param courseId Course Id.
|
||||||
|
* @returns Promise resolved with the display data.
|
||||||
|
*/
|
||||||
|
async getInfoIcons(methodType: string, courseId: number): Promise<CoreEnrolInfoIcon[]> {
|
||||||
|
const icons = await this.executeFunctionOnEnabled<CoreEnrolInfoIcon[]>(
|
||||||
|
methodType,
|
||||||
|
'getInfoIcons',
|
||||||
|
[courseId],
|
||||||
|
);
|
||||||
|
|
||||||
|
icons?.forEach((icon) => {
|
||||||
|
if (!icon.className) {
|
||||||
|
icon.className = `addon-enrol-${methodType}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return icons || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enrols the user and returns if it has been enrolled or not.
|
||||||
|
*
|
||||||
|
* @param method Course enrolment method.
|
||||||
|
* @returns If the user has been enrolled.
|
||||||
|
*/
|
||||||
|
async enrol(method: CoreEnrolEnrolmentMethod): Promise<boolean> {
|
||||||
|
const enrolled = await this.executeFunctionOnEnabled<boolean>(
|
||||||
|
method.type,
|
||||||
|
'enrol',
|
||||||
|
[method],
|
||||||
|
);
|
||||||
|
|
||||||
|
return !!enrolled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the user can access to the course.
|
||||||
|
*
|
||||||
|
* @param method Course enrolment method.
|
||||||
|
* @returns Whether the user can access.
|
||||||
|
*/
|
||||||
|
async canAccess(method: CoreEnrolEnrolmentMethod): Promise<boolean> {
|
||||||
|
const canAccess = await this.executeFunctionOnEnabled<boolean>(
|
||||||
|
method.type,
|
||||||
|
'canAccess',
|
||||||
|
[method],
|
||||||
|
);
|
||||||
|
|
||||||
|
return !!canAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the access to a course.
|
||||||
|
*
|
||||||
|
* @param method Course enrolment method.
|
||||||
|
* @returns Whether the user has validated the access to the course.
|
||||||
|
*/
|
||||||
|
async validateAccess(method: CoreEnrolEnrolmentMethod): Promise<boolean> {
|
||||||
|
const validated = await this.executeFunctionOnEnabled<boolean>(
|
||||||
|
method.type,
|
||||||
|
'validateAccess',
|
||||||
|
[method],
|
||||||
|
);
|
||||||
|
|
||||||
|
return !!validated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates the enrolment info.
|
||||||
|
*
|
||||||
|
* @param method Course enrolment method.
|
||||||
|
* @returns Promise resolved when done
|
||||||
|
*/
|
||||||
|
async invalidate(method: CoreEnrolEnrolmentMethod): Promise<void> {
|
||||||
|
await this.executeFunctionOnEnabled<boolean>(
|
||||||
|
method.type,
|
||||||
|
'invalidate',
|
||||||
|
[method],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CoreEnrolDelegate = makeSingleton(CoreEnrolDelegateService);
|
|
@ -0,0 +1,128 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { makeSingleton } from '@singletons';
|
||||||
|
import { CoreEnrolAction, CoreEnrolDelegate, CoreEnrolInfoIcon } from './enrol-delegate';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreEnrol, CoreEnrolEnrolmentMethod } from './enrol';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides helper functions for enrolment plugins.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class CoreEnrolHelperService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enrolment icons to show enrol status.
|
||||||
|
*
|
||||||
|
* @param methodTypes List of enrolment types to show.
|
||||||
|
* @param courseId Course Id.
|
||||||
|
* @returns Enrolment icons to show.
|
||||||
|
*/
|
||||||
|
async getEnrolmentIcons(methodTypes: string[], courseId: number): Promise<CoreEnrolInfoIcon[]> {
|
||||||
|
methodTypes = CoreUtils.uniqueArray(methodTypes);
|
||||||
|
|
||||||
|
let enrolmentIcons: CoreEnrolInfoIcon[] = [];
|
||||||
|
let addBrowserOption = false;
|
||||||
|
|
||||||
|
const promises = methodTypes.map(async (type) => {
|
||||||
|
const enrolIcons = await CoreEnrolDelegate.getInfoIcons(type, courseId);
|
||||||
|
|
||||||
|
if (enrolIcons.length) {
|
||||||
|
enrolmentIcons = enrolmentIcons.concat(enrolIcons);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = CoreEnrolDelegate.getEnrolmentAction(type);
|
||||||
|
addBrowserOption = addBrowserOption || action === CoreEnrolAction.BROWSER;
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
|
if (addBrowserOption) {
|
||||||
|
enrolmentIcons.push({
|
||||||
|
className: 'enrol_browser',
|
||||||
|
label: 'core.courses.otherenrolments',
|
||||||
|
icon: 'fas-up-right-from-square',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enrolmentIcons.length == 0) {
|
||||||
|
enrolmentIcons.push({
|
||||||
|
className: 'enrol_locked',
|
||||||
|
label: 'core.courses.notenrollable',
|
||||||
|
icon: 'fas-lock',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return enrolmentIcons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get enrolment methods divided by type.
|
||||||
|
*
|
||||||
|
* @param courseId Course Id.
|
||||||
|
* @returns Enrolment info divided by types.
|
||||||
|
*/
|
||||||
|
async getEnrolmentsByType(courseId: number): Promise<CoreEnrolmentsByType> {
|
||||||
|
const enrolmentMethods = await CoreEnrol.getSupportedCourseEnrolmentMethods(courseId);
|
||||||
|
|
||||||
|
const self: CoreEnrolEnrolmentMethod[] = [];
|
||||||
|
const guest: CoreEnrolEnrolmentMethod[] = [];
|
||||||
|
let hasBrowser = false;
|
||||||
|
let hasNotSupported = false;
|
||||||
|
|
||||||
|
enrolmentMethods.forEach((method) => {
|
||||||
|
if (!CoreUtils.isTrueOrOne(method.status)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = CoreEnrolDelegate.getEnrolmentAction(method.type);
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case CoreEnrolAction.SELF:
|
||||||
|
self.push(method);
|
||||||
|
break;
|
||||||
|
case CoreEnrolAction.GUEST:
|
||||||
|
guest.push(method);
|
||||||
|
break;
|
||||||
|
case CoreEnrolAction.BROWSER:
|
||||||
|
hasBrowser = true;
|
||||||
|
break;
|
||||||
|
case CoreEnrolAction.NOT_SUPPORTED:
|
||||||
|
hasNotSupported = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
self,
|
||||||
|
guest,
|
||||||
|
hasBrowser,
|
||||||
|
hasNotSupported,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CoreEnrolHelper = makeSingleton(CoreEnrolHelperService);
|
||||||
|
|
||||||
|
export type CoreEnrolmentsByType = {
|
||||||
|
self: CoreEnrolEnrolmentMethod[];
|
||||||
|
guest: CoreEnrolEnrolmentMethod[];
|
||||||
|
hasBrowser: boolean;
|
||||||
|
hasNotSupported: boolean;
|
||||||
|
};
|
|
@ -0,0 +1,138 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// 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 { makeSingleton } from '@singletons';
|
||||||
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreEnrolAction, CoreEnrolDelegate } from './enrol-delegate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that provides functions for enrolment plugins.
|
||||||
|
*/
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class CoreEnrolService {
|
||||||
|
|
||||||
|
protected static readonly ROOT_CACHE_KEY = 'CoreEnrol:';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the enrolment methods from a course.
|
||||||
|
*
|
||||||
|
* @param courseId ID of the course.
|
||||||
|
* @param siteId Site ID. If not defined, use current site.
|
||||||
|
* @returns Promise resolved with the methods.
|
||||||
|
*/
|
||||||
|
protected async getCourseEnrolmentMethods(courseId: number, siteId?: string): Promise<CoreEnrolEnrolmentMethod[]> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
const params: CoreEnrolGetCourseEnrolmentMethodsWSParams = {
|
||||||
|
courseid: courseId,
|
||||||
|
};
|
||||||
|
const preSets = {
|
||||||
|
cacheKey: this.getCourseEnrolmentMethodsCacheKey(courseId),
|
||||||
|
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read<CoreEnrolGetCourseEnrolmentMethodsWSResponse>('core_enrol_get_course_enrolment_methods', params, preSets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the enrolment methods from a course that are enabled and supported by the app.
|
||||||
|
*
|
||||||
|
* @param courseId ID of the course.
|
||||||
|
* @param options Options.
|
||||||
|
* @returns Promise resolved with the methods.
|
||||||
|
*/
|
||||||
|
async getSupportedCourseEnrolmentMethods(
|
||||||
|
courseId: number,
|
||||||
|
options: CoreEnrolGetSupportedMethodsOptions = {},
|
||||||
|
): Promise<CoreEnrolEnrolmentMethod[]> {
|
||||||
|
const methods = await CoreEnrol.getCourseEnrolmentMethods(courseId, options.siteId);
|
||||||
|
|
||||||
|
return methods.filter((method) => {
|
||||||
|
if (options.type && method.type !== options.type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CoreEnrolDelegate.isEnrolSupported(method.type) && CoreUtils.isTrueOrOne(method.status) &&
|
||||||
|
(!options.action || CoreEnrolDelegate.getEnrolmentAction(method.type) === options.action);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for get course enrolment methods WS call.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @returns Cache key.
|
||||||
|
*/
|
||||||
|
protected getCourseEnrolmentMethodsCacheKey(courseId: number): string {
|
||||||
|
return CoreEnrolService.ROOT_CACHE_KEY + 'enrolmentmethods:' + courseId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidates get course enrolment methods WS call.
|
||||||
|
*
|
||||||
|
* @param courseId Course ID.
|
||||||
|
* @param siteId Site Id. If not defined, use current site.
|
||||||
|
* @returns Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
async invalidateCourseEnrolmentMethods(courseId: number, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.getSite(siteId);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
site.invalidateWsCacheForKey(this.getCourseEnrolmentMethodsCacheKey(courseId)),
|
||||||
|
site.invalidateWsCacheForKey(`mmCourses:enrolmentmethods:${courseId}`), // @todo: Remove after 4.3 release.
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CoreEnrol = makeSingleton(CoreEnrolService);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Params of core_enrol_get_course_enrolment_methods WS.
|
||||||
|
*/
|
||||||
|
type CoreEnrolGetCourseEnrolmentMethodsWSParams = {
|
||||||
|
courseid: number; // Course id.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data returned by core_enrol_get_course_enrolment_methods WS.
|
||||||
|
*/
|
||||||
|
type CoreEnrolGetCourseEnrolmentMethodsWSResponse = CoreEnrolEnrolmentMethod[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Course enrolment method.
|
||||||
|
*/
|
||||||
|
export type CoreEnrolEnrolmentMethod = CoreEnrolEnrolmentInfo & {
|
||||||
|
wsfunction?: string; // Webservice function to get more information.
|
||||||
|
status: string; // Status of enrolment plugin. True if successful, else error message or false.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Course enrolment basic info.
|
||||||
|
*/
|
||||||
|
export type CoreEnrolEnrolmentInfo = {
|
||||||
|
id: number; // Id of course enrolment instance.
|
||||||
|
courseid: number; // Id of course.
|
||||||
|
type: string; // Type of enrolment plugin.
|
||||||
|
name: string; // Name of enrolment plugin.
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CoreEnrolGetSupportedMethodsOptions = {
|
||||||
|
type?: string; // If set, only get methods of a certain type.
|
||||||
|
action?: CoreEnrolAction; // If set, only get methods that use a certain action.
|
||||||
|
siteId?: string; // Site ID. If not defined, use current site.
|
||||||
|
};
|
|
@ -21,6 +21,7 @@ import { CoreCourseModule } from './course/course.module';
|
||||||
import { CoreCoursesModule } from './courses/courses.module';
|
import { CoreCoursesModule } from './courses/courses.module';
|
||||||
import { CoreEditorModule } from './editor/editor.module';
|
import { CoreEditorModule } from './editor/editor.module';
|
||||||
import { CoreEmulatorModule } from './emulator/emulator.module';
|
import { CoreEmulatorModule } from './emulator/emulator.module';
|
||||||
|
import { CoreEnrolModule } from './enrol/enrol.module';
|
||||||
import { CoreFileUploaderModule } from './fileuploader/fileuploader.module';
|
import { CoreFileUploaderModule } from './fileuploader/fileuploader.module';
|
||||||
import { CoreFilterModule } from './filter/filter.module';
|
import { CoreFilterModule } from './filter/filter.module';
|
||||||
import { CoreGradesModule } from './grades/grades.module';
|
import { CoreGradesModule } from './grades/grades.module';
|
||||||
|
@ -53,6 +54,7 @@ import { CoreReportBuilderModule } from './reportbuilder/reportbuilder.module';
|
||||||
CoreCourseModule,
|
CoreCourseModule,
|
||||||
CoreCoursesModule,
|
CoreCoursesModule,
|
||||||
CoreEditorModule,
|
CoreEditorModule,
|
||||||
|
CoreEnrolModule,
|
||||||
CoreFileUploaderModule,
|
CoreFileUploaderModule,
|
||||||
CoreFilterModule,
|
CoreFilterModule,
|
||||||
CoreGradesModule,
|
CoreGradesModule,
|
||||||
|
|
|
@ -60,6 +60,7 @@ import { CorePlatform } from '@services/platform';
|
||||||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
||||||
import { CoreLang } from '@services/lang';
|
import { CoreLang } from '@services/lang';
|
||||||
import { CorePasswordModalParams, CorePasswordModalResponse } from '@components/password-modal/password-modal';
|
import { CorePasswordModalParams, CorePasswordModalResponse } from '@components/password-modal/password-modal';
|
||||||
|
import { CoreWSError } from '@classes/errors/wserror';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "Utils" service with helper functions for UI, DOM elements and HTML code.
|
* "Utils" service with helper functions for UI, DOM elements and HTML code.
|
||||||
|
@ -1903,6 +1904,8 @@ export class CoreDomUtilsProvider {
|
||||||
|
|
||||||
if (modalData === undefined) {
|
if (modalData === undefined) {
|
||||||
throw new CoreCanceledError();
|
throw new CoreCanceledError();
|
||||||
|
} else if (modalData instanceof CoreWSError) {
|
||||||
|
throw modalData;
|
||||||
}
|
}
|
||||||
|
|
||||||
return modalData;
|
return modalData;
|
||||||
|
|
Loading…
Reference in New Issue