From f56b7cfd3c3f949bba4d591d8769c28c53e32f3f Mon Sep 17 00:00:00 2001 From: dpalou <dani@moodle.com> Date: Tue, 9 Oct 2018 08:56:30 +0200 Subject: [PATCH 1/2] MOBILE-2502 course: Support stealth modules as students --- .../components/format/core-course-format.html | 2 +- src/core/course/components/format/format.ts | 38 +++++++++++++++---- .../section-selector/section-selector.html | 2 +- .../section-selector/section-selector.ts | 2 + src/core/course/providers/course.ts | 19 ++++++++-- upgrade.txt | 1 + 6 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/core/course/components/format/core-course-format.html b/src/core/course/components/format/core-course-format.html index c70ec5eb3..58fa13eae 100644 --- a/src/core/course/components/format/core-course-format.html +++ b/src/core/course/components/format/core-course-format.html @@ -69,7 +69,7 @@ <!-- Template to render a section. --> <ng-template #sectionTemplate let-section="section"> - <section ion-list *ngIf="!section.hiddenbynumsections && section.id != allSectionsId"> + <section ion-list *ngIf="!section.hiddenbynumsections && section.id != allSectionsId && section.id != stealthModulesSectionId"> <!-- Title is only displayed when viewing all sections. --> <ion-item-divider text-wrap color="light" *ngIf="selectedSection.id == allSectionsId && section.name"> <core-format-text [text]="section.name"></core-format-text> diff --git a/src/core/course/components/format/format.ts b/src/core/course/components/format/format.ts index 57e16e51a..f084a9e07 100644 --- a/src/core/course/components/format/format.ts +++ b/src/core/course/components/format/format.ts @@ -70,6 +70,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { previousSection: any; nextSection: any; allSectionsId: number = CoreCourseProvider.ALL_SECTIONS_ID; + stealthModulesSectionId: number = CoreCourseProvider.STEALTH_MODULES_SECTION_ID; selectOptions: any = {}; loaded: boolean; @@ -151,13 +152,20 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { const section = this.sections[i]; if ((section.id && section.id == this.initialSectionId) || (section.section && section.section == this.initialSectionNumber)) { - this.loaded = true; - this.sectionChanged(section); + + // Don't load the section if it cannot be viewed by the user. + if (this.canViewSection(section)) { + this.loaded = true; + this.sectionChanged(section); + } break; } } - } else { - // No section specified, get current section. + + } + + if (!this.loaded) { + // No section specified, not found or not visible, get current section. this.cfDelegate.getCurrentSection(this.course, this.sections).then((section) => { this.loaded = true; this.sectionChanged(section); @@ -176,9 +184,12 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { if (!newSection) { // Section not found, calculate which one to use. - newSection = this.cfDelegate.getCurrentSection(this.course, this.sections); + this.cfDelegate.getCurrentSection(this.course, this.sections).then((section) => { + this.sectionChanged(section); + }); + } else { + this.sectionChanged(newSection); } - this.sectionChanged(newSection); } } @@ -262,14 +273,14 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { let j; for (j = i - 1; j >= 1; j--) { - if (this.sections[j].uservisible !== false && !this.sections[j].hiddenbynumsections) { + if (this.canViewSection(this.sections[j])) { break; } } this.previousSection = j >= 1 ? this.sections[j] : null; for (j = i + 1; j < this.sections.length; j++) { - if (this.sections[j].uservisible !== false && !this.sections[j].hiddenbynumsections) { + if (this.canViewSection(this.sections[j])) { break; } } @@ -437,4 +448,15 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy { component.callComponentFunction('ionViewDidLeave'); }); } + + /** + * Check whether a section can be viewed. + * + * @param {any} section The section to check. + * @return {boolean} Whether the section can be viewed. + */ + canViewSection(section: any): boolean { + return section.uservisible !== false && !section.hiddenbynumsections && + section.id != CoreCourseProvider.STEALTH_MODULES_SECTION_ID; + } } diff --git a/src/core/course/pages/section-selector/section-selector.html b/src/core/course/pages/section-selector/section-selector.html index a3076b1a1..6f54622ed 100644 --- a/src/core/course/pages/section-selector/section-selector.html +++ b/src/core/course/pages/section-selector/section-selector.html @@ -10,7 +10,7 @@ </ion-header> <ion-content> <ng-container *ngFor="let section of sections"> - <a ion-item *ngIf="!section.hiddenbynumsections" text-wrap (click)="selectSection(section)" [class.core-primary-item]="selected.id == section.id" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail-none> + <a ion-item *ngIf="!section.hiddenbynumsections && section.id != stealthModulesSectionId" text-wrap (click)="selectSection(section)" [class.core-primary-item]="selected.id == section.id" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail-none> <core-icon name="fa-folder" item-start></core-icon> <h2><core-format-text [text]="section.formattedName || section.name"></core-format-text></h2> <ion-badge color="secondary" *ngIf="section.visible === 0 && section.uservisible !== false">{{ 'core.course.hiddenfromstudents' | translate }}</ion-badge> diff --git a/src/core/course/pages/section-selector/section-selector.ts b/src/core/course/pages/section-selector/section-selector.ts index 82a4241c8..ee504362e 100644 --- a/src/core/course/pages/section-selector/section-selector.ts +++ b/src/core/course/pages/section-selector/section-selector.ts @@ -15,6 +15,7 @@ import { Component } from '@angular/core'; import { IonicPage, NavParams, ViewController } from 'ionic-angular'; import { CoreCourseHelperProvider } from '../../providers/helper'; +import { CoreCourseProvider } from '../../providers/course'; /** * Page that displays course section selector. @@ -26,6 +27,7 @@ import { CoreCourseHelperProvider } from '../../providers/helper'; }) export class CoreCourseSectionSelectorPage { + stealthModulesSectionId = CoreCourseProvider.STEALTH_MODULES_SECTION_ID; sections: any; selected: number; diff --git a/src/core/course/providers/course.ts b/src/core/course/providers/course.ts index 13ab87bd1..0e3570574 100644 --- a/src/core/course/providers/course.ts +++ b/src/core/course/providers/course.ts @@ -29,7 +29,8 @@ import { CoreCourseOfflineProvider } from './course-offline'; */ @Injectable() export class CoreCourseProvider { - static ALL_SECTIONS_ID = -1; + static ALL_SECTIONS_ID = -2; + static STEALTH_MODULES_SECTION_ID = -1; static ACCESS_GUEST = 'courses_access_guest'; static ACCESS_DEFAULT = 'courses_access_default'; @@ -266,9 +267,14 @@ export class CoreCourseProvider { // We have courseId, we can use core_course_get_contents for compatibility. this.logger.debug(`Getting module ${moduleId} in course ${courseId}`); - const params = { + const params: any = { courseid: courseId, - options: [] + options: [ + { + name: 'includestealthmodules', + value: 1 + } + ] }, preSets: any = { omitExpires: preferCache @@ -501,10 +507,11 @@ export class CoreCourseProvider { * @param {boolean} [excludeContents] Do not return module contents (i.e: files inside a resource). * @param {CoreSiteWSPreSets} [preSets] Presets to use. * @param {string} [siteId] Site ID. If not defined, current site. + * @param {boolean} [includeStealthModules] Whether to include stealth modules. Defaults to true. * @return {Promise} The reject contains the error message, else contains the sections. */ getSections(courseId?: number, excludeModules?: boolean, excludeContents?: boolean, preSets?: CoreSiteWSPreSets, - siteId?: string): Promise<any[]> { + siteId?: string, includeStealthModules: boolean = true): Promise<any[]> { return this.sitesProvider.getSite(siteId).then((site) => { preSets = preSets || {}; @@ -521,6 +528,10 @@ export class CoreCourseProvider { { name: 'excludecontents', value: excludeContents ? 1 : 0 + }, + { + name: 'includestealthmodules', + value: includeStealthModules ? 1 : 0 } ] }; diff --git a/upgrade.txt b/upgrade.txt index a4802c6ac..205e5d4c0 100644 --- a/upgrade.txt +++ b/upgrade.txt @@ -5,6 +5,7 @@ information provided here is intended especially for developers. - gulp was updated to v4. In order for gulp to work, you need to install gulp-cli: npm install -g gulp-cli It's also recommended to update ionic cli to v4, otherwise some errors could be raised while building: npm install -g ionic +- The value of the constant CoreCourseProvider.ALL_SECTIONS_ID has changed from -1 to -2. === 3.5.2 === From 98c4eba988028757a2934f89cdb50c47b1307763 Mon Sep 17 00:00:00 2001 From: dpalou <dani@moodle.com> Date: Mon, 15 Oct 2018 11:33:35 +0200 Subject: [PATCH 2/2] MOBILE-2502 course: Don't get sections using cache key --- src/core/course/providers/course.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/core/course/providers/course.ts b/src/core/course/providers/course.ts index 0e3570574..6af2d73f3 100644 --- a/src/core/course/providers/course.ts +++ b/src/core/course/providers/course.ts @@ -516,7 +516,6 @@ export class CoreCourseProvider { return this.sitesProvider.getSite(siteId).then((site) => { preSets = preSets || {}; preSets.cacheKey = this.getSectionsCacheKey(courseId); - preSets.getCacheUsingCacheKey = true; // This is to make sure users don't lose offline access when updating. const params = { courseid: courseId, @@ -536,7 +535,14 @@ export class CoreCourseProvider { ] }; - return site.read('core_course_get_contents', params, preSets).then((sections) => { + return site.read('core_course_get_contents', params, preSets).catch(() => { + // Error getting the data, it could fail because we added a new parameter and the call isn't cached. + // Retry without the new parameter and forcing cache. + preSets.omitExpires = true; + params.options.splice(-1, 1); + + return site.read('core_course_get_contents', params, preSets); + }).then((sections) => { const siteHomeId = site.getSiteHomeId(); let showSections = true;