MOBILE-3782 course: Fix guest access options and prefetch
parent
9a63793cf2
commit
1512ab72ed
|
@ -82,6 +82,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
|
|||
protected syncObserver?: CoreEventObserver;
|
||||
protected isDestroyed = false;
|
||||
protected modulesHaveCompletion = false;
|
||||
protected isGuest?: boolean;
|
||||
protected debouncedUpdateCachedCompletion?: () => void; // Update the cached completion after a certain time.
|
||||
|
||||
/**
|
||||
|
@ -101,6 +102,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
|
|||
this.sectionId = CoreNavigator.getRouteNumberParam('sectionId');
|
||||
this.sectionNumber = CoreNavigator.getRouteNumberParam('sectionNumber');
|
||||
this.moduleId = CoreNavigator.getRouteNumberParam('moduleId');
|
||||
this.isGuest = CoreNavigator.getRouteBooleanParam('isGuest');
|
||||
|
||||
this.displayEnableDownload = !CoreSites.getCurrentSite()?.isOfflineDisabled() &&
|
||||
CoreCourseFormatDelegate.displayEnableDownload(this.course);
|
||||
|
@ -309,7 +311,7 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async loadMenuHandlers(refresh?: boolean): Promise<void> {
|
||||
this.courseMenuHandlers = await CoreCourseOptionsDelegate.getMenuHandlersToDisplay(this.course, refresh);
|
||||
this.courseMenuHandlers = await CoreCourseOptionsDelegate.getMenuHandlersToDisplay(this.course, refresh, this.isGuest);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -450,9 +452,11 @@ export class CoreCourseContentsPage implements OnInit, OnDestroy {
|
|||
await CoreCourseHelper.confirmAndPrefetchCourse(
|
||||
this.prefetchCourseData,
|
||||
this.course,
|
||||
this.sections,
|
||||
undefined,
|
||||
this.courseMenuHandlers,
|
||||
{
|
||||
sections: this.sections,
|
||||
menuHandlers: this.courseMenuHandlers,
|
||||
isGuest: this.isGuest,
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
if (this.isDestroyed) {
|
||||
|
|
|
@ -48,6 +48,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
|||
protected firstTabName?: string;
|
||||
protected module?: CoreCourseWSModule;
|
||||
protected modParams?: Params;
|
||||
protected isGuest?: boolean;
|
||||
protected contentsTab: CoreTabsOutletTab = {
|
||||
page: CONTENTS_PAGE_NAME,
|
||||
title: 'core.course.contents',
|
||||
|
@ -91,6 +92,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
|||
this.firstTabName = CoreNavigator.getRouteParam('selectedTab');
|
||||
this.module = CoreNavigator.getRouteParam<CoreCourseWSModule>('module');
|
||||
this.modParams = CoreNavigator.getRouteParam<Params>('modParams');
|
||||
this.isGuest = CoreNavigator.getRouteBooleanParam('isGuest');
|
||||
|
||||
this.currentPagePath = CoreNavigator.getCurrentPath();
|
||||
this.contentsTab.page = CoreTextUtils.concatenatePaths(this.currentPagePath, this.contentsTab.page);
|
||||
|
@ -98,6 +100,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
|||
course: this.course,
|
||||
sectionId: CoreNavigator.getRouteNumberParam('sectionId'),
|
||||
sectionNumber: CoreNavigator.getRouteNumberParam('sectionNumber'),
|
||||
isGuest: this.isGuest,
|
||||
};
|
||||
|
||||
if (this.module) {
|
||||
|
@ -132,7 +135,7 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
|
|||
*/
|
||||
protected async loadCourseHandlers(): Promise<void> {
|
||||
// Load the course handlers.
|
||||
const handlers = await CoreCourseOptionsDelegate.getHandlersToDisplay(this.course!, false, false);
|
||||
const handlers = await CoreCourseOptionsDelegate.getHandlersToDisplay(this.course!, false, this.isGuest);
|
||||
|
||||
let tabToLoad: number | undefined;
|
||||
|
||||
|
|
|
@ -67,6 +67,7 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
|||
isMobile: boolean;
|
||||
|
||||
protected isGuestEnabled = false;
|
||||
protected useGuestAccess = false;
|
||||
protected guestInstanceId?: number;
|
||||
protected enrolmentMethods: CoreCourseEnrolmentMethod[] = [];
|
||||
protected waitStart = 0;
|
||||
|
@ -209,10 +210,12 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
|||
this.course!.fullname = course.fullname || this.course!.fullname;
|
||||
this.course!.summary = course.summary || this.course!.summary;
|
||||
this.canAccessCourse = true;
|
||||
this.useGuestAccess = false;
|
||||
} catch {
|
||||
// The user is not an admin/manager. Check if we can provide guest access to the course.
|
||||
try {
|
||||
this.canAccessCourse = !(await this.canAccessAsGuest());
|
||||
this.useGuestAccess = this.canAccessCourse;
|
||||
} catch {
|
||||
this.canAccessCourse = false;
|
||||
}
|
||||
|
@ -243,7 +246,7 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
|||
return;
|
||||
}
|
||||
|
||||
CoreCourseHelper.openCourse(this.course!);
|
||||
CoreCourseHelper.openCourse(this.course!, { isGuest: this.useGuestAccess });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -452,12 +455,16 @@ export class CoreCoursePreviewPage implements OnInit, OnDestroy {
|
|||
/**
|
||||
* Prefetch the course.
|
||||
*/
|
||||
prefetchCourse(): void {
|
||||
CoreCourseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course!).catch((error) => {
|
||||
async prefetchCourse(): Promise<void> {
|
||||
try {
|
||||
await CoreCourseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course!, {
|
||||
isGuest: this.useGuestAccess,
|
||||
});
|
||||
} catch (error) {
|
||||
if (!this.pageDestroyed) {
|
||||
CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -361,17 +361,13 @@ export class CoreCourseHelperProvider {
|
|||
*
|
||||
* @param data An object where to store the course icon and title: "prefetchCourseIcon", "title" and "downloadSucceeded".
|
||||
* @param course Course to prefetch.
|
||||
* @param sections List of course sections.
|
||||
* @param courseHandlers List of course handlers.
|
||||
* @param menuHandlers List of course menu handlers.
|
||||
* @param options Other options.
|
||||
* @return Promise resolved when the download finishes, rejected if an error occurs or the user cancels.
|
||||
*/
|
||||
async confirmAndPrefetchCourse(
|
||||
data: CorePrefetchStatusInfo,
|
||||
course: CoreCourseAnyCourseData,
|
||||
sections?: CoreCourseWSSection[],
|
||||
courseHandlers?: CoreCourseOptionsHandlerToDisplay[],
|
||||
menuHandlers?: CoreCourseOptionsMenuHandlerToDisplay[],
|
||||
options: CoreCoursePrefetchCourseOptions = {},
|
||||
): Promise<void> {
|
||||
const initialIcon = data.icon;
|
||||
const initialStatus = data.status;
|
||||
|
@ -386,23 +382,23 @@ export class CoreCourseHelperProvider {
|
|||
|
||||
try {
|
||||
// Get the sections first if needed.
|
||||
if (!sections) {
|
||||
sections = await CoreCourse.getSections(course.id, false, true);
|
||||
if (!options.sections) {
|
||||
options.sections = await CoreCourse.getSections(course.id, false, true);
|
||||
}
|
||||
|
||||
// Confirm the download.
|
||||
await this.confirmDownloadSizeSection(course.id, undefined, sections, true);
|
||||
await this.confirmDownloadSizeSection(course.id, undefined, options.sections, true);
|
||||
|
||||
// User confirmed, get the course handlers if needed.
|
||||
if (!courseHandlers) {
|
||||
courseHandlers = await CoreCourseOptionsDelegate.getHandlersToDisplay(course);
|
||||
if (!options.courseHandlers) {
|
||||
options.courseHandlers = await CoreCourseOptionsDelegate.getHandlersToDisplay(course, false, options.isGuest);
|
||||
}
|
||||
if (!menuHandlers) {
|
||||
menuHandlers = await CoreCourseOptionsDelegate.getMenuHandlersToDisplay(course);
|
||||
if (!options.menuHandlers) {
|
||||
options.menuHandlers = await CoreCourseOptionsDelegate.getMenuHandlersToDisplay(course, false, options.isGuest);
|
||||
}
|
||||
|
||||
// Now we have all the data, download the course.
|
||||
await this.prefetchCourse(course, sections, courseHandlers, menuHandlers, siteId);
|
||||
await this.prefetchCourse(course, options.sections, options.courseHandlers, options.menuHandlers, siteId);
|
||||
|
||||
// Download successful.
|
||||
data.downloadSucceeded = true;
|
||||
|
@ -422,12 +418,12 @@ export class CoreCourseHelperProvider {
|
|||
* Confirm and prefetches a list of courses.
|
||||
*
|
||||
* @param courses List of courses to download.
|
||||
* @param onProgress Function to call everytime a course is downloaded.
|
||||
* @param options Other options.
|
||||
* @return Resolved when downloaded, rejected if error or canceled.
|
||||
*/
|
||||
async confirmAndPrefetchCourses(
|
||||
courses: CoreEnrolledCourseDataWithExtraInfoAndOptions[],
|
||||
onProgress?: (data: CoreCourseCoursesProgress) => void,
|
||||
options: CoreCourseConfirmPrefetchCoursesOptions = {},
|
||||
): Promise<void> {
|
||||
const siteId = CoreSites.getCurrentSiteId();
|
||||
|
||||
|
@ -437,12 +433,18 @@ export class CoreCourseHelperProvider {
|
|||
const total = courses.length;
|
||||
let count = 0;
|
||||
|
||||
const promises = courses.map((course) => {
|
||||
const promises = courses.map(async (course) => {
|
||||
const subPromises: Promise<void>[] = [];
|
||||
let sections: CoreCourseWSSection[];
|
||||
let handlers: CoreCourseOptionsHandlerToDisplay[] = [];
|
||||
let menuHandlers: CoreCourseOptionsMenuHandlerToDisplay[] = [];
|
||||
let success = true;
|
||||
let isGuest = false;
|
||||
|
||||
if (options.canHaveGuestCourses) {
|
||||
// Check if the user can only access as guest.
|
||||
isGuest = await this.courseUsesGuestAccess(course.id, siteId);
|
||||
}
|
||||
|
||||
// Get the sections and the handlers.
|
||||
subPromises.push(CoreCourse.getSections(course.id, false, true).then((courseSections) => {
|
||||
|
@ -451,12 +453,12 @@ export class CoreCourseHelperProvider {
|
|||
return;
|
||||
}));
|
||||
|
||||
subPromises.push(CoreCourseOptionsDelegate.getHandlersToDisplay(course).then((cHandlers) => {
|
||||
subPromises.push(CoreCourseOptionsDelegate.getHandlersToDisplay(course, false, isGuest).then((cHandlers) => {
|
||||
handlers = cHandlers;
|
||||
|
||||
return;
|
||||
}));
|
||||
subPromises.push(CoreCourseOptionsDelegate.getMenuHandlersToDisplay(course).then((mHandlers) => {
|
||||
subPromises.push(CoreCourseOptionsDelegate.getMenuHandlersToDisplay(course, false, isGuest).then((mHandlers) => {
|
||||
menuHandlers = mHandlers;
|
||||
|
||||
return;
|
||||
|
@ -470,15 +472,15 @@ export class CoreCourseHelperProvider {
|
|||
}).finally(() => {
|
||||
// Course downloaded or failed, notify the progress.
|
||||
count++;
|
||||
if (onProgress) {
|
||||
onProgress({ count: count, total: total, courseId: course.id, success: success });
|
||||
if (options.onProgress) {
|
||||
options.onProgress({ count: count, total: total, courseId: course.id, success: success });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (onProgress) {
|
||||
if (options.onProgress) {
|
||||
// Notify the start of the download.
|
||||
onProgress({ count: 0, total: total, success: true });
|
||||
options.onProgress({ count: 0, total: total, success: true });
|
||||
}
|
||||
|
||||
return CoreUtils.allPromises(promises);
|
||||
|
@ -609,6 +611,55 @@ export class CoreCourseHelperProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a course is accessed using guest access.
|
||||
*
|
||||
* @param courseId Course ID.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with boolean: whether course is accessed using guest access.
|
||||
*/
|
||||
async courseUsesGuestAccess(courseId: number, siteId?: string): Promise<boolean> {
|
||||
try {
|
||||
try {
|
||||
// Check if user is enrolled. If enrolled, no guest access.
|
||||
await CoreCourses.getUserCourse(courseId, false, siteId);
|
||||
|
||||
return false;
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
|
||||
try {
|
||||
// The user is not enrolled in the course. Use getCourses to see if it's an admin/manager and can see the course.
|
||||
await CoreCourses.getCourse(courseId, siteId);
|
||||
|
||||
return false;
|
||||
} catch {
|
||||
// Ignore errors.
|
||||
}
|
||||
|
||||
// Check if guest access is enabled.
|
||||
const enrolmentMethods = await CoreCourses.getCourseEnrolmentMethods(courseId, siteId);
|
||||
|
||||
const method = enrolmentMethods.find((method) => method.type === 'guest');
|
||||
|
||||
if (!method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const info = await CoreCourses.getCourseGuestEnrolmentInfo(method.id);
|
||||
if (!info.status) {
|
||||
// Not active, reject.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't allow guest access if it requires a password.
|
||||
return !info.passwordrequired;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return a section for "All sections".
|
||||
*
|
||||
|
@ -1257,23 +1308,30 @@ export class CoreCourseHelperProvider {
|
|||
*
|
||||
* @param courses Courses array to prefetch.
|
||||
* @param prefetch Prefetch information to be updated.
|
||||
* @param options Other options.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
async prefetchCourses(
|
||||
courses: CoreEnrolledCourseDataWithExtraInfoAndOptions[],
|
||||
prefetch: CorePrefetchStatusInfo,
|
||||
options: CoreCoursePrefetchCoursesOptions = {},
|
||||
): Promise<void> {
|
||||
prefetch.loading = true;
|
||||
prefetch.icon = CoreConstants.ICON_DOWNLOADING;
|
||||
prefetch.badge = '';
|
||||
|
||||
try {
|
||||
await this.confirmAndPrefetchCourses(courses, (progress) => {
|
||||
const prefetchOptions = {
|
||||
...options,
|
||||
onProgress: (progress) => {
|
||||
prefetch.badge = progress.count + ' / ' + progress.total;
|
||||
prefetch.badgeA11yText = Translate.instant('core.course.downloadcoursesprogressdescription', progress);
|
||||
prefetch.count = progress.count;
|
||||
prefetch.total = progress.total;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
await this.confirmAndPrefetchCourses(courses, prefetchOptions);
|
||||
prefetch.icon = CoreConstants.ICON_OUTDATED;
|
||||
} finally {
|
||||
prefetch.loading = false;
|
||||
|
@ -2059,6 +2117,30 @@ export type CoreCourseModuleCompletionData = CoreCourseModuleWSCompletionData &
|
|||
offline?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for prefetch course function.
|
||||
*/
|
||||
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.
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for prefetch courses function.
|
||||
*/
|
||||
export type CoreCoursePrefetchCoursesOptions = {
|
||||
canHaveGuestCourses?: boolean; // Whether the list of courses can contain courses with only guest access.
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for confirm and prefetch courses function.
|
||||
*/
|
||||
export type CoreCourseConfirmPrefetchCoursesOptions = CoreCoursePrefetchCoursesOptions & {
|
||||
onProgress?: (data: CoreCourseCoursesProgress) => void;
|
||||
};
|
||||
|
||||
type ComponentWithContextMenu = {
|
||||
prefetchStatusIcon?: string;
|
||||
isDestroyed?: boolean;
|
||||
|
|
|
@ -185,13 +185,13 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy {
|
|||
this.downloadAllCoursesLoading = true;
|
||||
|
||||
try {
|
||||
await CoreCourseHelper.confirmAndPrefetchCourses(this.courses, (progress) => {
|
||||
await CoreCourseHelper.confirmAndPrefetchCourses(this.courses, { onProgress: (progress) => {
|
||||
this.downloadAllCoursesBadge = progress.count + ' / ' + progress.total;
|
||||
this.downloadAllCoursesBadgeA11yText =
|
||||
Translate.instant('core.course.downloadcoursesprogressdescription', progress);
|
||||
this.downloadAllCoursesCount = progress.count;
|
||||
this.downloadAllCoursesTotal = progress.total;
|
||||
});
|
||||
} });
|
||||
} catch (error) {
|
||||
if (!this.isDestroyed) {
|
||||
CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
This files describes API changes in the Moodle Mobile app,
|
||||
information provided here is intended especially for developers.
|
||||
|
||||
=== 3.9.6 ===
|
||||
|
||||
- The parameters of the functions confirmAndPrefetchCourse and confirmAndPrefetchCourses have changed, they now accept an object with options.
|
||||
|
||||
=== 3.9.5 ===
|
||||
|
||||
- Several functions inside AddonNotificationsProvider have been modified to accept an "options" parameter instead of having several optional parameters.
|
||||
|
|
Loading…
Reference in New Issue