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;