MOBILE-3814 course: Add helper functions to check stealth and visibility

main
Pau Ferrer Ocaña 2022-03-18 15:00:20 +01:00
parent b4b7743a5a
commit 8a5310ef1e
10 changed files with 70 additions and 23 deletions

View File

@ -21,6 +21,7 @@ import { ContextLevel, CoreConstants } from '@/core/constants';
import { Translate } from '@singletons'; import { Translate } from '@singletons';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { CoreNavigator } from '@services/navigator'; import { CoreNavigator } from '@services/navigator';
import { CoreCourseHelper } from '@features/course/services/course-helper';
/** /**
* Component to render an "activity modules" block. * Component to render an "activity modules" block.
@ -67,7 +68,7 @@ export class AddonBlockActivityModulesComponent extends CoreBlockBaseComponent i
} }
section.modules.forEach((mod) => { section.modules.forEach((mod) => {
if (mod.uservisible === false || !CoreCourse.moduleHasView(mod) || if (!CoreCourseHelper.canUserViewModule(mod, section) || !CoreCourse.moduleHasView(mod) ||
modFullNames[mod.modname] !== undefined) { modFullNames[mod.modname] !== undefined) {
// Ignore this module. // Ignore this module.
return; return;

View File

@ -34,6 +34,7 @@ import {
CoreCourseProvider, CoreCourseProvider,
} from '@features/course/services/course'; } from '@features/course/services/course';
import { import {
CoreCourseHelper,
CoreCourseSection, CoreCourseSection,
} from '@features/course/services/course-helper'; } from '@features/course/services/course-helper';
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate'; import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
@ -444,7 +445,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
await CoreCourseModuleDelegate.getModuleDataFor(module.modname, module, this.course.id); await CoreCourseModuleDelegate.getModuleDataFor(module.modname, module, this.course.id);
} }
if (module.uservisible !== false && module.handlerData?.action) { if (CoreCourseHelper.canUserViewModule(module, section) && module.handlerData?.action) {
module.handlerData.action(data.event, module, module.course); module.handlerData.action(data.event, module, module.course);
} }
@ -574,7 +575,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
continue; continue;
} }
modulesLoaded += sections[i].modules.reduce((total, module) => module.visibleoncoursepage !== 0 ? total + 1 : total, 0); modulesLoaded += sections[i].modules.reduce((total, module) =>
!CoreCourseHelper.isModuleStealth(module, sections[i]) ? total + 1 : total, 0);
if (modulesLoaded >= CoreCourseFormatComponent.LOAD_MORE_ACTIVITIES) { if (modulesLoaded >= CoreCourseFormatComponent.LOAD_MORE_ACTIVITIES) {
break; break;
@ -632,8 +634,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* @return Whether the section can be viewed. * @return Whether the section can be viewed.
*/ */
canViewSection(section: CoreCourseSection): boolean { canViewSection(section: CoreCourseSection): boolean {
return section.uservisible !== false && !section.hiddenbynumsections && return CoreCourseHelper.canUserViewSection(section) && !CoreCourseHelper.isSectionStealth(section);
section.id != CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
} }
} }

View File

@ -18,7 +18,7 @@ import {
CoreCourseModuleCompletionTracking, CoreCourseModuleCompletionTracking,
CoreCourseProvider, CoreCourseProvider,
} from '@features/course/services/course'; } from '@features/course/services/course';
import { CoreCourseSection } from '@features/course/services/course-helper'; import { CoreCourseHelper, CoreCourseSection } from '@features/course/services/course-helper';
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate'; import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
import { CoreCourseAnyCourseData } from '@features/courses/services/courses'; import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
import { IonContent } from '@ionic/angular'; import { IonContent } from '@ionic/angular';
@ -77,11 +77,10 @@ export class CoreCourseCourseIndexComponent implements OnInit {
// Clone sections to add information. // Clone sections to add information.
this.sectionsToRender = this.sections this.sectionsToRender = this.sections
.filter((section) => !section.hiddenbynumsections && .filter((section) => !CoreCourseHelper.isSectionStealth(section))
section.id != CoreCourseProvider.STEALTH_MODULES_SECTION_ID)
.map((section) => { .map((section) => {
const modules = section.modules const modules = section.modules
.filter((module) => module.visibleoncoursepage !== 0 && !module.noviewlink) .filter((module) => !CoreCourseHelper.isModuleStealth(module, section) && !module.noviewlink)
.map((module) => { .map((module) => {
const completionStatus = !completionEnabled || module.completiondata === undefined || const completionStatus = !completionEnabled || module.completiondata === undefined ||
module.completiondata.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE module.completiondata.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE
@ -93,7 +92,7 @@ export class CoreCourseCourseIndexComponent implements OnInit {
name: module.name, name: module.name,
course: module.course, course: module.course,
visible: !!module.visible, visible: !!module.visible,
uservisible: !!module.uservisible, uservisible: CoreCourseHelper.canUserViewModule(module, section),
completionStatus, completionStatus,
}; };
}); });
@ -103,7 +102,7 @@ export class CoreCourseCourseIndexComponent implements OnInit {
name: section.name, name: section.name,
availabilityinfo: !!section.availabilityinfo, availabilityinfo: !!section.availabilityinfo,
visible: !!section.visible, visible: !!section.visible,
uservisible: section.uservisible !== false, uservisible: CoreCourseHelper.canUserViewSection(section),
expanded: section.id === this.selectedId, expanded: section.id === this.selectedId,
highlighted: currentSectionData.section.id === section.id, highlighted: currentSectionData.section.id === section.id,
hasVisibleModules: modules.length > 0, hasVisibleModules: modules.length > 0,

View File

@ -13,8 +13,8 @@
// limitations under the License. // limitations under the License.
import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { CoreCourse, CoreCourseProvider, CoreCourseWSSection } from '@features/course/services/course'; import { CoreCourse, CoreCourseWSSection } from '@features/course/services/course';
import { CoreCourseModuleData } from '@features/course/services/course-helper'; import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
import { IonContent } from '@ionic/angular'; import { IonContent } from '@ionic/angular';
import { CoreNavigationOptions, CoreNavigator } from '@services/navigator'; import { CoreNavigationOptions, CoreNavigator } from '@services/navigator';
@ -187,7 +187,7 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy {
* @return Wether the module is available to the user or not. * @return Wether the module is available to the user or not.
*/ */
protected isSectionAvailable(section: CoreCourseWSSection): boolean { protected isSectionAvailable(section: CoreCourseWSSection): boolean {
return section.uservisible !== false && section.id != CoreCourseProvider.STEALTH_MODULES_SECTION_ID; return CoreCourseHelper.canUserViewSection(section) && !CoreCourseHelper.isSectionStealth(section);
} }
/** /**
@ -223,7 +223,7 @@ export class CoreCourseModuleNavigationComponent implements OnInit, OnDestroy {
animationDirection: next ? 'forward' : 'back', animationDirection: next ? 'forward' : 'back',
}; };
if (module.uservisible === false) { if (!CoreCourseHelper.canUserViewModule(module)) {
const section = next ? this.nextModuleSection : this.previousModuleSection; const section = next ? this.nextModuleSection : this.previousModuleSection;
options.params = { options.params = {
module, module,

View File

@ -19,6 +19,7 @@ import {
CoreCourseModuleData, CoreCourseModuleData,
CoreCourseModuleCompletionData, CoreCourseModuleCompletionData,
CoreCourseSection, CoreCourseSection,
CoreCourseHelper,
} from '@features/course/services/course-helper'; } from '@features/course/services/course-helper';
import { CoreCourse, CoreCourseModuleCompletionStatus, CoreCourseModuleCompletionTracking } from '@features/course/services/course'; import { CoreCourse, CoreCourseModuleCompletionStatus, CoreCourseModuleCompletionTracking } from '@features/course/services/course';
import { CoreCourseModuleDelegate, CoreCourseModuleHandlerButton } from '@features/course/services/module-delegate'; import { CoreCourseModuleDelegate, CoreCourseModuleHandlerButton } from '@features/course/services/module-delegate';
@ -166,7 +167,7 @@ export class CoreCourseModuleComponent implements OnInit, OnDestroy {
* @param event Click event. * @param event Click event.
*/ */
moduleClicked(event: Event): void { moduleClicked(event: Event): void {
if (this.module.uservisible !== false && this.module.handlerData?.action) { if (CoreCourseHelper.canUserViewModule(this.module, this.section) && this.module.handlerData?.action) {
this.module.handlerData.action(event, this.module, this.module.course); this.module.handlerData.action(event, this.module, this.module.course);
} }
} }

View File

@ -75,7 +75,7 @@ export class CoreCourseListModTypePage implements OnInit {
} }
section.modules = section.modules.filter((mod) => { section.modules = section.modules.filter((mod) => {
if (mod.uservisible === false || !CoreCourse.moduleHasView(mod)) { if (!CoreCourseHelper.canUserViewModule(mod, section) || !CoreCourse.moduleHasView(mod)) {
// Ignore this module. // Ignore this module.
return false; return false;
} }

View File

@ -198,7 +198,7 @@ export class CoreCourseHelperProvider {
} }
// Check if the module is stealth. // Check if the module is stealth.
module.isStealth = module.visibleoncoursepage === 0 || (!!module.visible && !section.visible); module.isStealth = CoreCourseHelper.isModuleStealth(module, section);
})); }));
return section; return section;
@ -208,6 +208,50 @@ export class CoreCourseHelperProvider {
return { hasContent, sections: formattedSections }; return { hasContent, sections: formattedSections };
} }
/**
* Module is stealth.
*
* @param module Module to check.
* @param section Section to check.
* @return Wether the module is stealth.
*/
isModuleStealth(module: CoreCourseModuleData, section?: CoreCourseWSSection): boolean {
// visibleoncoursepage can be 1 for teachers when the section is hidden.
return !!module.visible && (!module.visibleoncoursepage || (!!section && !section.visible));
}
/**
* Module is visible by the user.
*
* @param module Module to check.
* @param section Section to check. Omitted if not defined.
* @return Wether the section is visible by the user.
*/
canUserViewModule(module: CoreCourseModuleData, section?: CoreCourseWSSection): boolean {
return module.uservisible !== false && (!section || CoreCourseHelper.canUserViewSection(section));
}
/**
* Section is stealth.
* This should not be true on Moodle 4.0 onwards.
*
* @param section Section to check.
* @return Wether section is stealth (accessible but not visible to students).
*/
isSectionStealth(section: CoreCourseWSSection): boolean {
return section.hiddenbynumsections === 1 || section.id === CoreCourseProvider.STEALTH_MODULES_SECTION_ID;
}
/**
* Section is visible by the user.
*
* @param section Section to check.
* @return Wether the section is visible by the user.
*/
canUserViewSection(section: CoreCourseWSSection): boolean {
return section.uservisible !== false;
}
/** /**
* Calculate completion data of a module. * Calculate completion data of a module.
* *

View File

@ -33,7 +33,7 @@ import { CoreError } from '@classes/errors/error';
import { CoreWSFile, CoreWSExternalWarning } from '@services/ws'; import { CoreWSFile, CoreWSExternalWarning } from '@services/ws';
import { CHECK_UPDATES_TIMES_TABLE, CoreCourseCheckUpdatesDBRecord } from './database/module-prefetch'; import { CHECK_UPDATES_TIMES_TABLE, CoreCourseCheckUpdatesDBRecord } from './database/module-prefetch';
import { CoreFileSizeSum } from '@services/plugin-file-delegate'; import { CoreFileSizeSum } from '@services/plugin-file-delegate';
import { CoreCourseModuleData } from './course-helper'; import { CoreCourseHelper, CoreCourseModuleData } from './course-helper';
const ROOT_CACHE_KEY = 'mmCourse:'; const ROOT_CACHE_KEY = 'mmCourse:';
@ -956,7 +956,7 @@ export class CoreCourseModulePrefetchDelegateService extends CoreDelegate<CoreCo
* @return Promise resolved with true if downloadable, false otherwise. * @return Promise resolved with true if downloadable, false otherwise.
*/ */
async isModuleDownloadable(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> { async isModuleDownloadable(module: CoreCourseAnyModuleData, courseId: number): Promise<boolean> {
if ('uservisible' in module && module.uservisible === false) { if ('uservisible' in module && !CoreCourseHelper.canUserViewModule(module)) {
// Module isn't visible by the user, cannot be downloaded. // Module isn't visible by the user, cannot be downloaded.
return false; return false;
} }

View File

@ -30,6 +30,7 @@ import { makeSingleton } from '@singletons';
import { CoreEvents, CoreEventSiteData } from '@singletons/events'; import { CoreEvents, CoreEventSiteData } from '@singletons/events';
import { CoreLogger } from '@singletons/logger'; import { CoreLogger } from '@singletons/logger';
import { CoreSite } from '@classes/site'; import { CoreSite } from '@classes/site';
import { CoreCourseHelper } from '@features/course/services/course-helper';
/** /**
* Helper service to provide filter functionalities. * Helper service to provide filter functionalities.
@ -159,7 +160,7 @@ export class CoreFilterHelperProvider {
sections.forEach((section) => { sections.forEach((section) => {
if (section.modules) { if (section.modules) {
section.modules.forEach((module) => { section.modules.forEach((module) => {
if (module.uservisible) { if (CoreCourseHelper.canUserViewModule(module, section)) {
contexts.push({ contexts.push({
contextlevel: 'module', contextlevel: 'module',
instanceid: module.id, instanceid: module.id,

View File

@ -16,7 +16,7 @@ import { Type } from '@angular/core';
import { CoreConstants } from '@/core/constants'; import { CoreConstants } from '@/core/constants';
import { CoreCourse } from '@features/course/services/course'; import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleData } from '@features/course/services/course-helper'; import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate'; import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
import { CoreSitePluginsModuleIndexComponent } from '@features/siteplugins/components/module-index/module-index'; import { CoreSitePluginsModuleIndexComponent } from '@features/siteplugins/components/module-index/module-index';
import { import {
@ -105,7 +105,7 @@ export class CoreSitePluginsModuleHandler extends CoreSitePluginsBaseHandler imp
}; };
} }
if (forCoursePage && this.handlerSchema.coursepagemethod && module.visibleoncoursepage !== 0) { if (forCoursePage && this.handlerSchema.coursepagemethod && !CoreCourseHelper.isModuleStealth(module)) {
// Call the method to get the course page template. // Call the method to get the course page template.
const method = this.handlerSchema.coursepagemethod; const method = this.handlerSchema.coursepagemethod;
this.loadCoursePageTemplate(module, courseId, handlerData, method); this.loadCoursePageTemplate(module, courseId, handlerData, method);