2
0
Fork 0

Merge pull request #1977 from dpalou/MOBILE-2735

Mobile 2735
main
Juan Leyva 2019-07-09 14:45:07 +02:00 committed by GitHub
commit 714d8d4c7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 328 additions and 99 deletions

View File

@ -1260,6 +1260,8 @@
"core.completion-alt-manual-y-override": "completion", "core.completion-alt-manual-y-override": "completion",
"core.confirmcanceledit": "local_moodlemobileapp", "core.confirmcanceledit": "local_moodlemobileapp",
"core.confirmdeletefile": "repository", "core.confirmdeletefile": "repository",
"core.confirmgotabroot": "local_moodlemobileapp",
"core.confirmgotabrootdefault": "local_moodlemobileapp",
"core.confirmloss": "local_moodlemobileapp", "core.confirmloss": "local_moodlemobileapp",
"core.confirmopeninbrowser": "local_moodlemobileapp", "core.confirmopeninbrowser": "local_moodlemobileapp",
"core.considereddigitalminor": "moodle", "core.considereddigitalminor": "moodle",

View File

@ -15,7 +15,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { AddonBadgesProvider } from './badges'; import { AddonBadgesProvider } from './badges';
/** /**
@ -26,7 +26,7 @@ export class AddonBadgesBadgeLinkHandler extends CoreContentLinksHandlerBase {
name = 'AddonBadgesBadgeLinkHandler'; name = 'AddonBadgesBadgeLinkHandler';
pattern = /\/badges\/badge\.php.*([\?\&]hash=)/; pattern = /\/badges\/badge\.php.*([\?\&]hash=)/;
constructor(private badgesProvider: AddonBadgesProvider, private loginHelper: CoreLoginHelperProvider) { constructor(private badgesProvider: AddonBadgesProvider, private linkHelper: CoreContentLinksHelperProvider) {
super(); super();
} }
@ -44,8 +44,7 @@ export class AddonBadgesBadgeLinkHandler extends CoreContentLinksHandlerBase {
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
// Always use redirect to make it the new history root (to avoid "loops" in history). this.linkHelper.goInSite(navCtrl, 'AddonBadgesIssuedBadgePage', {courseId: 0, badgeHash: params.hash}, siteId);
this.loginHelper.redirect('AddonBadgesIssuedBadgePage', {courseId: 0, badgeHash: params.hash}, siteId);
} }
}]; }];
} }

View File

@ -15,7 +15,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { AddonBadgesProvider } from './badges'; import { AddonBadgesProvider } from './badges';
/** /**
@ -27,7 +27,7 @@ export class AddonBadgesMyBadgesLinkHandler extends CoreContentLinksHandlerBase
featureName = 'CoreUserDelegate_AddonBadges'; featureName = 'CoreUserDelegate_AddonBadges';
pattern = /\/badges\/mybadges\.php/; pattern = /\/badges\/mybadges\.php/;
constructor(private badgesProvider: AddonBadgesProvider, private loginHelper: CoreLoginHelperProvider) { constructor(private badgesProvider: AddonBadgesProvider, private linkHelper: CoreContentLinksHelperProvider) {
super(); super();
} }
@ -45,8 +45,7 @@ export class AddonBadgesMyBadgesLinkHandler extends CoreContentLinksHandlerBase
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
// Always use redirect to make it the new history root (to avoid "loops" in history). this.linkHelper.goInSite(navCtrl, 'AddonBadgesUserBadgesPage', {}, siteId);
this.loginHelper.redirect('AddonBadgesUserBadgesPage', {}, siteId);
} }
}]; }];
} }

View File

@ -15,7 +15,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { AddonBlogProvider } from './blog'; import { AddonBlogProvider } from './blog';
/** /**
@ -27,7 +27,7 @@ export class AddonBlogIndexLinkHandler extends CoreContentLinksHandlerBase {
featureName = 'CoreUserDelegate_AddonBlog:blogs'; featureName = 'CoreUserDelegate_AddonBlog:blogs';
pattern = /\/blog\/index\.php/; pattern = /\/blog\/index\.php/;
constructor(private blogProvider: AddonBlogProvider, private loginHelper: CoreLoginHelperProvider) { constructor(private blogProvider: AddonBlogProvider, private linkHelper: CoreContentLinksHelperProvider) {
super(); super();
} }
@ -53,8 +53,7 @@ export class AddonBlogIndexLinkHandler extends CoreContentLinksHandlerBase {
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
// Always use redirect to make it the new history root (to avoid "loops" in history). this.linkHelper.goInSite(navCtrl, 'AddonBlogEntriesPage', pageParams, siteId, !Object.keys(pageParams).length);
this.loginHelper.redirect('AddonBlogEntriesPage', pageParams, siteId);
} }
}]; }];
} }

View File

@ -63,7 +63,7 @@ export class AddonBlogUserHandler implements CoreUserProfileHandler {
action: (event, navCtrl, user, courseId): void => { action: (event, navCtrl, user, courseId): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'AddonBlogEntriesPage', { userId: user.id, courseId: courseId }); this.linkHelper.goInSite(navCtrl, 'AddonBlogEntriesPage', { userId: user.id, courseId: courseId });
} }
}; };

View File

@ -15,7 +15,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { AddonCompetencyProvider } from './competency'; import { AddonCompetencyProvider } from './competency';
/** /**
@ -26,7 +26,7 @@ export class AddonCompetencyPlansLinkHandler extends CoreContentLinksHandlerBase
name = 'AddonCompetencyPlansLinkHandler'; name = 'AddonCompetencyPlansLinkHandler';
pattern = /\/admin\/tool\/lp\/plans\.php/; pattern = /\/admin\/tool\/lp\/plans\.php/;
constructor(private loginHelper: CoreLoginHelperProvider, private competencyProvider: AddonCompetencyProvider) { constructor(private linkHelper: CoreContentLinksHelperProvider, private competencyProvider: AddonCompetencyProvider) {
super(); super();
} }
@ -44,8 +44,8 @@ export class AddonCompetencyPlansLinkHandler extends CoreContentLinksHandlerBase
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
// Always use redirect to make it the new history root (to avoid "loops" in history). this.linkHelper.goInSite(navCtrl, 'AddonCompetencyPlanListPage', { userId: params.userid }, siteId,
this.loginHelper.redirect('AddonCompetencyPlanListPage', { userId: params.userid }, siteId); typeof params.userid == 'undefined');
} }
}]; }];
} }

View File

@ -111,7 +111,7 @@ export class AddonCompetencyUserHandler implements CoreUserProfileHandler {
action: (event, navCtrl, user, courseId): void => { action: (event, navCtrl, user, courseId): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'AddonCompetencyCourseCompetenciesPage', {courseId, userId: user.id}); this.linkHelper.goInSite(navCtrl, 'AddonCompetencyCourseCompetenciesPage', {courseId, userId: user.id});
} }
}; };
@ -123,7 +123,7 @@ export class AddonCompetencyUserHandler implements CoreUserProfileHandler {
action: (event, navCtrl, user, courseId): void => { action: (event, navCtrl, user, courseId): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'AddonCompetencyPlanListPage', {userId: user.id}); this.linkHelper.goInSite(navCtrl, 'AddonCompetencyPlanListPage', {userId: user.id});
} }
}; };

View File

@ -43,7 +43,6 @@ export class AddonMessagesContactRequestLinkHandler extends CoreContentLinksHand
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'AddonMessagesContactsPage', {}, siteId); this.linkHelper.goInSite(navCtrl, 'AddonMessagesContactsPage', {}, siteId);
} }
}]; }];

View File

@ -49,7 +49,6 @@ export class AddonMessagesDiscussionLinkHandler extends CoreContentLinksHandlerB
const stateParams = { const stateParams = {
userId: parseInt(params.id || params.user2, 10) userId: parseInt(params.id || params.user2, 10)
}; };
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'AddonMessagesDiscussionPage', stateParams, siteId); this.linkHelper.goInSite(navCtrl, 'AddonMessagesDiscussionPage', stateParams, siteId);
} }
}]; }];

View File

@ -49,7 +49,6 @@ export class AddonMessagesIndexLinkHandler extends CoreContentLinksHandlerBase {
pageName = 'AddonMessagesGroupConversationsPage'; pageName = 'AddonMessagesGroupConversationsPage';
} }
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, pageName, undefined, siteId); this.linkHelper.goInSite(navCtrl, pageName, undefined, siteId);
} }
}]; }];

View File

@ -76,7 +76,6 @@ export class AddonMessagesSendMessageUserHandler implements CoreUserProfileHandl
showKeyboard: true, showKeyboard: true,
userId: user.id userId: user.id
}; };
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'AddonMessagesDiscussionPage', pageParams); this.linkHelper.goInSite(navCtrl, 'AddonMessagesDiscussionPage', pageParams);
} }
}; };

View File

@ -46,7 +46,7 @@ export class AddonModBookLinkHandler extends CoreContentLinksModuleIndexHandler
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId, undefined, this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId, undefined,
this.useModNameToGetModule ? this.modName : undefined, modParams); this.useModNameToGetModule ? this.modName : undefined, modParams, navCtrl);
} }
}]; }];
} }

View File

@ -70,7 +70,7 @@ export class AddonModLessonGradeLinkHandler extends CoreContentLinksModuleGradeH
this.linkHelper.goInSite(navCtrl, 'AddonModLessonUserRetakePage', pageParams, siteId); this.linkHelper.goInSite(navCtrl, 'AddonModLessonUserRetakePage', pageParams, siteId);
} else { } else {
// User cannot view the report, go to lesson index. // User cannot view the report, go to lesson index.
this.courseHelper.navigateToModule(moduleId, siteId, courseId, module.section); this.courseHelper.navigateToModule(moduleId, siteId, courseId, module.section, undefined, undefined, navCtrl);
} }
}).catch((error) => { }).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true); this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);

View File

@ -19,6 +19,7 @@ import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseProvider } from '@core/course/providers/course';
import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseHelperProvider } from '@core/course/providers/helper';
import { AddonModLessonProvider } from './lesson'; import { AddonModLessonProvider } from './lesson';
import { NavController } from 'ionic-angular';
/** /**
* Handler to treat links to lesson index. * Handler to treat links to lesson index.
@ -51,9 +52,10 @@ export class AddonModLessonIndexLinkHandler extends CoreContentLinksModuleIndexH
/* Ignore the pageid param. If we open the lesson player with a certain page and the user hasn't started /* Ignore the pageid param. If we open the lesson player with a certain page and the user hasn't started
the lesson, an error is thrown: could not find lesson_timer records. */ the lesson, an error is thrown: could not find lesson_timer records. */
if (params.userpassword) { if (params.userpassword) {
this.navigateToModuleWithPassword(parseInt(params.id, 10), courseId, params.userpassword, siteId); this.navigateToModuleWithPassword(parseInt(params.id, 10), courseId, params.userpassword, siteId, navCtrl);
} else { } else {
this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId); this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId,
undefined, undefined, undefined, navCtrl);
} }
} }
}]; }];
@ -80,9 +82,11 @@ export class AddonModLessonIndexLinkHandler extends CoreContentLinksModuleIndexH
* @param {number} courseId Course ID. * @param {number} courseId Course ID.
* @param {string} password Password. * @param {string} password Password.
* @param {string} siteId Site ID. * @param {string} siteId Site ID.
* @param {NavController} navCtrl Navigation controller.
* @return {Promise<any>} Promise resolved when navigated. * @return {Promise<any>} Promise resolved when navigated.
*/ */
protected navigateToModuleWithPassword(moduleId: number, courseId: number, password: string, siteId: string): Promise<any> { protected navigateToModuleWithPassword(moduleId: number, courseId: number, password: string, siteId: string,
navCtrl?: NavController): Promise<any> {
const modal = this.domUtils.showModalLoading(); const modal = this.domUtils.showModalLoading();
// Get the module. // Get the module.
@ -93,11 +97,12 @@ export class AddonModLessonIndexLinkHandler extends CoreContentLinksModuleIndexH
return this.lessonProvider.storePassword(parseInt(module.instance, 10), password, siteId).catch(() => { return this.lessonProvider.storePassword(parseInt(module.instance, 10), password, siteId).catch(() => {
// Ignore errors. // Ignore errors.
}).then(() => { }).then(() => {
return this.courseHelper.navigateToModule(moduleId, siteId, courseId, module.section); return this.courseHelper.navigateToModule(moduleId, siteId, courseId, module.section,
undefined, undefined, navCtrl);
}); });
}).catch(() => { }).catch(() => {
// Error, go to index page. // Error, go to index page.
return this.courseHelper.navigateToModule(moduleId, siteId, courseId); return this.courseHelper.navigateToModule(moduleId, siteId, courseId, undefined, undefined, undefined, navCtrl);
}).finally(() => { }).finally(() => {
modal.dismiss(); modal.dismiss();
}); });

View File

@ -99,7 +99,7 @@ export class AddonNotesUserHandler implements CoreUserProfileHandler {
action: (event, navCtrl, user, courseId): void => { action: (event, navCtrl, user, courseId): void => {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'AddonNotesListPage', { userId: user.id, courseId: courseId }); this.linkHelper.goInSite(navCtrl, 'AddonNotesListPage', { userId: user.id, courseId: courseId });
} }
}; };

View File

@ -1260,6 +1260,8 @@
"core.completion-alt-manual-y-override": "Completed: {{$a.modname}} (set by {{$a.overrideuser}}). Select to mark as not complete.", "core.completion-alt-manual-y-override": "Completed: {{$a.modname}} (set by {{$a.overrideuser}}). Select to mark as not complete.",
"core.confirmcanceledit": "Are you sure you want to leave this page? All changes will be lost.", "core.confirmcanceledit": "Are you sure you want to leave this page? All changes will be lost.",
"core.confirmdeletefile": "Are you sure you want to delete this file?", "core.confirmdeletefile": "Are you sure you want to delete this file?",
"core.confirmgotabroot": "Are you sure you want to go back to {{name}}?",
"core.confirmgotabrootdefault": "Are you sure you want to go to the initial page of the current tab?",
"core.confirmloss": "Are you sure? All changes will be lost.", "core.confirmloss": "Are you sure? All changes will be lost.",
"core.confirmopeninbrowser": "Do you want to open it in a web browser?", "core.confirmopeninbrowser": "Do you want to open it in a web browser?",
"core.considereddigitalminor": "You are too young to create an account on this site.", "core.considereddigitalminor": "You are too young to create an account on this site.",

View File

@ -1,5 +1,5 @@
<div class="tabbar" role="tablist" #tabbar [hidden]="hidden"> <div class="tabbar" role="tablist" #tabbar [hidden]="hidden">
<a [hidden]="_loaded === false" *ngFor="let t of _tabs" [tab]="t" class="tab-button" role="tab" href="#" (ionSelect)="select(t)" [attr.aria-hidden]="!t.show" [attr.aria-label]="t.tabTitle || ''" [title]="t.tabTitle || ''"></a> <a [hidden]="_loaded === false" *ngFor="let t of _tabs" [tab]="t" class="tab-button" role="tab" href="#" (ionSelect)="select(t, undefined, undefined, true)" [attr.aria-hidden]="!t.show" [attr.aria-label]="t.tabTitle || ''" [title]="t.tabTitle || ''"></a>
<div class="tab-highlight"></div> <div class="tab-highlight"></div>
<div *ngIf="_loaded === false" class="core-ion-tabs-loading"> <div *ngIf="_loaded === false" class="core-ion-tabs-loading">
<span class="core-ion-tabs-loading-spinner"> <span class="core-ion-tabs-loading-spinner">

View File

@ -20,11 +20,14 @@ import {
import { CoreIonTabComponent } from './ion-tab'; import { CoreIonTabComponent } from './ion-tab';
import { CoreUtilsProvider, PromiseDefer } from '@providers/utils/utils'; import { CoreUtilsProvider, PromiseDefer } from '@providers/utils/utils';
import { CoreAppProvider } from '@providers/app'; import { CoreAppProvider } from '@providers/app';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { TranslateService } from '@ngx-translate/core';
/** /**
* Equivalent to ion-tabs. It has 2 improvements: * Equivalent to ion-tabs. It has several improvements:
* - If a core-ion-tab is added or removed, it will be reflected in the tab bar in the right position. * - If a core-ion-tab is added or removed, it will be reflected in the tab bar in the right position.
* - It supports a loaded input to tell when are the tabs ready. * - It supports a loaded input to tell when are the tabs ready.
* - When the user clicks the tab again to go to root, a confirm modal is shown.
*/ */
@Component({ @Component({
selector: 'core-ion-tabs', selector: 'core-ion-tabs',
@ -73,7 +76,8 @@ export class CoreIonTabsComponent extends Tabs implements OnDestroy {
constructor(protected utils: CoreUtilsProvider, protected appProvider: CoreAppProvider, @Optional() parent: NavController, constructor(protected utils: CoreUtilsProvider, protected appProvider: CoreAppProvider, @Optional() parent: NavController,
@Optional() viewCtrl: ViewController, _app: App, config: Config, elementRef: ElementRef, _plt: Platform, @Optional() viewCtrl: ViewController, _app: App, config: Config, elementRef: ElementRef, _plt: Platform,
renderer: Renderer, _linker: DeepLinker, keyboard?: Keyboard) { renderer: Renderer, _linker: DeepLinker, protected domUtils: CoreDomUtilsProvider,
protected translate: TranslateService, keyboard?: Keyboard) {
super(parent, viewCtrl, _app, config, elementRef, _plt, renderer, _linker, keyboard); super(parent, viewCtrl, _app, config, elementRef, _plt, renderer, _linker, keyboard);
} }
@ -272,13 +276,25 @@ export class CoreIonTabsComponent extends Tabs implements OnDestroy {
* *
* @param {number|Tab} tabOrIndex Index, or the Tab instance, of the tab to select. * @param {number|Tab} tabOrIndex Index, or the Tab instance, of the tab to select.
* @param {NavOptions} Nav options. * @param {NavOptions} Nav options.
* @param {boolean} [fromUrl=true] Whether to load from a URL. * @param {boolean} [fromUrl] Whether to load from a URL.
* @param {boolean} [manualClick] Whether the user manually clicked the tab.
* @return {Promise<any>} Promise resolved when selected. * @return {Promise<any>} Promise resolved when selected.
*/ */
select(tabOrIndex: number | Tab, opts: NavOptions = {}, fromUrl: boolean = false): Promise<any> { select(tabOrIndex: number | Tab, opts: NavOptions = {}, fromUrl?: boolean, manualClick?: boolean): Promise<any> {
if (this.initialized) { if (this.initialized) {
// Tabs have been initialized, select the tab. // Tabs have been initialized, select the tab.
if (manualClick) {
// If we'll go to the root of the current tab, ask the user to confirm first.
const tab = typeof tabOrIndex == 'number' ? this.getByIndex(tabOrIndex) : tabOrIndex;
return this.confirmGoToRoot(tab).then(() => {
return super.select(tabOrIndex, opts, fromUrl);
}, () => {
// User cancelled.
});
}
return super.select(tabOrIndex, opts, fromUrl); return super.select(tabOrIndex, opts, fromUrl);
} else { } else {
// Tabs not initialized yet. Mark it as "selectedIndex" input so it's treated when the tabs are initialized. // Tabs not initialized yet. Mark it as "selectedIndex" input so it's treated when the tabs are initialized.
@ -305,11 +321,16 @@ export class CoreIonTabsComponent extends Tabs implements OnDestroy {
if (this.initialized) { if (this.initialized) {
const tab = this.getByIndex(index); const tab = this.getByIndex(index);
if (tab) { if (tab) {
return tab.goToRoot({animate: false, updateUrl: true, isNavRoot: true}).then(() => { return this.confirmGoToRoot(tab).then(() => {
// Tab not previously selected. Select it after going to root. // User confirmed, go to root.
if (!tab.isSelected) { return tab.goToRoot({animate: tab.isSelected, updateUrl: true, isNavRoot: true}).then(() => {
return this.select(tab, {animate: false, updateUrl: true, isNavRoot: true}); // Tab not previously selected. Select it after going to root.
} if (!tab.isSelected) {
return this.select(tab, {animate: false, updateUrl: true, isNavRoot: true});
}
});
}, () => {
// User cancelled.
}); });
} }
@ -349,4 +370,23 @@ export class CoreIonTabsComponent extends Tabs implements OnDestroy {
// Unregister the custom back button action for this page // Unregister the custom back button action for this page
this.unregisterBackButtonAction && this.unregisterBackButtonAction(); this.unregisterBackButtonAction && this.unregisterBackButtonAction();
} }
/**
* Confirm if the user wants to go to the root of the current tab.
*
* @param {Tab} tab Tab to go to root.
* @return {Promise<any>} Promise resolved when confirmed.
*/
confirmGoToRoot(tab: Tab): Promise<any> {
if (!tab || !tab.isSelected || (tab.getActive() && tab.getActive().isFirst())) {
// Tab not selected or is already at root, no need to confirm.
return Promise.resolve();
} else {
if (tab.tabTitle) {
return this.domUtils.showConfirm(this.translate.instant('core.confirmgotabroot', {name: tab.tabTitle}));
} else {
return this.domUtils.showConfirm(this.translate.instant('core.confirmgotabrootdefault'));
}
}
}
} }

View File

@ -77,7 +77,7 @@ export class CoreContentLinksModuleGradeHandler extends CoreContentLinksHandlerB
if (!params.userid || params.userid == site.getUserId()) { if (!params.userid || params.userid == site.getUserId()) {
// No user specified or current user. Navigate to module. // No user specified or current user. Navigate to module.
this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId, undefined, this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId, undefined,
this.useModNameToGetModule ? this.modName : undefined); this.useModNameToGetModule ? this.modName : undefined, undefined, navCtrl);
} else if (this.canReview) { } else if (this.canReview) {
// Use the goToReview function. // Use the goToReview function.
this.goToReview(url, params, courseId, siteId, navCtrl); this.goToReview(url, params, courseId, siteId, navCtrl);

View File

@ -60,7 +60,7 @@ export class CoreContentLinksModuleIndexHandler extends CoreContentLinksHandlerB
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId, undefined, this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId, undefined,
this.useModNameToGetModule ? this.modName : undefined); this.useModNameToGetModule ? this.modName : undefined, undefined, navCtrl);
} }
}]; }];
} }

View File

@ -63,7 +63,6 @@ export class CoreContentLinksModuleListHandler extends CoreContentLinksHandlerBa
title: this.title || this.translate.instant('addon.mod_' + this.modName + '.modulenameplural') title: this.title || this.translate.instant('addon.mod_' + this.modName + '.modulenameplural')
}; };
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'CoreCourseListModTypePage', stateParams, siteId); this.linkHelper.goInSite(navCtrl, 'CoreCourseListModTypePage', stateParams, siteId);
} }
}]; }];

View File

@ -30,6 +30,7 @@ import { CoreConstants } from '@core/constants';
import { CoreConfigConstants } from '../../../configconstants'; import { CoreConfigConstants } from '../../../configconstants';
import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins'; import { CoreSitePluginsProvider } from '@core/siteplugins/providers/siteplugins';
import { CoreSite } from '@classes/site'; import { CoreSite } from '@classes/site';
import { CoreMainMenuProvider } from '@core/mainmenu/providers/mainmenu';
/** /**
* Service that provides some features regarding content links. * Service that provides some features regarding content links.
@ -42,7 +43,8 @@ export class CoreContentLinksHelperProvider {
private contentLinksDelegate: CoreContentLinksDelegate, private appProvider: CoreAppProvider, private contentLinksDelegate: CoreContentLinksDelegate, private appProvider: CoreAppProvider,
private domUtils: CoreDomUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private translate: TranslateService, private domUtils: CoreDomUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private translate: TranslateService,
private initDelegate: CoreInitDelegate, eventsProvider: CoreEventsProvider, private textUtils: CoreTextUtilsProvider, private initDelegate: CoreInitDelegate, eventsProvider: CoreEventsProvider, private textUtils: CoreTextUtilsProvider,
private sitePluginsProvider: CoreSitePluginsProvider, private zone: NgZone, private utils: CoreUtilsProvider) { private sitePluginsProvider: CoreSitePluginsProvider, private zone: NgZone, private utils: CoreUtilsProvider,
private mainMenuProvider: CoreMainMenuProvider) {
this.logger = logger.getInstance('CoreContentLinksHelperProvider'); this.logger = logger.getInstance('CoreContentLinksHelperProvider');
} }
@ -103,9 +105,10 @@ export class CoreContentLinksHelperProvider {
* @param {string} pageName Name of the page to go. * @param {string} pageName Name of the page to go.
* @param {any} [pageParams] Params to send to the page. * @param {any} [pageParams] Params to send to the page.
* @param {string} [siteId] Site ID. If not defined, current site. * @param {string} [siteId] Site ID. If not defined, current site.
* @param {boolean} [checkMenu] If true, check if the root page of a main menu tab. Only the page name will be checked.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
goInSite(navCtrl: NavController, pageName: string, pageParams: any, siteId?: string): Promise<any> { goInSite(navCtrl: NavController, pageName: string, pageParams: any, siteId?: string, checkMenu?: boolean): Promise<any> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
const deferred = this.utils.promiseDefer(); const deferred = this.utils.promiseDefer();
@ -113,7 +116,23 @@ export class CoreContentLinksHelperProvider {
// Execute the code in the Angular zone, so change detection doesn't stop working. // Execute the code in the Angular zone, so change detection doesn't stop working.
this.zone.run(() => { this.zone.run(() => {
if (navCtrl && siteId == this.sitesProvider.getCurrentSiteId()) { if (navCtrl && siteId == this.sitesProvider.getCurrentSiteId()) {
navCtrl.push(pageName, pageParams).then(deferred.resolve, deferred.reject); if (checkMenu) {
// Check if the page is in the main menu.
this.mainMenuProvider.isCurrentMainMenuHandler(pageName, pageParams).catch(() => {
return false; // Shouldn't happen.
}).then((isInMenu) => {
if (isInMenu) {
// Just select the tab.
this.loginHelper.loadPageInMainMenu(pageName, pageParams);
deferred.resolve();
} else {
navCtrl.push(pageName, pageParams).then(deferred.resolve, deferred.reject);
}
});
} else {
navCtrl.push(pageName, pageParams).then(deferred.resolve, deferred.reject);
}
} else { } else {
this.loginHelper.redirect(pageName, pageParams, siteId).then(deferred.resolve, deferred.reject); this.loginHelper.redirect(pageName, pageParams, siteId).then(deferred.resolve, deferred.reject);
} }

View File

@ -245,7 +245,6 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
* @param {any} event Event. * @param {any} event Event.
*/ */
gotoBlog(event: any): void { gotoBlog(event: any): void {
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(this.navCtrl, 'AddonBlogEntriesPage', { cmId: this.module.id }); this.linkHelper.goInSite(this.navCtrl, 'AddonBlogEntriesPage', { cmId: this.module.id });
} }

View File

@ -76,12 +76,14 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
loaded: boolean; loaded: boolean;
protected sectionStatusObserver; protected sectionStatusObserver;
protected selectTabObserver;
protected lastCourseFormat: string; protected lastCourseFormat: string;
constructor(private cfDelegate: CoreCourseFormatDelegate, translate: TranslateService, private injector: Injector, constructor(private cfDelegate: CoreCourseFormatDelegate, translate: TranslateService, private injector: Injector,
private courseHelper: CoreCourseHelperProvider, private domUtils: CoreDomUtilsProvider, private courseHelper: CoreCourseHelperProvider, private domUtils: CoreDomUtilsProvider,
eventsProvider: CoreEventsProvider, private sitesProvider: CoreSitesProvider, private content: Content, eventsProvider: CoreEventsProvider, private sitesProvider: CoreSitesProvider, private content: Content,
prefetchDelegate: CoreCourseModulePrefetchDelegate, private modalCtrl: ModalController) { prefetchDelegate: CoreCourseModulePrefetchDelegate, private modalCtrl: ModalController,
private courseProvider: CoreCourseProvider) {
this.selectOptions.title = translate.instant('core.course.sections'); this.selectOptions.title = translate.instant('core.course.sections');
this.completionChanged = new EventEmitter(); this.completionChanged = new EventEmitter();
@ -124,6 +126,28 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
}); });
} }
}, this.sitesProvider.getCurrentSiteId()); }, this.sitesProvider.getCurrentSiteId());
// Listen for select course tab events to select the right section if needed.
this.selectTabObserver = eventsProvider.on(CoreEventsProvider.SELECT_COURSE_TAB, (data) => {
if (!data.name) {
let section;
if (typeof data.sectionId != 'undefined' && data.sectionId != null && this.sections) {
section = this.sections.find((section) => {
return section.id == data.sectionId;
});
} else if (typeof data.sectionNumber != 'undefined' && data.sectionNumber != null && this.sections) {
section = this.sections.find((section) => {
return section.section == data.sectionNumber;
});
}
if (section) {
this.sectionChanged(section);
}
}
});
} }
/** /**
@ -312,6 +336,13 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
} else { } else {
this.domUtils.scrollToTop(this.content, 0); this.domUtils.scrollToTop(this.content, 0);
} }
if (!previousValue || previousValue.id != newSection.id) {
// First load or section changed, add log in Moodle.
this.courseProvider.logView(this.course.id, newSection.section, undefined, this.course.fullname).catch(() => {
// Ignore errors.
});
}
} }
/** /**
@ -437,9 +468,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
* Component destroyed. * Component destroyed.
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
if (this.sectionStatusObserver) { this.sectionStatusObserver && this.sectionStatusObserver.off();
this.sectionStatusObserver.off(); this.selectTabObserver && this.selectTabObserver.off();
}
} }
/** /**

View File

@ -67,6 +67,7 @@ export class CoreCourseSectionPage implements OnDestroy {
protected modParams: any; protected modParams: any;
protected completionObserver; protected completionObserver;
protected courseStatusObserver; protected courseStatusObserver;
protected selectTabObserver;
protected syncObserver; protected syncObserver;
protected firstTabName: string; protected firstTabName: string;
protected isDestroyed = false; protected isDestroyed = false;
@ -120,6 +121,26 @@ export class CoreCourseSectionPage implements OnDestroy {
} }
}, sitesProvider.getCurrentSiteId()); }, sitesProvider.getCurrentSiteId());
} }
this.selectTabObserver = eventsProvider.on(CoreEventsProvider.SELECT_COURSE_TAB, (data) => {
if (!data.name) {
// If needed, set sectionId and sectionNumber. They'll only be used if the content tabs hasn't been loaded yet.
this.sectionId = data.sectionId || this.sectionId;
this.sectionNumber = data.sectionNumber || this.sectionNumber;
// Select course contents.
this.tabsComponent && this.tabsComponent.selectTab(0);
} else if (this.courseHandlers) {
const index = this.courseHandlers.findIndex((handler) => {
return handler.name == data.name;
});
if (index >= 0) {
this.tabsComponent && this.tabsComponent.selectTab(index + 1);
}
}
});
} }
/** /**
@ -213,11 +234,6 @@ export class CoreCourseSectionPage implements OnDestroy {
}).then((sections) => { }).then((sections) => {
let promise; let promise;
// Add log in Moodle.
this.courseProvider.logView(this.course.id, this.sectionNumber, undefined, this.course.fullname).catch(() => {
// Ignore errors.
});
// Get the completion status. // Get the completion status.
if (this.course.enablecompletion === false) { if (this.course.enablecompletion === false) {
// Completion not enabled. // Completion not enabled.
@ -483,9 +499,8 @@ export class CoreCourseSectionPage implements OnDestroy {
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
this.isDestroyed = true; this.isDestroyed = true;
if (this.completionObserver) { this.completionObserver && this.completionObserver.off();
this.completionObserver.off(); this.selectTabObserver && this.selectTabObserver.off();
}
} }
/** /**

View File

@ -164,6 +164,23 @@ export class CoreCourseProvider {
}); });
} }
/**
* Check if the current view in a NavController is a certain course initial page.
*
* @param {NavController} navCtrl NavController.
* @param {number} courseId Course ID.
* @return {boolean} Whether the current view is a certain course.
*/
currentViewIsCourse(navCtrl: NavController, courseId: number): boolean {
if (navCtrl) {
const view = navCtrl.getActive();
return view && view.id == 'CoreCourseSectionPage' && view.data && view.data.course && view.data.course.id == courseId;
}
return false;
}
/** /**
* Get completion status of all the activities in a course for a certain user. * Get completion status of all the activities in a course for a certain user.
* *
@ -973,6 +990,19 @@ export class CoreCourseProvider {
}); });
} }
/**
* Select a certain tab in the course. Please use currentViewIsCourse() first to verify user is viewing the course.
*
* @param {string} [name] Name of the tab. If not provided, course contents.
* @param {any} [params] Other params.
*/
selectCourseTab(name?: string, params?: any): void {
params = params || {};
params.name = name || '';
this.eventsProvider.trigger(CoreEventsProvider.SELECT_COURSE_TAB, params);
}
/** /**
* Change the course status, setting it to the previous status. * Change the course status, setting it to the previous status.
* *

View File

@ -36,6 +36,7 @@ import { CoreCourseModulePrefetchDelegate } from './module-prefetch-delegate';
import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreLoginHelperProvider } from '@core/login/providers/helper';
import { CoreConstants } from '@core/constants'; import { CoreConstants } from '@core/constants';
import { CoreSite } from '@classes/site'; import { CoreSite } from '@classes/site';
import { CoreLoggerProvider } from '@providers/logger';
import * as moment from 'moment'; import * as moment from 'moment';
/** /**
@ -115,16 +116,21 @@ export type CoreCourseCoursesProgress = {
export class CoreCourseHelperProvider { export class CoreCourseHelperProvider {
protected courseDwnPromises: { [s: string]: { [id: number]: Promise<any> } } = {}; protected courseDwnPromises: { [s: string]: { [id: number]: Promise<any> } } = {};
protected logger;
constructor(private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider, constructor(private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
private moduleDelegate: CoreCourseModuleDelegate, private prefetchDelegate: CoreCourseModulePrefetchDelegate, private moduleDelegate: CoreCourseModuleDelegate, private prefetchDelegate: CoreCourseModulePrefetchDelegate,
private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider, private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider,
private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider, private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider,
private utils: CoreUtilsProvider, private translate: TranslateService, private loginHelper: CoreLoginHelperProvider, private utils: CoreUtilsProvider, private translate: TranslateService, private loginHelper: CoreLoginHelperProvider,
private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider, private courseOptionsDelegate: CoreCourseOptionsDelegate, private siteHomeProvider: CoreSiteHomeProvider,
private eventsProvider: CoreEventsProvider, private fileHelper: CoreFileHelperProvider, private eventsProvider: CoreEventsProvider, private fileHelper: CoreFileHelperProvider,
private appProvider: CoreAppProvider, private fileProvider: CoreFileProvider, private injector: Injector, private appProvider: CoreAppProvider, private fileProvider: CoreFileProvider, private injector: Injector,
private coursesProvider: CoreCoursesProvider, private courseOffline: CoreCourseOfflineProvider) { } private coursesProvider: CoreCoursesProvider, private courseOffline: CoreCourseOfflineProvider,
loggerProvider: CoreLoggerProvider) {
this.logger = loggerProvider.getInstance('CoreCourseHelperProvider');
}
/** /**
* This function treats every module on the sections provided to load the handler data, treat completion * This function treats every module on the sections provided to load the handler data, treat completion
@ -1109,9 +1115,12 @@ export class CoreCourseHelperProvider {
* @param {string} [modName] If set, the app will retrieve all modules of this type with a single WS call. This reduces the * @param {string} [modName] If set, the app will retrieve all modules of this type with a single WS call. This reduces the
* number of WS calls, but it isn't recommended for modules that can return a lot of contents. * number of WS calls, but it isn't recommended for modules that can return a lot of contents.
* @param {any} [modParams] Params to pass to the module * @param {any} [modParams] Params to pass to the module
* @param {NavController} [navCtrl] NavController for adding new pages to the current history. Optional for legacy support, but
* generates a warning if omitted.
* @return {Promise<void>} Promise resolved when done. * @return {Promise<void>} Promise resolved when done.
*/ */
navigateToModule(moduleId: number, siteId?: string, courseId?: number, sectionId?: number, modName?: string, modParams?: any) navigateToModule(moduleId: number, siteId?: string, courseId?: number, sectionId?: number, modName?: string, modParams?: any,
navCtrl?: NavController)
: Promise<void> { : Promise<void> {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
@ -1157,6 +1166,16 @@ export class CoreCourseHelperProvider {
module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, sectionId); module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, sectionId);
if (navCtrl) {
// If the link handler for this module passed through navCtrl, we can use the module's handler to navigate cleanly.
// Otherwise, we will redirect below.
modal.dismiss();
return module.handlerData.action(new Event('click'), navCtrl, module, courseId);
}
this.logger.warn('navCtrl was not passed to navigateToModule by the link handler for ' + module.modname);
if (courseId == site.getSiteHomeId()) { if (courseId == site.getSiteHomeId()) {
// Check if site home is available. // Check if site home is available.
return this.siteHomeProvider.isAvailable().then(() => { return this.siteHomeProvider.isAvailable().then(() => {

View File

@ -22,6 +22,8 @@ import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreCourseProvider } from '@core/course/providers/course';
import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseHelperProvider } from '@core/course/providers/helper';
import { CoreCoursesProvider } from './courses'; import { CoreCoursesProvider } from './courses';
import { NavController } from 'ionic-angular';
import { CoreLoggerProvider } from '@providers/logger';
/** /**
* Handler to treat links to course view or enrol (except site home). * Handler to treat links to course view or enrol (except site home).
@ -32,12 +34,16 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
pattern = /((\/enrol\/index\.php)|(\/course\/enrol\.php)|(\/course\/view\.php)).*([\?\&]id=\d+)/; pattern = /((\/enrol\/index\.php)|(\/course\/enrol\.php)|(\/course\/view\.php)).*([\?\&]id=\d+)/;
protected waitStart = 0; protected waitStart = 0;
protected logger;
constructor(private sitesProvider: CoreSitesProvider, private coursesProvider: CoreCoursesProvider, constructor(private sitesProvider: CoreSitesProvider, private coursesProvider: CoreCoursesProvider,
private domUtils: CoreDomUtilsProvider, private domUtils: CoreDomUtilsProvider,
private translate: TranslateService, private courseProvider: CoreCourseProvider, private translate: TranslateService, private courseProvider: CoreCourseProvider,
private textUtils: CoreTextUtilsProvider, private courseHelper: CoreCourseHelperProvider) { private textUtils: CoreTextUtilsProvider, private courseHelper: CoreCourseHelperProvider,
loggerProvider: CoreLoggerProvider) {
super(); super();
this.logger = loggerProvider.getInstance('CoreCoursesCourseLinkHandler');
} }
/** /**
@ -75,9 +81,17 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
siteId = siteId || this.sitesProvider.getCurrentSiteId(); siteId = siteId || this.sitesProvider.getCurrentSiteId();
if (siteId == this.sitesProvider.getCurrentSiteId()) { if (siteId == this.sitesProvider.getCurrentSiteId()) {
this.actionEnrol(courseId, url, pageParams).catch(() => { // Check if we already are in the course index page.
// Ignore errors. if (this.courseProvider.currentViewIsCourse(navCtrl, courseId)) {
}); // Current view is this course, just select the contents tab.
this.courseProvider.selectCourseTab('', pageParams);
return;
} else {
this.actionEnrol(courseId, url, pageParams, navCtrl).catch(() => {
// Ignore errors.
});
}
} else { } else {
// Don't pass the navCtrl to make the course the new history root (to avoid "loops" in history). // Don't pass the navCtrl to make the course the new history root (to avoid "loops" in history).
this.courseHelper.getAndOpenCourse(undefined, courseId, pageParams, siteId); this.courseHelper.getAndOpenCourse(undefined, courseId, pageParams, siteId);
@ -115,9 +129,11 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
* @param {number} courseId Course ID. * @param {number} courseId Course ID.
* @param {string} url Treated URL. * @param {string} url Treated URL.
* @param {any} pageParams Params to send to the new page. * @param {any} pageParams Params to send to the new page.
* @param {NavController} [navCtrl] NavController for adding new pages to the current history. Optional for legacy support, but
* generates a warning if omitted.
* @return {Promise<any>} Promise resolved when done. * @return {Promise<any>} Promise resolved when done.
*/ */
protected actionEnrol(courseId: number, url: string, pageParams: any): Promise<any> { protected actionEnrol(courseId: number, url: string, pageParams: any, navCtrl?: NavController): Promise<any> {
const modal = this.domUtils.showModalLoading(), const modal = this.domUtils.showModalLoading(),
isEnrolUrl = !!url.match(/(\/enrol\/index\.php)|(\/course\/enrol\.php)/); isEnrolUrl = !!url.match(/(\/enrol\/index\.php)|(\/course\/enrol\.php)/);
let course; let course;
@ -188,8 +204,12 @@ export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
}).then((course) => { }).then((course) => {
modal.dismiss(); modal.dismiss();
if (typeof navCtrl === 'undefined') {
this.logger.warn('navCtrl was not passed to actionEnrol');
}
// Now open the course. // Now open the course.
this.courseHelper.openCourse(undefined, course, pageParams); this.courseHelper.openCourse(navCtrl, course, pageParams);
}); });
} }

View File

@ -15,7 +15,7 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { CoreCoursesProvider } from './courses'; import { CoreCoursesProvider } from './courses';
/** /**
@ -27,7 +27,7 @@ export class CoreCoursesIndexLinkHandler extends CoreContentLinksHandlerBase {
featureName = 'CoreMainMenuDelegate_CoreCourses'; featureName = 'CoreMainMenuDelegate_CoreCourses';
pattern = /\/course\/?(index\.php.*)?$/; pattern = /\/course\/?(index\.php.*)?$/;
constructor(private coursesProvider: CoreCoursesProvider, private loginHelper: CoreLoginHelperProvider) { constructor(private coursesProvider: CoreCoursesProvider, private linkHelper: CoreContentLinksHelperProvider) {
super(); super();
} }
@ -56,8 +56,7 @@ export class CoreCoursesIndexLinkHandler extends CoreContentLinksHandlerBase {
} }
} }
// Always use redirect to make it the new history root (to avoid "loops" in history). this.linkHelper.goInSite(navCtrl, page, pageParams, siteId);
this.loginHelper.redirect(page, pageParams, siteId);
} }
}]; }];
} }

View File

@ -43,7 +43,7 @@ export class CoreCoursesDashboardLinkHandler extends CoreContentLinksHandlerBase
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
// Always use redirect to make it the new history root (to avoid "loops" in history). // Use redirect to select the tab.
this.loginHelper.redirect('CoreCoursesDashboardPage', undefined, siteId); this.loginHelper.redirect('CoreCoursesDashboardPage', undefined, siteId);
} }
}]; }];

View File

@ -24,7 +24,6 @@ import { CoreUrlUtilsProvider } from '@providers/utils/url';
import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { CoreLoginHelperProvider } from '@core/login/providers/helper';
import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseHelperProvider } from '@core/course/providers/helper';
/** /**
@ -38,8 +37,7 @@ export class CoreGradesHelperProvider {
private gradesProvider: CoreGradesProvider, private sitesProvider: CoreSitesProvider, private gradesProvider: CoreGradesProvider, private sitesProvider: CoreSitesProvider,
private textUtils: CoreTextUtilsProvider, private courseProvider: CoreCourseProvider, private textUtils: CoreTextUtilsProvider, private courseProvider: CoreCourseProvider,
private domUtils: CoreDomUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private utils: CoreUtilsProvider, private domUtils: CoreDomUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private utils: CoreUtilsProvider,
private linkHelper: CoreContentLinksHelperProvider, private loginHelper: CoreLoginHelperProvider, private linkHelper: CoreContentLinksHelperProvider, private courseHelper: CoreCourseHelperProvider) {
private courseHelper: CoreCourseHelperProvider) {
this.logger = logger.getInstance('CoreGradesHelperProvider'); this.logger = logger.getInstance('CoreGradesHelperProvider');
} }
@ -457,14 +455,22 @@ export class CoreGradesHelperProvider {
}); });
} }
// View own grades. Open the course with the grades tab selected. // View own grades. Check if we already are in the course index page.
if (this.courseProvider.currentViewIsCourse(navCtrl, courseId)) {
// Current view is this course, just select the grades tab.
this.courseProvider.selectCourseTab('CoreGrades');
return;
}
// Open the course with the grades tab selected.
return this.courseHelper.getCourse(courseId, siteId).then((result) => { return this.courseHelper.getCourse(courseId, siteId).then((result) => {
const pageParams: any = { const pageParams: any = {
course: result.course, course: result.course,
selectedTab: 'CoreGrades' selectedTab: 'CoreGrades'
}; };
return this.loginHelper.redirect('CoreCourseSectionPage', pageParams, siteId).catch(() => { return this.linkHelper.goInSite(navCtrl, 'CoreCourseSectionPage', pageParams, siteId).catch(() => {
// Ignore errors. // Ignore errors.
}); });
}); });

View File

@ -43,7 +43,6 @@ export class CoreGradesOverviewLinkHandler extends CoreContentLinksHandlerBase {
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'CoreGradesCoursesPage', undefined, siteId); this.linkHelper.goInSite(navCtrl, 'CoreGradesCoursesPage', undefined, siteId);
} }
}]; }];

View File

@ -112,7 +112,6 @@ export class CoreGradesUserHandler implements CoreUserProfileHandler {
courseId: courseId, courseId: courseId,
userId: user.id userId: user.id
}; };
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'CoreGradesCoursePage', pageParams); this.linkHelper.goInSite(navCtrl, 'CoreGradesCoursePage', pageParams);
} }
}; };

View File

@ -629,7 +629,7 @@ export class CoreLoginHelperProvider {
* @param {string} page Name of the page to load. * @param {string} page Name of the page to load.
* @param {any} params Params to pass to the page. * @param {any} params Params to pass to the page.
*/ */
protected loadPageInMainMenu(page: string, params: any): void { loadPageInMainMenu(page: string, params: any): void {
if (!this.appProvider.isMainMenuOpen()) { if (!this.appProvider.isMainMenuOpen()) {
// Main menu not open. Store the page to be loaded later. // Main menu not open. Store the page to be loaded later.
this.pageToLoad = { this.pageToLoad = {

View File

@ -16,7 +16,9 @@ import { Injectable } from '@angular/core';
import { NavController } from 'ionic-angular'; import { NavController } from 'ionic-angular';
import { CoreLangProvider } from '@providers/lang'; import { CoreLangProvider } from '@providers/lang';
import { CoreSitesProvider } from '@providers/sites'; import { CoreSitesProvider } from '@providers/sites';
import { CoreUtilsProvider } from '@providers/utils/utils';
import { CoreConfigConstants } from '../../../configconstants'; import { CoreConfigConstants } from '../../../configconstants';
import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from './delegate';
/** /**
* Custom main menu item. * Custom main menu item.
@ -56,10 +58,34 @@ export class CoreMainMenuProvider {
static ITEM_MIN_WIDTH = 72; // Min with of every item, based on 5 items on a 360 pixel wide screen. static ITEM_MIN_WIDTH = 72; // Min with of every item, based on 5 items on a 360 pixel wide screen.
protected tablet = false; protected tablet = false;
constructor(private langProvider: CoreLangProvider, private sitesProvider: CoreSitesProvider) { constructor(private langProvider: CoreLangProvider, private sitesProvider: CoreSitesProvider,
protected menuDelegate: CoreMainMenuDelegate, protected utils: CoreUtilsProvider) {
this.tablet = window && window.innerWidth && window.innerWidth >= 576 && window.innerHeight >= 576; this.tablet = window && window.innerWidth && window.innerWidth >= 576 && window.innerHeight >= 576;
} }
/**
* Get the current main menu handlers.
*
* @return {Promise<CoreMainMenuHandlerToDisplay[]>} Promise resolved with the current main menu handlers.
*/
getCurrentMainMenuHandlers(): Promise<CoreMainMenuHandlerToDisplay[]> {
const deferred = this.utils.promiseDefer();
const subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
subscription && subscription.unsubscribe();
// Remove the handlers that should only appear in the More menu.
handlers = handlers.filter((handler) => {
return !handler.onlyInMore;
});
// Return main handlers.
deferred.resolve(handlers.slice(0, this.getNumItems()));
});
return deferred.promise;
}
/** /**
* Get a list of custom menu items for a certain site. * Get a list of custom menu items for a certain site.
* *
@ -211,6 +237,23 @@ export class CoreMainMenuProvider {
return tablet ? 'side' : 'bottom'; return tablet ? 'side' : 'bottom';
} }
/**
* Check if a certain page is the root of a main menu handler currently displayed.
*
* @param {string} page Name of the page.
* @param {string} [pageParams] Page params.
* @return {Promise<boolean>} Promise resolved with boolean: whether it's the root of a main menu handler.
*/
isCurrentMainMenuHandler(pageName: string, pageParams?: any): Promise<boolean> {
return this.getCurrentMainMenuHandlers().then((handlers) => {
const handler = handlers.find((handler, i) => {
return handler.page == pageName;
});
return !!handler;
});
}
/** /**
* Check if responsive main menu items is disabled in the current site. * Check if responsive main menu items is disabled in the current site.
* *

View File

@ -16,7 +16,7 @@ import { Injectable } from '@angular/core';
import { CoreSitesProvider } from '@providers/sites'; import { CoreSitesProvider } from '@providers/sites';
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { CoreSiteHomeProvider } from './sitehome'; import { CoreSiteHomeProvider } from './sitehome';
/** /**
@ -29,7 +29,7 @@ export class CoreSiteHomeIndexLinkHandler extends CoreContentLinksHandlerBase {
pattern = /\/course\/view\.php.*([\?\&]id=\d+)/; pattern = /\/course\/view\.php.*([\?\&]id=\d+)/;
constructor(private sitesProvider: CoreSitesProvider, private siteHomeProvider: CoreSiteHomeProvider, constructor(private sitesProvider: CoreSitesProvider, private siteHomeProvider: CoreSiteHomeProvider,
private loginHelper: CoreLoginHelperProvider) { private linkHelper: CoreContentLinksHelperProvider) {
super(); super();
} }
@ -46,8 +46,7 @@ export class CoreSiteHomeIndexLinkHandler extends CoreContentLinksHandlerBase {
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> { CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
// Always use redirect to make it the new history root (to avoid "loops" in history). this.linkHelper.goInSite(navCtrl, 'CoreSiteHomeIndexPage', undefined, siteId);
this.loginHelper.redirect('CoreSiteHomeIndexPage', undefined, siteId);
} }
}]; }];
} }

View File

@ -16,7 +16,7 @@ import { Injectable } from '@angular/core';
import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler'; import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate'; import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
import { CoreLoginHelperProvider } from '@core/login/providers/helper'; import { CoreCourseProvider } from '@core/course/providers/course';
import { CoreCourseHelperProvider } from '@core/course/providers/helper'; import { CoreCourseHelperProvider } from '@core/course/providers/helper';
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
import { CoreUserProvider } from './user'; import { CoreUserProvider } from './user';
@ -30,9 +30,9 @@ export class CoreUserParticipantsLinkHandler extends CoreContentLinksHandlerBase
featureName = 'CoreCourseOptionsDelegate_CoreUserParticipants'; featureName = 'CoreCourseOptionsDelegate_CoreUserParticipants';
pattern = /\/user\/index\.php/; pattern = /\/user\/index\.php/;
constructor(private userProvider: CoreUserProvider, private loginHelper: CoreLoginHelperProvider, constructor(private userProvider: CoreUserProvider,
private courseHelper: CoreCourseHelperProvider, private domUtils: CoreDomUtilsProvider, private courseHelper: CoreCourseHelperProvider, private domUtils: CoreDomUtilsProvider,
private linkHelper: CoreContentLinksHelperProvider) { private linkHelper: CoreContentLinksHelperProvider, private courseProvider: CoreCourseProvider) {
super(); super();
} }
@ -51,6 +51,14 @@ export class CoreUserParticipantsLinkHandler extends CoreContentLinksHandlerBase
return [{ return [{
action: (siteId, navCtrl?): void => { action: (siteId, navCtrl?): void => {
// Check if we already are in the course index page.
if (this.courseProvider.currentViewIsCourse(navCtrl, courseId)) {
// Current view is this course, just select the participants tab.
this.courseProvider.selectCourseTab('CoreUserParticipants');
return;
}
const modal = this.domUtils.showModalLoading(); const modal = this.domUtils.showModalLoading();
this.courseHelper.getCourse(courseId, siteId).then((result) => { this.courseHelper.getCourse(courseId, siteId).then((result) => {
@ -59,8 +67,9 @@ export class CoreUserParticipantsLinkHandler extends CoreContentLinksHandlerBase
selectedTab: 'CoreUserParticipants' selectedTab: 'CoreUserParticipants'
}; };
// Always use redirect to make it the new history root (to avoid "loops" in history). return this.linkHelper.goInSite(navCtrl, 'CoreCourseSectionPage', params, siteId).catch(() => {
return this.loginHelper.redirect('CoreCourseSectionPage', params, siteId); // Ignore errors.
});
}).catch(() => { }).catch(() => {
// Cannot get course for some reason, just open the participants page. // Cannot get course for some reason, just open the participants page.
return this.linkHelper.goInSite(navCtrl, 'CoreUserParticipantsPage', {courseId: courseId}, siteId); return this.linkHelper.goInSite(navCtrl, 'CoreUserParticipantsPage', {courseId: courseId}, siteId);

View File

@ -47,7 +47,6 @@ export class CoreUserProfileLinkHandler extends CoreContentLinksHandlerBase {
courseId: params.course, courseId: params.course,
userId: parseInt(params.id, 10) userId: parseInt(params.id, 10)
}; };
// Always use redirect to make it the new history root (to avoid "loops" in history).
this.linkHelper.goInSite(navCtrl, 'CoreUserProfilePage', pageParams, siteId); this.linkHelper.goInSite(navCtrl, 'CoreUserProfilePage', pageParams, siteId);
} }
}]; }];

View File

@ -40,6 +40,8 @@
"completion-alt-manual-y-override": "Completed: {{$a.modname}} (set by {{$a.overrideuser}}). Select to mark as not complete.", "completion-alt-manual-y-override": "Completed: {{$a.modname}} (set by {{$a.overrideuser}}). Select to mark as not complete.",
"confirmcanceledit": "Are you sure you want to leave this page? All changes will be lost.", "confirmcanceledit": "Are you sure you want to leave this page? All changes will be lost.",
"confirmdeletefile": "Are you sure you want to delete this file?", "confirmdeletefile": "Are you sure you want to delete this file?",
"confirmgotabroot": "Are you sure you want to go back to {{name}}?",
"confirmgotabrootdefault": "Are you sure you want to go to the initial page of the current tab?",
"confirmloss": "Are you sure? All changes will be lost.", "confirmloss": "Are you sure? All changes will be lost.",
"confirmopeninbrowser": "Do you want to open it in a web browser?", "confirmopeninbrowser": "Do you want to open it in a web browser?",
"considereddigitalminor": "You are too young to create an account on this site.", "considereddigitalminor": "You are too young to create an account on this site.",

View File

@ -60,6 +60,7 @@ export class CoreEventsProvider {
static LOAD_PAGE_MAIN_MENU = 'load_page_main_menu'; static LOAD_PAGE_MAIN_MENU = 'load_page_main_menu';
static SEND_ON_ENTER_CHANGED = 'send_on_enter_changed'; static SEND_ON_ENTER_CHANGED = 'send_on_enter_changed';
static MAIN_MENU_OPEN = 'main_menu_open'; static MAIN_MENU_OPEN = 'main_menu_open';
static SELECT_COURSE_TAB = 'select_course_tab';
protected logger; protected logger;
protected observables: { [s: string]: Subject<any> } = {}; protected observables: { [s: string]: Subject<any> } = {};