parent
47af5a252e
commit
b975e6f0d8
|
@ -237,6 +237,14 @@
|
|||
"addon.coursecompletion.requirement": "block_completionstatus",
|
||||
"addon.coursecompletion.status": "moodle",
|
||||
"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.pushdisabledwarning": "local_moodlemobileapp",
|
||||
"addon.messages.acceptandaddcontact": "message",
|
||||
|
@ -1590,9 +1598,6 @@
|
|||
"core.course.errordownloadingsection": "local_moodlemobileapp",
|
||||
"core.course.errorgetmodule": "local_moodlemobileapp",
|
||||
"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.hiddenoncoursepage": "moodle",
|
||||
"core.course.highlighted": "moodle",
|
||||
|
@ -1623,7 +1628,6 @@
|
|||
"core.coursedetails": "moodle",
|
||||
"core.coursenogroups": "local_moodlemobileapp",
|
||||
"core.courses.addtofavourites": "block_myoverview",
|
||||
"core.courses.allowguests": "enrol_guest",
|
||||
"core.courses.aria:coursecategory": "course",
|
||||
"core.courses.aria:coursename": "course",
|
||||
"core.courses.aria:courseprogress": "block_myoverview",
|
||||
|
@ -1633,7 +1637,6 @@
|
|||
"core.courses.cannotretrievemorecategories": "local_moodlemobileapp",
|
||||
"core.courses.categories": "moodle",
|
||||
"core.courses.completeenrolmentbrowser": "local_moodlemobileapp",
|
||||
"core.courses.confirmselfenrol": "local_moodlemobileapp",
|
||||
"core.courses.courses": "moodle",
|
||||
"core.courses.downloadcourses": "local_moodlemobileapp",
|
||||
"core.courses.enrolme": "local_moodlemobileapp",
|
||||
|
@ -1641,7 +1644,6 @@
|
|||
"core.courses.errorloadcourses": "local_moodlemobileapp",
|
||||
"core.courses.errorloadplugins": "local_moodlemobileapp",
|
||||
"core.courses.errorsearching": "local_moodlemobileapp",
|
||||
"core.courses.errorselfenrol": "local_moodlemobileapp",
|
||||
"core.courses.favourite": "course",
|
||||
"core.courses.filtermycourses": "local_moodlemobileapp",
|
||||
"core.courses.frontpage": "admin",
|
||||
|
@ -1663,7 +1665,6 @@
|
|||
"core.courses.search": "moodle",
|
||||
"core.courses.searchcourses": "moodle",
|
||||
"core.courses.searchcoursesadvice": "local_moodlemobileapp",
|
||||
"core.courses.selfenrolment": "local_moodlemobileapp",
|
||||
"core.courses.show": "block_myoverview",
|
||||
"core.courses.showonlyenrolled": "local_moodlemobileapp",
|
||||
"core.courses.therearecourses": "moodle",
|
||||
|
|
|
@ -20,6 +20,7 @@ import { AddonBlogModule } from './blog/blog.module';
|
|||
import { AddonCalendarModule } from './calendar/calendar.module';
|
||||
import { AddonCompetencyModule } from './competency/competency.module';
|
||||
import { AddonCourseCompletionModule } from './coursecompletion/coursecompletion.module';
|
||||
import { AddonEnrolModule } from './enrol/enrol.module';
|
||||
import { AddonFilterModule } from './filter/filter.module';
|
||||
import { AddonMessageOutputModule } from './messageoutput/messageoutput.module';
|
||||
import { AddonMessagesModule } from './messages/messages.module';
|
||||
|
@ -42,6 +43,7 @@ import { AddonUserProfileFieldModule } from './userprofilefield/userprofilefield
|
|||
AddonCalendarModule,
|
||||
AddonCompetencyModule,
|
||||
AddonCourseCompletionModule,
|
||||
AddonEnrolModule,
|
||||
AddonFilterModule,
|
||||
AddonMessageOutputModule,
|
||||
AddonMessagesModule,
|
||||
|
|
|
@ -50,8 +50,8 @@ export class AddonCompetencyCourseOptionHandlerService implements CoreCourseOpti
|
|||
accessData: CoreCourseAccess,
|
||||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||
): Promise<boolean> {
|
||||
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
||||
return false; // Not enabled for guests.
|
||||
if (accessData && accessData.type === CoreCourseProvider.ACCESS_GUEST) {
|
||||
return false; // Not enabled for guest access.
|
||||
}
|
||||
|
||||
if (navOptions && navOptions.competencies !== undefined) {
|
||||
|
|
|
@ -43,8 +43,8 @@ export class AddonCourseCompletionCourseOptionHandlerService implements CoreCour
|
|||
* @inheritdoc
|
||||
*/
|
||||
async isEnabledForCourse(courseId: number, accessData: CoreCourseAccess): Promise<boolean> {
|
||||
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
||||
return false; // Not enabled for guests.
|
||||
if (accessData && accessData.type === CoreCourseProvider.ACCESS_GUEST) {
|
||||
return false; // Not enabled for guest access.
|
||||
}
|
||||
|
||||
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,
|
||||
navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
|
||||
): Promise<boolean> {
|
||||
if (accessData && accessData.type == CoreCourseProvider.ACCESS_GUEST) {
|
||||
return false; // Not enabled for guests.
|
||||
if (accessData && accessData.type === CoreCourseProvider.ACCESS_GUEST) {
|
||||
return false; // Not enabled for guest access.
|
||||
}
|
||||
|
||||
if (navOptions && navOptions.notes !== undefined) {
|
||||
|
|
|
@ -61,7 +61,12 @@ export class CorePasswordModalComponent {
|
|||
ModalController.dismiss(response);
|
||||
}
|
||||
|
||||
this.error = response.error;
|
||||
if (typeof response.error === 'string') {
|
||||
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() 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() 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
|
||||
@ViewChildren(CoreDynamicComponent) dynamicComponents?: QueryList<CoreDynamicComponent<any>>;
|
||||
|
|
|
@ -35,9 +35,6 @@
|
|||
"errordownloadingsection": "Error downloading section.",
|
||||
"errorgetmodule": "Error getting activity data.",
|
||||
"failed": "Failed",
|
||||
"guestaccess_passwordinvalid": "Incorrect access password, please try again",
|
||||
"guestaccess_withpassword": "Guest access requires password",
|
||||
"guestaccess": "Guest access",
|
||||
"hiddenfromstudents": "Hidden from students",
|
||||
"hiddenoncoursepage": "Available but not shown on course page",
|
||||
"highlighted": "Highlighted",
|
||||
|
|
|
@ -138,26 +138,18 @@
|
|||
<ion-label>{{item.data.title | translate }}</ion-label>
|
||||
</ion-button>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="!isEnrolled">
|
||||
<ion-button expand="block" (click)="enrolMe()" *ngIf="selfEnrolInstances.length || hasBrowserEnrolments" class="ion-text-wrap">
|
||||
{{ 'core.courses.enrolme' | translate }}
|
||||
</ion-button>
|
||||
|
||||
<ion-button expand="block" (click)="enrolMe()" *ngIf="!isEnrolled && (selfEnrolInstances.length > 0 || otherEnrolments)"
|
||||
class="ion-text-wrap">
|
||||
{{ 'core.courses.enrolme' | translate }}
|
||||
</ion-button>
|
||||
|
||||
<ion-card class="core-info-card ion-text-wrap" *ngIf="!isEnrolled && !selfEnrolInstances.length && !otherEnrolments">
|
||||
<ion-item>
|
||||
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>{{ 'core.courses.notenrollable' | translate }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
|
||||
<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-card class="core-info-card ion-text-wrap" *ngIf="!selfEnrolInstances.length && !hasBrowserEnrolments">
|
||||
<ion-item>
|
||||
<ion-icon name="fas-circle-info" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>{{ 'core.courses.notenrollable' | translate }}</ion-label>
|
||||
</ion-item>
|
||||
</ion-card>
|
||||
</ng-container>
|
||||
<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>
|
||||
{{ 'core.course.viewcourse' | translate }}
|
||||
|
|
|
@ -19,7 +19,6 @@ import { CoreSites } from '@services/sites';
|
|||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import {
|
||||
CoreCourseCustomField,
|
||||
CoreCourseEnrolmentMethod,
|
||||
CoreCourses,
|
||||
CoreCourseSearchedData,
|
||||
CoreCoursesProvider,
|
||||
|
@ -37,13 +36,12 @@ import { CoreCoursesHelper, CoreCourseWithImageAndColor } from '@features/course
|
|||
import { Subscription } from 'rxjs';
|
||||
import { CoreColors } from '@singletons/colors';
|
||||
import { CorePath } from '@singletons/path';
|
||||
import { CorePromisedValue } from '@classes/promised-value';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
import { CorePasswordModalResponse } from '@components/password-modal/password-modal';
|
||||
import { CoreTime } from '@singletons/time';
|
||||
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
||||
|
||||
const ENROL_BROWSER_METHODS = ['fee', 'paypal'];
|
||||
import { CoreEnrolHelper } from '@features/enrol/services/enrol-helper';
|
||||
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.
|
||||
|
@ -61,21 +59,21 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
@ViewChild('courseThumb') courseThumb?: ElementRef;
|
||||
|
||||
isEnrolled = false;
|
||||
|
||||
canAccessCourse = true;
|
||||
selfEnrolInstances: CoreCourseEnrolmentMethod[] = [];
|
||||
otherEnrolments = false;
|
||||
useGuestAccess = false;
|
||||
|
||||
selfEnrolInstances: CoreEnrolEnrolmentMethod[] = [];
|
||||
guestEnrolInstances: CoreEnrolEnrolmentMethod[] = [];
|
||||
hasBrowserEnrolments = false;
|
||||
dataLoaded = false;
|
||||
isModal = false;
|
||||
contactsExpanded = false;
|
||||
useGuestAccess = false;
|
||||
guestAccessPasswordRequired = false;
|
||||
courseUrl = '';
|
||||
progress?: number;
|
||||
courseMenuHandlers: CoreCourseOptionsMenuHandlerToDisplay[] = [];
|
||||
|
||||
protected actionSheet?: HTMLIonActionSheetElement;
|
||||
protected guestInstanceId = new CorePromisedValue<number | undefined>();
|
||||
protected courseData = new CorePromisedValue<CoreCourseSummaryData | undefined>();
|
||||
protected waitStart = 0;
|
||||
protected enrolUrl = '';
|
||||
protected pageDestroyed = false;
|
||||
|
@ -143,40 +141,14 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
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.
|
||||
*
|
||||
* @param refresh If it's refreshing content.
|
||||
*/
|
||||
protected async getCourse(refresh = false): Promise<void> {
|
||||
this.otherEnrolments = false;
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
this.getEnrolmentMethods(),
|
||||
this.getCourseData(),
|
||||
this.loadCourseExtraData(),
|
||||
]);
|
||||
await this.getCourseData();
|
||||
|
||||
this.logView();
|
||||
} catch (error) {
|
||||
|
@ -201,40 +173,13 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
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.
|
||||
*/
|
||||
protected async getCourseData(): Promise<void> {
|
||||
this.canAccessCourse = false;
|
||||
this.useGuestAccess = false;
|
||||
|
||||
try {
|
||||
// Check if user is enrolled in the course.
|
||||
try {
|
||||
|
@ -250,40 +195,51 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
this.canAccessCourse = true;
|
||||
this.useGuestAccess = false;
|
||||
} catch {
|
||||
// The user is not an admin/manager. Check if we can provide guest access to the course.
|
||||
this.canAccessCourse = await this.canAccessAsGuest();
|
||||
this.useGuestAccess = this.canAccessCourse;
|
||||
// Ignore errors.
|
||||
}
|
||||
|
||||
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> {
|
||||
try {
|
||||
const courseByField = await CoreCourses.getCourseByField('id', this.courseId);
|
||||
const courseData = await this.courseData;
|
||||
protected async getEnrolmentInfo(): Promise<void> {
|
||||
if (this.isEnrolled) {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
const enrolByType = await CoreEnrolHelper.getEnrolmentsByType(this.courseId);
|
||||
|
||||
// enrollmentmethods contains ALL enrolment methods including manual.
|
||||
if (!this.isEnrolled && courseByField.enrollmentmethods?.some((method) => ENROL_BROWSER_METHODS.includes(method))) {
|
||||
this.otherEnrolments = true;
|
||||
}
|
||||
this.hasBrowserEnrolments = enrolByType.hasBrowser;
|
||||
this.selfEnrolInstances = enrolByType.self;
|
||||
this.guestEnrolInstances = enrolByType.guest;
|
||||
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
@ -312,50 +295,34 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
return;
|
||||
}
|
||||
|
||||
const guestInstanceId = await this.guestInstanceId;
|
||||
if (this.useGuestAccess && this.guestAccessPasswordRequired && guestInstanceId) {
|
||||
// Check if the user has access to the course as guest with a previous sent password.
|
||||
let validated = await CoreCourseHelper.userHasAccessToCourse(this.courseId);
|
||||
const hasAccess = await CoreCourseHelper.userHasAccessToCourse(this.courseId);
|
||||
if (!hasAccess && this.guestEnrolInstances.length) {
|
||||
if (this.guestEnrolInstances.length == 1) {
|
||||
this.validateAccessAndOpen(this.guestEnrolInstances[0], replaceCurrentPage);
|
||||
|
||||
if (!validated) {
|
||||
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 {
|
||||
password, validated, error,
|
||||
};
|
||||
} catch {
|
||||
this.refreshData();
|
||||
|
||||
return {
|
||||
password,
|
||||
cancel: true,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const response = await CoreDomUtils.promptPassword<ValidatorResponse>({
|
||||
title: 'core.course.guestaccess',
|
||||
validator: validatePassword,
|
||||
});
|
||||
|
||||
if (!response.validated || response.cancel) {
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// Cancelled, return
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const buttons: ActionSheetButton[] = this.guestEnrolInstances.map((enrolMethod) => ({
|
||||
text: enrolMethod.name,
|
||||
handler: (): void => {
|
||||
this.validateAccessAndOpen(enrolMethod, replaceCurrentPage);
|
||||
},
|
||||
}));
|
||||
|
||||
buttons.push({
|
||||
text: Translate.instant('core.cancel'),
|
||||
role: 'cancel',
|
||||
});
|
||||
|
||||
this.actionSheet = await ActionSheetController.create({
|
||||
header: Translate.instant('core.course.viewcourse'),
|
||||
buttons: buttons,
|
||||
});
|
||||
|
||||
await this.actionSheet.present();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CoreCourseHelper.openCourse(this.course, { params: { isGuest: this.useGuestAccess }, replace: replaceCurrentPage });
|
||||
|
@ -389,74 +356,22 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Confirm user to Self enrol in course.
|
||||
* Self enrol in a course.
|
||||
*
|
||||
* @param enrolMethod The enrolment method.
|
||||
*/
|
||||
async selfEnrolConfirm(enrolMethod: CoreCourseEnrolmentMethod): Promise<void> {
|
||||
async selfEnrolInCourse(enrolMethod: CoreEnrolEnrolmentMethod): Promise<void> {
|
||||
let enrolled = false;
|
||||
try {
|
||||
await CoreDomUtils.showConfirm(Translate.instant('core.courses.confirmselfenrol'), enrolMethod.name);
|
||||
|
||||
this.selfEnrolInCourse(enrolMethod.id);
|
||||
enrolled = await CoreEnrolDelegate.enrol(enrolMethod);
|
||||
} 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;
|
||||
}
|
||||
} catch {
|
||||
// Cancelled, return
|
||||
return;
|
||||
}
|
||||
if (!enrolled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh data.
|
||||
|
@ -488,12 +403,18 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
|
||||
promises.push(CoreCourses.invalidateUserCourses());
|
||||
promises.push(CoreCourses.invalidateCourse(this.courseId));
|
||||
promises.push(CoreCourses.invalidateCourseEnrolmentMethods(this.courseId));
|
||||
promises.push(CoreCourseOptionsDelegate.clearAndInvalidateCoursesOptions(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(() => {
|
||||
refresher?.complete();
|
||||
|
@ -587,13 +508,13 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
* Open enrol action sheet.
|
||||
*/
|
||||
async enrolMe(): Promise<void> {
|
||||
if (this.selfEnrolInstances.length == 1 && !this.otherEnrolments) {
|
||||
this.selfEnrolConfirm(this.selfEnrolInstances[0]);
|
||||
if (this.selfEnrolInstances.length == 1 && !this.hasBrowserEnrolments) {
|
||||
this.selfEnrolInCourse(this.selfEnrolInstances[0]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selfEnrolInstances.length == 0 && this.otherEnrolments) {
|
||||
if (this.selfEnrolInstances.length == 0 && this.hasBrowserEnrolments) {
|
||||
this.browserEnrol();
|
||||
|
||||
return;
|
||||
|
@ -602,11 +523,11 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
const buttons: ActionSheetButton[] = this.selfEnrolInstances.map((enrolMethod) => ({
|
||||
text: enrolMethod.name,
|
||||
handler: (): void => {
|
||||
this.selfEnrolConfirm(enrolMethod);
|
||||
this.selfEnrolInCourse(enrolMethod);
|
||||
},
|
||||
}));
|
||||
|
||||
if (this.otherEnrolments) {
|
||||
if (this.hasBrowserEnrolments) {
|
||||
buttons.push({
|
||||
text: Translate.instant('core.courses.completeenrolmentbrowser'),
|
||||
handler: (): void => {
|
||||
|
|
|
@ -74,6 +74,8 @@ import { CoreCourseWithImageAndColor } from '@features/courses/services/courses-
|
|||
import { CoreCourseSummaryPage } from '../pages/course-summary/course-summary.page';
|
||||
import { CoreRemindersPushNotificationData } from '@features/reminders/services/reminders';
|
||||
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.
|
||||
|
@ -622,19 +624,16 @@ export class CoreCourseHelperProvider {
|
|||
}
|
||||
|
||||
// Check if guest access is enabled.
|
||||
const enrolmentMethods = await CoreCourses.getCourseEnrolmentMethods(courseId, siteId);
|
||||
|
||||
const method = enrolmentMethods.find((method) => method.type === 'guest');
|
||||
|
||||
if (!method) {
|
||||
const enrolmentMethods = await CoreEnrol.getSupportedCourseEnrolmentMethods(courseId, { type: 'guest', siteId });
|
||||
if (!enrolmentMethods) {
|
||||
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.
|
||||
return {
|
||||
guestAccess: info.status && (!info.passwordrequired || CoreCourses.isValidateGuestAccessPasswordAvailable()),
|
||||
guestAccess: info.status && (!info.passwordrequired || AddonEnrolGuest.isValidateGuestAccessPasswordAvailable()),
|
||||
passwordRequired: info.passwordrequired,
|
||||
};
|
||||
} catch {
|
||||
|
@ -2151,7 +2150,7 @@ export type CoreCoursePrefetchCourseOptions = {
|
|||
sections?: CoreCourseWSSection[]; // List of course sections.
|
||||
courseHandlers?: CoreCourseOptionsHandlerToDisplay[]; // List of course 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 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 admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||
* @returns Promise resolved with array of handlers.
|
||||
|
@ -379,7 +379,7 @@ export class CoreCourseOptionsDelegateService extends CoreDelegate<CoreCourseOpt
|
|||
*
|
||||
* @param course The course object.
|
||||
* @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 admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||
* @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 course The course object.
|
||||
* @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 admOptions Course admin options for current user. See CoreCoursesProvider.getUserAdministrationOptions.
|
||||
* @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
|
||||
Then I should find "Course summary" 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
|
||||
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">
|
||||
<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>
|
||||
</span>
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import { CoreEventCourseStatusChanged, CoreEventObserver, CoreEvents } from '@si
|
|||
import { CoreCourseListItem, CoreCourses, CoreCoursesProvider } from '../../services/courses';
|
||||
import { CoreCoursesHelper, CoreEnrolledCourseDataWithExtraInfoAndOptions } from '../../services/courses-helper';
|
||||
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.
|
||||
|
@ -98,33 +99,7 @@ export class CoreCoursesCourseListItemComponent implements OnInit, OnDestroy, On
|
|||
this.initPrefetchCourse();
|
||||
|
||||
} else if ('enrollmentmethods' in this.course) {
|
||||
this.enrolmentIcons = [];
|
||||
|
||||
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',
|
||||
});
|
||||
}
|
||||
this.enrolmentIcons = await CoreEnrolHelper.getEnrolmentIcons(this.course.enrollmentmethods, this.course.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{
|
||||
"addtofavourites": "Star this course",
|
||||
"allowguests": "This course allows guest users to enter",
|
||||
"aria:coursecategory": "Course category",
|
||||
"aria:coursename": "Course name",
|
||||
"aria:courseprogress": "Course progress:",
|
||||
|
@ -10,7 +9,6 @@
|
|||
"cannotretrievemorecategories": "Categories deeper than level {{$a}} cannot be retrieved.",
|
||||
"categories": "Course categories",
|
||||
"completeenrolmentbrowser": "Complete enrolment in browser",
|
||||
"confirmselfenrol": "Are you sure you want to enrol yourself in this course?",
|
||||
"courses": "Courses",
|
||||
"downloadcourses": "Download all courses",
|
||||
"enrolme": "Enrol me",
|
||||
|
@ -18,7 +16,6 @@
|
|||
"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.",
|
||||
"errorsearching": "An error occurred while searching.",
|
||||
"errorselfenrol": "An error occurred while self enrolling.",
|
||||
"favourite": "Starred course",
|
||||
"filtermycourses": "Filter my courses",
|
||||
"frontpage": "Site home",
|
||||
|
@ -40,7 +37,6 @@
|
|||
"search": "Search",
|
||||
"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.",
|
||||
"selfenrolment": "Self enrolment",
|
||||
"show": "Restore to view",
|
||||
"showonlyenrolled": "Show only my courses",
|
||||
"therearecourses": "There are {{$a}} courses",
|
||||
|
|
|
@ -16,13 +16,15 @@ import { Injectable } from '@angular/core';
|
|||
import { CoreSites, CoreSitesCommonWSOptions, CoreSitesReadingStrategy } from '@services/sites';
|
||||
import { CoreSite, CoreSiteWSPreSets, WSObservable } from '@classes/site';
|
||||
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 { CoreWSError } from '@classes/errors/wserror';
|
||||
import { CoreCourseAnyCourseDataWithExtraInfoAndOptions, CoreCourseWithImageAndColor } from './courses-helper';
|
||||
import { asyncObservable, firstValueFrom, ignoreErrors, zipIncludingComplete } from '@/core/utils/rxjs';
|
||||
import { of } from 'rxjs';
|
||||
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:';
|
||||
|
||||
|
@ -309,33 +311,13 @@ export class CoreCoursesProvider {
|
|||
/**
|
||||
* 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.
|
||||
* @returns Promise resolved with the methods.
|
||||
* @deprecated since 4.3. Use CoreEnrol.getSupportedCourseEnrolmentMethods instead.
|
||||
*/
|
||||
async getCourseEnrolmentMethods(id: number, siteId?: string): Promise<CoreCourseEnrolmentMethod[]> {
|
||||
const site = await CoreSites.getSite(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;
|
||||
async getCourseEnrolmentMethods(courseId: number, siteId?: string): Promise<CoreEnrolEnrolmentMethod[]> {
|
||||
return CoreEnrol.getSupportedCourseEnrolmentMethods(courseId, { siteId });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -344,70 +326,10 @@ export class CoreCoursesProvider {
|
|||
* @param instanceId Guest instance ID.
|
||||
* @param siteId Site ID. If not defined, use current site.
|
||||
* @returns Promise resolved when the info is retrieved.
|
||||
* @deprecated since 4.3 use AddonEnrolGuest.getCourseGuestEnrolmentInfo instead.
|
||||
*/
|
||||
async getCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<CoreCourseEnrolmentGuestMethod> {
|
||||
const site = await CoreSites.getSite(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);
|
||||
async getCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<AddonEnrolGuestInfo> {
|
||||
return AddonEnrolGuest.getGuestEnrolmentInfo(instanceId, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1116,14 +1038,13 @@ export class CoreCoursesProvider {
|
|||
/**
|
||||
* 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.
|
||||
* @returns Promise resolved when the data is invalidated.
|
||||
* @deprecated since 4.3, use CoreEnrol.invalidateCourseEnrolmentMethods instead.
|
||||
*/
|
||||
async invalidateCourseEnrolmentMethods(id: number, siteId?: string): Promise<void> {
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
await site.invalidateWsCacheForKey(this.getCourseEnrolmentMethodsCacheKey(id));
|
||||
async invalidateCourseEnrolmentMethods(courseId: number, siteId?: string): Promise<void> {
|
||||
return CoreEnrol.invalidateCourseEnrolmentMethods(courseId, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1132,11 +1053,10 @@ export class CoreCoursesProvider {
|
|||
* @param instanceId Guest instance ID.
|
||||
* @param siteId Site Id. If not defined, use current site.
|
||||
* @returns Promise resolved when the data is invalidated.
|
||||
* @deprecated since 4.3, use CoreEnrolDelegate.invalidate instead.
|
||||
*/
|
||||
async invalidateCourseGuestEnrolmentInfo(instanceId: number, siteId?: string): Promise<void> {
|
||||
const site = await CoreSites.getSite(siteId);
|
||||
|
||||
await site.invalidateWsCacheForKey(this.getCourseGuestEnrolmentInfoCacheKey(instanceId));
|
||||
return AddonEnrolGuest.invalidateGuestEnrolmentInfo(instanceId, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1270,16 +1190,6 @@ export class CoreCoursesProvider {
|
|||
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.
|
||||
*
|
||||
|
@ -1339,42 +1249,10 @@ export class CoreCoursesProvider {
|
|||
* @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.
|
||||
* @deprecated since 4.3, use CoreEnrolDelegate.enrol instead.
|
||||
*/
|
||||
async selfEnrol(courseId: number, password: string = '', instanceId?: number, siteId?: string): Promise<boolean> {
|
||||
|
||||
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');
|
||||
return AddonEnrolSelf.selfEnrol(courseId, password, instanceId, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1823,50 +1701,19 @@ export type CoreCourseUserAdminOrNavOptionIndexed = {
|
|||
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.
|
||||
*
|
||||
* @deprecated since 4.3 use CoreEnrolEnrolmentInfo instead.
|
||||
*/
|
||||
export type CoreCourseEnrolmentInfo = {
|
||||
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 CoreCourseEnrolmentInfo = CoreEnrolEnrolmentInfo;
|
||||
|
||||
/**
|
||||
* Course enrolment method.
|
||||
*
|
||||
* @deprecated since 4.3 use CoreEnrolEnrolmentMethod instead.
|
||||
*/
|
||||
export type CoreCourseEnrolmentMethod = CoreCourseEnrolmentInfo & {
|
||||
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[];
|
||||
};
|
||||
export type CoreCourseEnrolmentMethod = CoreEnrolEnrolmentMethod;
|
||||
|
||||
/**
|
||||
* Params of core_course_get_recent_courses WS.
|
||||
|
@ -1888,23 +1735,6 @@ export type CoreCourseGetRecentCoursesOptions = CoreSitesCommonWSOptions & {
|
|||
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.
|
||||
*/
|
||||
|
@ -1928,23 +1758,6 @@ export type CoreCourseAnyCourseDataWithOptions = CoreCourseAnyCourseData & {
|
|||
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.
|
||||
*/
|
||||
|
|
|
@ -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 { CoreEditorModule } from './editor/editor.module';
|
||||
import { CoreEmulatorModule } from './emulator/emulator.module';
|
||||
import { CoreEnrolModule } from './enrol/enrol.module';
|
||||
import { CoreFileUploaderModule } from './fileuploader/fileuploader.module';
|
||||
import { CoreFilterModule } from './filter/filter.module';
|
||||
import { CoreGradesModule } from './grades/grades.module';
|
||||
|
@ -53,6 +54,7 @@ import { CoreReportBuilderModule } from './reportbuilder/reportbuilder.module';
|
|||
CoreCourseModule,
|
||||
CoreCoursesModule,
|
||||
CoreEditorModule,
|
||||
CoreEnrolModule,
|
||||
CoreFileUploaderModule,
|
||||
CoreFilterModule,
|
||||
CoreGradesModule,
|
||||
|
|
|
@ -60,6 +60,7 @@ import { CorePlatform } from '@services/platform';
|
|||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
||||
import { CoreLang } from '@services/lang';
|
||||
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.
|
||||
|
@ -1903,6 +1904,8 @@ export class CoreDomUtilsProvider {
|
|||
|
||||
if (modalData === undefined) {
|
||||
throw new CoreCanceledError();
|
||||
} else if (modalData instanceof CoreWSError) {
|
||||
throw modalData;
|
||||
}
|
||||
|
||||
return modalData;
|
||||
|
|
Loading…
Reference in New Issue