commit
ca95f53134
|
@ -27,7 +27,11 @@ const routes: Routes = [
|
|||
},
|
||||
{
|
||||
path: 'settings',
|
||||
loadChildren: () => import('./core/settings/settings.module').then( m => m.CoreAppSettingsPageModule),
|
||||
loadChildren: () => import('./core/settings/settings.module').then( m => m.CoreSettingsModule),
|
||||
},
|
||||
{
|
||||
path: 'mainmenu',
|
||||
loadChildren: () => import('./core/mainmenu/mainmenu.module').then( m => m.CoreMainMenuModule),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { NavController } from '@ionic/angular';
|
||||
|
||||
import { CoreLangProvider } from '@services/lang';
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
|
||||
|
@ -24,7 +26,8 @@ import { CoreEvents } from '@singletons/events';
|
|||
export class AppComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private langProvider: CoreLangProvider,
|
||||
protected langProvider: CoreLangProvider,
|
||||
protected navCtrl: NavController,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -34,16 +37,13 @@ export class AppComponent implements OnInit {
|
|||
ngOnInit(): void {
|
||||
CoreEvents.on(CoreEvents.LOGOUT, () => {
|
||||
// Go to sites page when user is logged out.
|
||||
// Due to DeepLinker, we need to use the ViewCtrl instead of name.
|
||||
// Otherwise some pages are re-created when they shouldn't.
|
||||
// TODO
|
||||
// CoreApp.instance.getRootNavController().setRoot(CoreLoginSitesPage);
|
||||
this.navCtrl.navigateRoot('/login/sites');
|
||||
|
||||
// Unload lang custom strings.
|
||||
this.langProvider.clearCustomStrings();
|
||||
|
||||
// Remove version classes from body.
|
||||
// TODO
|
||||
// @todo
|
||||
// this.removeVersionClass();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ import { CoreUtilsProvider } from '@services/utils/utils';
|
|||
// Import core modules.
|
||||
import { CoreEmulatorModule } from '@core/emulator/emulator.module';
|
||||
import { CoreLoginModule } from '@core/login/login.module';
|
||||
import { CoreCoursesModule } from '@core/courses/courses.module';
|
||||
|
||||
import { setSingletonsInjector } from '@singletons/core.singletons';
|
||||
|
||||
|
@ -81,6 +82,7 @@ export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
|
|||
AppRoutingModule,
|
||||
CoreEmulatorModule,
|
||||
CoreLoginModule,
|
||||
CoreCoursesModule,
|
||||
],
|
||||
providers: [
|
||||
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
|
||||
|
|
|
@ -50,20 +50,20 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit {
|
|||
@Input() message?: string; // Message to show while loading.
|
||||
@ViewChild('content') content?: ElementRef;
|
||||
|
||||
protected uniqueId!: string;
|
||||
uniqueId: string;
|
||||
protected element: HTMLElement; // Current element.
|
||||
|
||||
constructor(element: ElementRef) {
|
||||
this.element = element.nativeElement;
|
||||
|
||||
// Calculate the unique ID.
|
||||
this.uniqueId = 'core-loading-content-' + CoreUtils.instance.getUniqueId('CoreLoadingComponent');
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
// Calculate the unique ID.
|
||||
this.uniqueId = 'core-loading-content-' + CoreUtils.instance.getUniqueId('CoreLoadingComponent');
|
||||
|
||||
if (!this.message) {
|
||||
// Default loading message.
|
||||
this.message = Translate.instance.instant('core.loading');
|
||||
|
|
|
@ -45,8 +45,8 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
|
|||
@ContentChild(IonInput) ionInput?: IonInput;
|
||||
|
||||
shown!: boolean; // Whether the password is shown.
|
||||
label?: string; // Label for the button to show/hide.
|
||||
iconName?: string; // Name of the icon of the button to show/hide.
|
||||
label!: string; // Label for the button to show/hide.
|
||||
iconName!: string; // Name of the icon of the button to show/hide.
|
||||
selector = ''; // Selector to identify the input.
|
||||
|
||||
protected input?: HTMLInputElement | null; // Input affected.
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { CoreMainMenuDelegate } from '@core/mainmenu/services/delegate';
|
||||
import { CoreHomeMainMenuHandler } from './handlers/mainmenu';
|
||||
|
||||
@NgModule({
|
||||
imports: [],
|
||||
declarations: [],
|
||||
})
|
||||
export class CoreCoursesModule {
|
||||
|
||||
constructor(mainMenuDelegate: CoreMainMenuDelegate) {
|
||||
mainMenuDelegate.registerHandler(new CoreHomeMainMenuHandler());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/services/delegate';
|
||||
|
||||
/**
|
||||
* Handler to add Home into main menu.
|
||||
*/
|
||||
export class CoreHomeMainMenuHandler implements CoreMainMenuHandler {
|
||||
|
||||
name = 'CoreHome';
|
||||
priority = 1100;
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled on a site level.
|
||||
*
|
||||
* @return Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
isEnabled(): Promise<boolean> {
|
||||
return this.isEnabledForSite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled on a certain site.
|
||||
*
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Whether or not the handler is enabled on a site level.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async isEnabledForSite(siteId?: string): Promise<boolean> {
|
||||
// @todo
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @return Data needed to render the handler.
|
||||
*/
|
||||
getDisplayData(): CoreMainMenuHandlerData {
|
||||
return {
|
||||
icon: 'fa-home',
|
||||
title: 'core.courses.mymoodle',
|
||||
page: 'home',
|
||||
class: 'core-home-handler',
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"addtofavourites": "Star this course",
|
||||
"allowguests": "This course allows guest users to enter",
|
||||
"availablecourses": "Available courses",
|
||||
"cannotretrievemorecategories": "Categories deeper than level {{$a}} cannot be retrieved.",
|
||||
"categories": "Course categories",
|
||||
"confirmselfenrol": "Are you sure you want to enrol yourself in this course?",
|
||||
"courses": "Courses",
|
||||
"downloadcourses": "Download courses",
|
||||
"enrolme": "Enrol me",
|
||||
"errorloadcategories": "An error occurred while loading categories.",
|
||||
"errorloadcourses": "An error occurred while loading courses.",
|
||||
"errorloadplugins": "The plugins required by this course could not be loaded correctly. Please reload the app to try again.",
|
||||
"errorsearching": "An error occurred while searching.",
|
||||
"errorselfenrol": "An error occurred while self enrolling.",
|
||||
"filtermycourses": "Filter my courses",
|
||||
"frontpage": "Front page",
|
||||
"hidecourse": "Remove from view",
|
||||
"ignore": "Ignore",
|
||||
"mycourses": "My courses",
|
||||
"mymoodle": "Dashboard",
|
||||
"nocourses": "No course information to show.",
|
||||
"nocoursesyet": "No courses in this category",
|
||||
"nosearchresults": "No results",
|
||||
"notenroled": "You are not enrolled in this course",
|
||||
"notenrollable": "You cannot enrol yourself in this course.",
|
||||
"password": "Enrolment key",
|
||||
"paymentrequired": "This course requires a payment for entry.",
|
||||
"paypalaccepted": "PayPal payments accepted",
|
||||
"reload": "Reload",
|
||||
"removefromfavourites": "Unstar this course",
|
||||
"search": "Search",
|
||||
"searchcourses": "Search courses",
|
||||
"searchcoursesadvice": "You can use the search courses button to find courses to access as a guest or enrol yourself in courses that allow it.",
|
||||
"selfenrolment": "Self enrolment",
|
||||
"sendpaymentbutton": "Send payment via PayPal",
|
||||
"show": "Restore to view",
|
||||
"totalcoursesearchresults": "Total courses: {{$a}}"
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||
</ion-buttons>
|
||||
|
||||
<ion-title>
|
||||
<core-format-text [text]="siteName" contextLevel="system" [contextInstanceId]="0"></core-format-text>
|
||||
<img src="assets/img/login_logo.png" class="core-header-logo" [alt]="siteName">
|
||||
</ion-title>
|
||||
|
||||
<ion-buttons slot="end">
|
||||
<!-- @todo -->
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<!-- @todo -->
|
||||
Home page.
|
||||
</ion-content>
|
|
@ -0,0 +1,47 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
|
||||
import { CoreCoursesHomePage } from './home.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreCoursesHomePage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreCoursesHomePage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreCoursesHomePageModule {}
|
|
@ -0,0 +1,36 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Page that displays the Home.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'page-core-courses-home',
|
||||
templateUrl: 'home.html',
|
||||
styleUrls: ['home.scss'],
|
||||
})
|
||||
export class CoreCoursesHomePage implements OnInit {
|
||||
|
||||
siteName = 'Hello world';
|
||||
|
||||
/**
|
||||
* Initialize the component.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
// @todo
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
$core-dashboard-logo: false !default;
|
||||
|
||||
@if $core-dashboard-logo {
|
||||
.toolbar-title-default.md .title-default .core-header-logo {
|
||||
max-height: $toolbar-md-height - 24;
|
||||
}
|
||||
|
||||
.toolbar-title-default.ios .title-default .core-header-logo {
|
||||
max-height: $toolbar-ios-height - 24;
|
||||
}
|
||||
|
||||
.toolbar-title-default .title-default core-format-text {
|
||||
display: none;
|
||||
}
|
||||
} @else {
|
||||
.toolbar-title-default .core-header-logo {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
ion-badge.core-course-download-courses-progress {
|
||||
display: block;
|
||||
// @include float(start);
|
||||
// @include margin(12px, 12px, null, 12px);
|
||||
}
|
||||
|
|
@ -15,27 +15,27 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { CoreLoginCredentialsPage } from './pages/credentials/credentials.page';
|
||||
import { CoreLoginInitPage } from './pages/init/init.page';
|
||||
import { CoreLoginSitePage } from './pages/site/site.page';
|
||||
import { CoreLoginSitesPage } from './pages/sites/sites.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreLoginInitPage,
|
||||
redirectTo: 'init',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'init',
|
||||
loadChildren: () => import('./pages/init/init.page.module').then( m => m.CoreLoginInitPageModule),
|
||||
},
|
||||
{
|
||||
path: 'site',
|
||||
component: CoreLoginSitePage,
|
||||
loadChildren: () => import('./pages/site/site.page.module').then( m => m.CoreLoginSitePageModule),
|
||||
},
|
||||
{
|
||||
path: 'credentials',
|
||||
component: CoreLoginCredentialsPage,
|
||||
loadChildren: () => import('./pages/credentials/credentials.page.module').then( m => m.CoreLoginCredentialsPageModule),
|
||||
},
|
||||
{
|
||||
path: 'sites',
|
||||
component: CoreLoginSitesPage,
|
||||
loadChildren: () => import('./pages/sites/sites.page.module').then( m => m.CoreLoginSitesPageModule),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -13,42 +13,12 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
|
||||
import { CoreLoginRoutingModule } from './login-routing.module';
|
||||
import { CoreLoginCredentialsPage } from './pages/credentials/credentials.page';
|
||||
import { CoreLoginInitPage } from './pages/init/init.page';
|
||||
import { CoreLoginSitePage } from './pages/site/site.page';
|
||||
import { CoreLoginSitesPage } from './pages/sites/sites.page';
|
||||
import { CoreLoginHelperProvider } from './services/helper';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
CoreLoginRoutingModule,
|
||||
CoreComponentsModule,
|
||||
TranslateModule.forChild(),
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreLoginCredentialsPage,
|
||||
CoreLoginInitPage,
|
||||
CoreLoginSitePage,
|
||||
CoreLoginSitesPage,
|
||||
],
|
||||
providers: [
|
||||
CoreLoginHelperProvider,
|
||||
],
|
||||
declarations: [],
|
||||
})
|
||||
export class CoreLoginModule {}
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
<ion-title>{{ 'core.login.login' | translate }}</ion-title>
|
||||
|
||||
<ion-buttons slot="end">
|
||||
<!-- @todo: Settings button. -->
|
||||
<ion-button router-direction="forward" routerLink="/settings/app"
|
||||
[attr.aria-label]="'core.settings.appsettings' | translate">
|
||||
<core-icon slot="icon-only" name="fa-cog"></core-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
|
||||
import { CoreLoginCredentialsPage } from './credentials.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreLoginCredentialsPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreLoginCredentialsPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreLoginCredentialsPageModule {}
|
|
@ -243,7 +243,7 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy {
|
|||
|
||||
this.siteId = id;
|
||||
|
||||
await CoreLoginHelper.instance.goToSiteInitialPage(undefined, undefined, undefined, undefined, this.urlToOpen);
|
||||
await CoreLoginHelper.instance.goToSiteInitialPage({ urlToOpen: this.urlToOpen });
|
||||
} catch (error) {
|
||||
CoreLoginHelper.instance.treatUserTokenError(siteUrl, error, username, password);
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
|
||||
import { CoreLoginInitPage } from './init.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreLoginInitPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
IonicModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreLoginInitPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreLoginInitPageModule {}
|
|
@ -15,9 +15,13 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { NavController } from '@ionic/angular';
|
||||
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreApp, CoreRedirectData } from '@services/app';
|
||||
import { CoreInit } from '@services/init';
|
||||
import { SplashScreen } from '@singletons/core.singletons';
|
||||
import { CoreConstants } from '@core/constants';
|
||||
import { CoreSite } from '@/app/classes/site';
|
||||
import { CoreSites } from '@/app/services/sites';
|
||||
import { CoreLoginHelper, CoreLoginHelperProvider } from '../../services/helper';
|
||||
|
||||
/**
|
||||
* Page that displays a "splash screen" while the app is being initialized.
|
||||
|
@ -40,55 +44,75 @@ export class CoreLoginInitPage implements OnInit {
|
|||
|
||||
// Check if there was a pending redirect.
|
||||
const redirectData = CoreApp.instance.getRedirect();
|
||||
|
||||
if (redirectData.siteId) {
|
||||
// Unset redirect data.
|
||||
CoreApp.instance.storeRedirect('', '', {});
|
||||
|
||||
// Only accept the redirect if it was stored less than 20 seconds ago.
|
||||
if (redirectData.timemodified && Date.now() - redirectData.timemodified < 20000) {
|
||||
// if (redirectData.siteId != CoreConstants.NO_SITE_ID) {
|
||||
// // The redirect is pointing to a site, load it.
|
||||
// return this.sitesProvider.loadSite(redirectData.siteId, redirectData.page, redirectData.params)
|
||||
// .then((loggedIn) => {
|
||||
|
||||
// if (loggedIn) {
|
||||
// return this.loginHelper.goToSiteInitialPage(this.navCtrl, redirectData.page, redirectData.params,
|
||||
// { animate: false });
|
||||
// }
|
||||
// }).catch(() => {
|
||||
// // Site doesn't exist.
|
||||
// return this.loadPage();
|
||||
// });
|
||||
// } else {
|
||||
// // No site to load, open the page.
|
||||
// return this.loginHelper.goToNoSitePage(this.navCtrl, redirectData.page, redirectData.params);
|
||||
// }
|
||||
}
|
||||
await this.handleRedirect(redirectData);
|
||||
} else {
|
||||
await this.loadPage();
|
||||
}
|
||||
|
||||
await this.loadPage();
|
||||
|
||||
// If we hide the splash screen now, the init view is still seen for an instant. Wait a bit to make sure it isn't seen.
|
||||
setTimeout(() => {
|
||||
SplashScreen.instance.hide();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Treat redirect data.
|
||||
*
|
||||
* @param redirectData Redirect data.
|
||||
*/
|
||||
protected async handleRedirect(redirectData: CoreRedirectData): Promise<void> {
|
||||
// Unset redirect data.
|
||||
CoreApp.instance.storeRedirect('', '', {});
|
||||
|
||||
// Only accept the redirect if it was stored less than 20 seconds ago.
|
||||
if (redirectData.timemodified && Date.now() - redirectData.timemodified < 20000) {
|
||||
if (redirectData.siteId != CoreConstants.NO_SITE_ID) {
|
||||
// The redirect is pointing to a site, load it.
|
||||
try {
|
||||
const loggedIn = await CoreSites.instance.loadSite(
|
||||
redirectData.siteId!,
|
||||
redirectData.page,
|
||||
redirectData.params,
|
||||
);
|
||||
|
||||
if (!loggedIn) {
|
||||
return;
|
||||
}
|
||||
|
||||
return CoreLoginHelper.instance.goToSiteInitialPage({
|
||||
redirectPage: redirectData.page,
|
||||
redirectParams: redirectData.params,
|
||||
});
|
||||
} catch (error) {
|
||||
// Site doesn't exist.
|
||||
return this.loadPage();
|
||||
}
|
||||
} else {
|
||||
// No site to load, open the page.
|
||||
return CoreLoginHelper.instance.goToNoSitePage(redirectData.page, redirectData.params);
|
||||
}
|
||||
}
|
||||
|
||||
return this.loadPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the right page.
|
||||
*
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async loadPage(): Promise<void> {
|
||||
// if (this.sitesProvider.isLoggedIn()) {
|
||||
// if (this.loginHelper.isSiteLoggedOut()) {
|
||||
// return this.sitesProvider.logout().then(() => {
|
||||
// return this.loadPage();
|
||||
// });
|
||||
// }
|
||||
if (CoreSites.instance.isLoggedIn()) {
|
||||
if (CoreLoginHelper.instance.isSiteLoggedOut()) {
|
||||
await CoreSites.instance.logout();
|
||||
|
||||
// return this.loginHelper.goToSiteInitialPage();
|
||||
// }
|
||||
return this.loadPage();
|
||||
}
|
||||
|
||||
return CoreLoginHelper.instance.goToSiteInitialPage();
|
||||
}
|
||||
|
||||
await this.navCtrl.navigateRoot('/login/sites');
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
|
||||
import { CoreLoginSitePage } from './site.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreLoginSitePage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreLoginSitePage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreLoginSitePageModule {}
|
|
@ -417,8 +417,8 @@ export class CoreLoginSitePage implements OnInit {
|
|||
* @param event Received Event.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
filterChanged(event: any): void {
|
||||
const newValue = event.target.value?.trim().toLowerCase();
|
||||
filterChanged(event?: any): void {
|
||||
const newValue = event?.target.value?.trim().toLowerCase();
|
||||
if (!newValue || !this.fixedSites) {
|
||||
this.filteredSites = this.fixedSites;
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
|
||||
import { CoreLoginSitesPage } from './sites.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreLoginSitesPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreLoginSitesPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreLoginSitesPageModule {}
|
|
@ -34,11 +34,14 @@ import { CoreWSError } from '@classes/errors/wserror';
|
|||
import { makeSingleton, Translate } from '@singletons/core.singletons';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
import { CoreUrl } from '@singletons/url';
|
||||
import { NavigationOptions } from '@ionic/angular/providers/nav-controller';
|
||||
|
||||
/**
|
||||
* Helper provider that provides some common features regarding authentication.
|
||||
*/
|
||||
@Injectable()
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CoreLoginHelperProvider {
|
||||
|
||||
static readonly OPEN_COURSE = 'open_course';
|
||||
|
@ -448,7 +451,7 @@ export class CoreLoginHelperProvider {
|
|||
* @return Promise resolved when done.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
goToNoSitePage(navCtrl: NavController, page: string, params?: Params): Promise<any> {
|
||||
goToNoSitePage(page?: string, params?: Params): Promise<any> {
|
||||
// @todo
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
@ -456,17 +459,11 @@ export class CoreLoginHelperProvider {
|
|||
/**
|
||||
* Go to the initial page of a site depending on 'userhomepage' setting.
|
||||
*
|
||||
* @param navCtrl NavController to use. Defaults to app root NavController.
|
||||
* @param page Name of the page to load after loading the main page.
|
||||
* @param params Params to pass to the page.
|
||||
* @param options Navigation options.
|
||||
* @param url URL to open once the main menu is loaded.
|
||||
* @param options Options.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
goToSiteInitialPage(navCtrl?: NavController, page?: string, params?: Params, options?: any, url?: string): Promise<any> {
|
||||
// @todo
|
||||
return Promise.resolve();
|
||||
goToSiteInitialPage(options?: OpenMainMenuOptions): Promise<void> {
|
||||
return this.openMainMenu(options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -664,17 +661,32 @@ export class CoreLoginHelperProvider {
|
|||
/**
|
||||
* Open the main menu, loading a certain page.
|
||||
*
|
||||
* @param navCtrl NavController.
|
||||
* @param page Name of the page to load.
|
||||
* @param params Params to pass to the page.
|
||||
* @param options Navigation options.
|
||||
* @param url URL to open once the main menu is loaded.
|
||||
* @param options Options.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
protected openMainMenu(navCtrl: NavController, page: string, params: Params, options?: any, url?: string): Promise<any> {
|
||||
// @todo
|
||||
return Promise.resolve();
|
||||
protected async openMainMenu(options?: OpenMainMenuOptions): Promise<void> {
|
||||
|
||||
// Due to DeepLinker, we need to remove the path from the URL before going to main menu.
|
||||
// IonTabs checks the URL to determine which path to load for deep linking, so we clear the URL.
|
||||
// @todo this.location.replaceState('');
|
||||
|
||||
if (options?.redirectPage == CoreLoginHelperProvider.OPEN_COURSE) {
|
||||
// Load the main menu first, and then open the course.
|
||||
try {
|
||||
await this.navCtrl.navigateRoot('/mainmenu');
|
||||
} finally {
|
||||
// @todo: Open course.
|
||||
}
|
||||
} else {
|
||||
// Open the main menu.
|
||||
const queryParams: Params = Object.assign({}, options);
|
||||
delete queryParams.navigationOptions;
|
||||
|
||||
await this.navCtrl.navigateRoot('/mainmenu', {
|
||||
queryParams,
|
||||
...options?.navigationOptions,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1375,3 +1387,10 @@ type StoredLoginLaunchData = {
|
|||
pageParams: Params;
|
||||
ssoUrlParams: CoreUrlParams;
|
||||
};
|
||||
|
||||
type OpenMainMenuOptions = {
|
||||
redirectPage?: string; // Route of the page to open in main menu. If not defined, default tab will be selected.
|
||||
redirectParams?: Params; // Params to pass to the selected tab if any.
|
||||
urlToOpen?: string; // URL to open once the main menu is loaded.
|
||||
navigationOptions?: NavigationOptions; // Navigation options.
|
||||
};
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"changesite": "Change site",
|
||||
"help": "Help",
|
||||
"logout": "Log out",
|
||||
"website": "Website"
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { CoreMainMenuPage } from './pages/menu/menu.page';
|
||||
import { CoreMainMenuMorePage } from './pages/more/more.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreMainMenuPage,
|
||||
children: [
|
||||
{
|
||||
path: 'home', // @todo: Add this route dynamically.
|
||||
loadChildren: () => import('../courses/pages/home/home.page.module').then( m => m.CoreCoursesHomePageModule),
|
||||
},
|
||||
{
|
||||
path: 'more',
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: CoreMainMenuMorePage,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreMainMenuRoutingModule {}
|
|
@ -0,0 +1,42 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreComponentsModule } from '@/app/components/components.module';
|
||||
import { CoreDirectivesModule } from '@/app/directives/directives.module';
|
||||
|
||||
import { CoreMainMenuRoutingModule } from './mainmenu-routing.module';
|
||||
import { CoreMainMenuPage } from './pages/menu/menu.page';
|
||||
import { CoreMainMenuMorePage } from './pages/more/more.page';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
CoreMainMenuRoutingModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreMainMenuPage,
|
||||
CoreMainMenuMorePage,
|
||||
],
|
||||
})
|
||||
export class CoreMainMenuModule {}
|
|
@ -0,0 +1,27 @@
|
|||
<ion-tabs #mainTabs [hidden]="!showTabs">
|
||||
|
||||
<ion-tab-bar slot="bottom">
|
||||
<ion-spinner *ngIf="!loaded"></ion-spinner>
|
||||
|
||||
<ion-tab-button tab="redirect" [disabled]="true" [hidden]="true"></ion-tab-button> <!-- [root]="redirectPage" [rootParams]="redirectParams" -->
|
||||
|
||||
<ion-tab-button (ionTabButtonClick)="tabClicked($event, tab.page)" [hidden]="!loaded && tab.hide" *ngFor="let tab of tabs" [tab]="tab.page" [disabled]="tab.hide" layout="label-hide" class="{{tab.class}}">
|
||||
<core-icon [name]="tab.icon"></core-icon>
|
||||
<ion-label>{{ tab.title | translate }}</ion-label>
|
||||
<ion-badge *ngIf="tab.badge">{{ tab.badge }}</ion-badge>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button (ionTabButtonClick)="tabClicked($event, 'more')" [hidden]="!loaded" tab="more" layout="label-hide">
|
||||
<core-icon name="fa-bars"></core-icon>
|
||||
<ion-label>{{ 'core.more' | translate }}</ion-label>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
</ion-tabs>
|
||||
<div class="core-network-message" [hidden]="!showTabs">
|
||||
<div class="core-online-message">
|
||||
{{ "core.youreonline" | translate }}
|
||||
</div>
|
||||
<div class="core-offline-message">
|
||||
{{ "core.youreoffline" | translate }}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,255 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core';
|
||||
import { ActivatedRoute, Params, Router } from '@angular/router';
|
||||
import { NavController, IonTabs } from '@ionic/angular';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreEvents, CoreEventObserver, CoreEventLoadPageMainMenuData } from '@singletons/events';
|
||||
import { CoreMainMenu } from '../../services/mainmenu';
|
||||
import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from '../../services/delegate';
|
||||
import { CoreUtils } from '@/app/services/utils/utils';
|
||||
import { CoreDomUtils } from '@/app/services/utils/dom';
|
||||
import { Translate } from '@/app/singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Page that displays the main menu of the app.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'page-core-mainmenu',
|
||||
templateUrl: 'menu.html',
|
||||
styleUrls: ['menu.scss'],
|
||||
})
|
||||
export class CoreMainMenuPage implements OnInit, OnDestroy {
|
||||
|
||||
tabs: CoreMainMenuHandlerToDisplay[] = [];
|
||||
allHandlers?: CoreMainMenuHandlerToDisplay[];
|
||||
loaded = false;
|
||||
redirectPage?: string;
|
||||
redirectParams?: Params;
|
||||
showTabs = false;
|
||||
tabsPlacement = 'bottom';
|
||||
|
||||
protected subscription?: Subscription;
|
||||
protected redirectObs?: CoreEventObserver;
|
||||
protected pendingRedirect?: CoreEventLoadPageMainMenuData;
|
||||
protected urlToOpen?: string;
|
||||
protected mainMenuId: number;
|
||||
protected keyboardObserver?: CoreEventObserver;
|
||||
|
||||
@ViewChild('mainTabs') mainTabs?: IonTabs;
|
||||
|
||||
constructor(
|
||||
protected route: ActivatedRoute,
|
||||
protected navCtrl: NavController,
|
||||
protected menuDelegate: CoreMainMenuDelegate,
|
||||
protected changeDetector: ChangeDetectorRef,
|
||||
protected router: Router,
|
||||
) {
|
||||
this.mainMenuId = CoreApp.instance.getMainMenuId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the component.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
if (!CoreSites.instance.isLoggedIn()) {
|
||||
this.navCtrl.navigateRoot('/login/init');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.route.queryParams.subscribe(params => {
|
||||
const redirectPage = params['redirectPage'];
|
||||
if (redirectPage) {
|
||||
this.pendingRedirect = {
|
||||
redirectPage: redirectPage,
|
||||
redirectParams: params['redirectParams'],
|
||||
};
|
||||
}
|
||||
|
||||
this.urlToOpen = params['urlToOpen'];
|
||||
});
|
||||
|
||||
this.showTabs = true;
|
||||
|
||||
this.redirectObs = CoreEvents.on(CoreEvents.LOAD_PAGE_MAIN_MENU, (data: CoreEventLoadPageMainMenuData) => {
|
||||
if (!this.loaded) {
|
||||
// View isn't ready yet, wait for it to be ready.
|
||||
this.pendingRedirect = data;
|
||||
} else {
|
||||
delete this.pendingRedirect;
|
||||
this.handleRedirect(data);
|
||||
}
|
||||
});
|
||||
|
||||
this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
|
||||
// Remove the handlers that should only appear in the More menu.
|
||||
this.allHandlers = handlers.filter((handler) => !handler.onlyInMore);
|
||||
|
||||
this.initHandlers();
|
||||
|
||||
if (this.loaded && this.pendingRedirect) {
|
||||
// Wait for tabs to be initialized and then handle the redirect.
|
||||
setTimeout(() => {
|
||||
if (this.pendingRedirect) {
|
||||
this.handleRedirect(this.pendingRedirect);
|
||||
delete this.pendingRedirect;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('resize', this.initHandlers.bind(this));
|
||||
|
||||
if (CoreApp.instance.isIOS()) {
|
||||
// In iOS, the resize event is triggered before the keyboard is opened/closed and not triggered again once done.
|
||||
// Init handlers again once keyboard is closed since the resize event doesn't have the updated height.
|
||||
this.keyboardObserver = CoreEvents.on(CoreEvents.KEYBOARD_CHANGE, (kbHeight: number) => {
|
||||
if (kbHeight === 0) {
|
||||
this.initHandlers();
|
||||
|
||||
// If the device is slow it can take a bit more to update the window height. Retry in a few ms.
|
||||
setTimeout(() => {
|
||||
this.initHandlers();
|
||||
}, 250);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
CoreApp.instance.setMainMenuOpen(this.mainMenuId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init handlers on change (size or handlers).
|
||||
*/
|
||||
initHandlers(): void {
|
||||
if (this.allHandlers) {
|
||||
this.tabsPlacement = CoreMainMenu.instance.getTabPlacement();
|
||||
|
||||
const handlers = this.allHandlers.slice(0, CoreMainMenu.instance.getNumItems()); // Get main handlers.
|
||||
|
||||
// Re-build the list of tabs. If a handler is already in the list, use existing object to prevent re-creating the tab.
|
||||
const newTabs: CoreMainMenuHandlerToDisplay[] = [];
|
||||
|
||||
for (let i = 0; i < handlers.length; i++) {
|
||||
const handler = handlers[i];
|
||||
|
||||
// Check if the handler is already in the tabs list. If so, use it.
|
||||
const tab = this.tabs.find((tab) => tab.title == handler.title && tab.icon == handler.icon);
|
||||
|
||||
tab ? tab.hide = false : null;
|
||||
handler.hide = false;
|
||||
|
||||
newTabs.push(tab || handler);
|
||||
}
|
||||
|
||||
this.tabs = newTabs;
|
||||
|
||||
// Sort them by priority so new handlers are in the right position.
|
||||
this.tabs.sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
||||
|
||||
this.loaded = this.menuDelegate.areHandlersLoaded();
|
||||
|
||||
if (this.loaded && this.mainTabs && !this.mainTabs.getSelected()) {
|
||||
// Select the first tab.
|
||||
setTimeout(() => {
|
||||
this.mainTabs!.select(this.tabs[0]?.page || 'more');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.urlToOpen) {
|
||||
// There's a content link to open.
|
||||
// @todo: Treat URL.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a redirect.
|
||||
*
|
||||
* @param data Data received.
|
||||
*/
|
||||
protected handleRedirect(data: CoreEventLoadPageMainMenuData): void {
|
||||
// Check if the redirect page is the root page of any of the tabs.
|
||||
const i = this.tabs.findIndex((tab) => tab.page == data.redirectPage);
|
||||
|
||||
if (i >= 0) {
|
||||
// Tab found. Open it with the params.
|
||||
this.navCtrl.navigateForward(data.redirectPage, {
|
||||
queryParams: data.redirectParams,
|
||||
animated: false,
|
||||
});
|
||||
} else {
|
||||
// Tab not found, use a phantom tab.
|
||||
// @todo
|
||||
}
|
||||
|
||||
// Force change detection, otherwise sometimes the tab was selected before the params were applied.
|
||||
this.changeDetector.detectChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
this.subscription?.unsubscribe();
|
||||
this.redirectObs?.off();
|
||||
window.removeEventListener('resize', this.initHandlers.bind(this));
|
||||
CoreApp.instance.setMainMenuOpen(this.mainMenuId, false);
|
||||
this.keyboardObserver?.off();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tab clicked.
|
||||
*
|
||||
* @param e Event.
|
||||
* @param page Page of the tab.
|
||||
*/
|
||||
async tabClicked(e: Event, page: string): Promise<void> {
|
||||
if (this.mainTabs?.getSelected() != page) {
|
||||
// Just change the tab.
|
||||
return;
|
||||
}
|
||||
|
||||
// Current tab was clicked. Check if user is already at root level.
|
||||
if (this.router.url == '/mainmenu/' + page) {
|
||||
// Already at root level, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// Ask the user if he wants to go back to the root page of the tab.
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
try {
|
||||
const tab = this.tabs.find((tab) => tab.page == page);
|
||||
|
||||
if (tab?.title) {
|
||||
await CoreDomUtils.instance.showConfirm(Translate.instance.instant('core.confirmgotabroot', { name: tab.title }));
|
||||
} else {
|
||||
await CoreDomUtils.instance.showConfirm(Translate.instance.instant('core.confirmgotabrootdefault'));
|
||||
}
|
||||
|
||||
// User confirmed, go to root.
|
||||
this.mainTabs?.select(page);
|
||||
} catch (error) {
|
||||
// User canceled.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
ion-icon.tab-button-icon {
|
||||
text-overflow: unset;
|
||||
overflow: visible;
|
||||
text-align: center;
|
||||
transition: margin 500ms ease-in-out, transform 300ms ease-in-out;
|
||||
}
|
||||
|
||||
.ion-md-fa-graduation-cap,
|
||||
.ion-ios-fa-graduation-cap,
|
||||
.ion-ios-fa-graduation-cap-outline,
|
||||
.ion-fa-graduation-cap {
|
||||
// @todo @extend .fa-graduation-cap;
|
||||
// @todo @extend .fa;
|
||||
font-size: 21px;
|
||||
height: 21px;
|
||||
|
||||
}
|
||||
|
||||
.ion-ios-fa-graduation-cap-outline {
|
||||
color: transparent;
|
||||
-webkit-text-stroke-width: 0.8px;
|
||||
// @todo -webkit-text-stroke-color: $tabs-tab-color-inactive;
|
||||
font-size: 23px;
|
||||
height: 23px;
|
||||
}
|
||||
|
||||
.ion-md-fa-newspaper-o,
|
||||
.ion-ios-fa-newspaper-o,
|
||||
.ion-ios-fa-newspaper-o-outline,
|
||||
.ion-fa-newspaper-o {
|
||||
// @todo @extend .fa-newspaper-o;
|
||||
// @todo @extend .fa;
|
||||
font-size: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.ion-ios-fa-newspaper-o-outline {
|
||||
font-size: 23px;
|
||||
height: 23px;
|
||||
}
|
||||
|
||||
.core-network-message {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
visibility: hidden;
|
||||
height: 0;
|
||||
transition: all 500ms ease-in-out;
|
||||
opacity: .8;
|
||||
}
|
||||
|
||||
.core-online-message,
|
||||
.core-offline-message {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
.core-online ion-app.app-root page-core-mainmenu,
|
||||
.core-offline ion-app.app-root page-core-mainmenu {
|
||||
|
||||
core-ion-tabs[tabsplacement="bottom"] ion-icon.tab-button-icon {
|
||||
margin-bottom: $core-network-message-height / 2;
|
||||
|
||||
&.icon-ios {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.core-network-message {
|
||||
visibility: visible;
|
||||
height: $core-network-message-height;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.core-offline ion-app.app-root page-core-mainmenu .core-offline-message,
|
||||
.core-online ion-app.app-root page-core-mainmenu .core-online-message {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.core-online ion-app.app-root page-core-mainmenu .core-network-message {
|
||||
background: $green;
|
||||
}
|
||||
|
||||
.core-offline ion-app.app-root page-core-mainmenu .core-network-message {
|
||||
background: $red;
|
||||
}*/
|
|
@ -0,0 +1,84 @@
|
|||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
|
||||
</ion-buttons>
|
||||
|
||||
<ion-title><core-format-text [text]="siteName" contextLevel="system" [contextInstanceId]="0"></core-format-text></ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item *ngIf="siteInfo" class="ion-text-wrap"> <!-- @todo core-user-link [userId]="siteInfo.userid" -->
|
||||
<ion-avatar slot="start"></ion-avatar> <!-- @todo core-user-avatar [user]="siteInfo" -->
|
||||
<ion-label>
|
||||
<h2>{{siteInfo.fullname}}</h2>
|
||||
<p><core-format-text [text]="siteName" contextLevel="system" [contextInstanceId]="0" [wsNotFiltered]="true"></core-format-text></p>
|
||||
<p>{{ siteUrl }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-divider></ion-item-divider>
|
||||
<ion-item class="ion-text-center" *ngIf="(!handlers || !handlers.length) && !handlersLoaded">
|
||||
<ion-spinner></ion-spinner>
|
||||
</ion-item>
|
||||
<ion-item *ngFor="let handler of handlers" [ngClass]="['core-moremenu-handler', handler.class || '']" (click)="openHandler(handler)" title="{{ handler.title | translate }}" detail="true">
|
||||
<core-icon [name]="handler.icon" slot="start"></core-icon>
|
||||
<ion-label>
|
||||
<h2>{{ handler.title | translate}}</h2>
|
||||
</ion-label>
|
||||
<ion-badge slot="end" *ngIf="handler.showBadge" [hidden]="handler.loading || !handler.badge">{{handler.badge}}</ion-badge>
|
||||
<ion-spinner slot="end" *ngIf="handler.showBadge && handler.loading"></ion-spinner>
|
||||
</ion-item>
|
||||
<div *ngFor="let item of customItems" class="core-moremenu-customitem">
|
||||
<ion-item *ngIf="item.type != 'embedded'" [href]="item.url" title="{{item.label}}" core-link [capture]="item.type == 'app'" [inApp]="item.type == 'inappbrowser'">
|
||||
<core-icon [name]="item.icon" slot="start"></core-icon>
|
||||
<ion-label>
|
||||
<h2>{{item.label}}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="item.type == 'embedded'" (click)="openItem(item)" title="{{item.label}}">
|
||||
<core-icon [name]="item.icon" slot="start"></core-icon>
|
||||
<ion-label>
|
||||
<h2>{{item.label}}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</div>
|
||||
<ion-item *ngIf="showScanQR" (click)="scanQR()">
|
||||
<core-icon name="fa-qrcode" slot="start" aria-hidden="true"></core-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.scanqr' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="showWeb && siteInfo" [href]="siteInfo.siteurl" core-link autoLogin="yes" title="{{ 'core.mainmenu.website' | translate }}">
|
||||
<ion-icon name="globe" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.mainmenu.website' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item *ngIf="showHelp" [href]="docsUrl" core-link autoLogin="no" title="{{ 'core.mainmenu.help' | translate }}">
|
||||
<ion-icon name="help-buoy" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.mainmenu.help' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item (click)="openSitePreferences()" title="{{ 'core.settings.preferences' | translate }}">
|
||||
<core-icon name="fa-wrench" slot="start"></core-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.settings.preferences' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item (click)="logout()" title="{{ logoutLabel | translate }}">
|
||||
<ion-icon name="log-out" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<h2>{{ logoutLabel | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-divider></ion-item-divider>
|
||||
<ion-item (click)="openAppSettings()" title="{{ 'core.settings.appsettings' | translate }}">
|
||||
<core-icon name="fa-cogs" slot="start"></core-icon>
|
||||
<ion-label>
|
||||
<h2>{{ 'core.settings.appsettings' | translate }}</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
|
@ -0,0 +1,182 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreSiteInfo } from '@classes/site';
|
||||
import { CoreLoginHelper } from '@core/login/services/helper';
|
||||
import { CoreMainMenuDelegate, CoreMainMenuHandlerData } from '../../services/delegate';
|
||||
import { CoreMainMenu, CoreMainMenuCustomItem } from '../../services/mainmenu';
|
||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||
|
||||
/**
|
||||
* Page that displays the main menu of the app.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'page-core-mainmenu-more',
|
||||
templateUrl: 'more.html',
|
||||
styleUrls: ['more.scss'],
|
||||
})
|
||||
export class CoreMainMenuMorePage implements OnInit, OnDestroy {
|
||||
|
||||
handlers?: CoreMainMenuHandlerData[];
|
||||
allHandlers?: CoreMainMenuHandlerData[];
|
||||
handlersLoaded = false;
|
||||
siteInfo?: CoreSiteInfo;
|
||||
siteName?: string;
|
||||
logoutLabel = 'core.mainmenu.changesite';
|
||||
showScanQR: boolean;
|
||||
showWeb?: boolean;
|
||||
showHelp?: boolean;
|
||||
docsUrl?: string;
|
||||
customItems?: CoreMainMenuCustomItem[];
|
||||
siteUrl?: string;
|
||||
|
||||
protected subscription!: Subscription;
|
||||
protected langObserver: CoreEventObserver;
|
||||
protected updateSiteObserver: CoreEventObserver;
|
||||
|
||||
constructor(
|
||||
protected menuDelegate: CoreMainMenuDelegate,
|
||||
) {
|
||||
|
||||
this.langObserver = CoreEvents.on(CoreEvents.LANGUAGE_CHANGED, this.loadSiteInfo.bind(this));
|
||||
this.updateSiteObserver = CoreEvents.on(
|
||||
CoreEvents.SITE_UPDATED,
|
||||
this.loadSiteInfo.bind(this),
|
||||
CoreSites.instance.getCurrentSiteId(),
|
||||
);
|
||||
this.loadSiteInfo();
|
||||
this.showScanQR = CoreUtils.instance.canScanQR() &&
|
||||
!CoreSites.instance.getCurrentSite()?.isFeatureDisabled('CoreMainMenuDelegate_QrReader');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize component.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
// Load the handlers.
|
||||
this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
|
||||
this.allHandlers = handlers;
|
||||
|
||||
this.initHandlers();
|
||||
});
|
||||
|
||||
window.addEventListener('resize', this.initHandlers.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy(): void {
|
||||
window.removeEventListener('resize', this.initHandlers.bind(this));
|
||||
this.langObserver?.off();
|
||||
this.updateSiteObserver?.off();
|
||||
this.subscription?.unsubscribe();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init handlers on change (size or handlers).
|
||||
*/
|
||||
initHandlers(): void {
|
||||
if (!this.allHandlers) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the main handlers not to display them in this view.
|
||||
const mainHandlers = this.allHandlers
|
||||
.filter((handler) => !handler.onlyInMore)
|
||||
.slice(0, CoreMainMenu.instance.getNumItems());
|
||||
|
||||
// Get only the handlers that don't appear in the main view.
|
||||
this.handlers = this.allHandlers.filter((handler) => mainHandlers.indexOf(handler) == -1);
|
||||
|
||||
this.handlersLoaded = this.menuDelegate.areHandlersLoaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the site info required by the view.
|
||||
*/
|
||||
protected async loadSiteInfo(): Promise<void> {
|
||||
const currentSite = CoreSites.instance.getCurrentSite();
|
||||
|
||||
if (!currentSite) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.siteInfo = currentSite.getInfo();
|
||||
this.siteName = currentSite.getSiteName();
|
||||
this.siteUrl = currentSite.getURL();
|
||||
this.logoutLabel = CoreLoginHelper.instance.getLogoutLabel(currentSite);
|
||||
this.showWeb = !currentSite.isFeatureDisabled('CoreMainMenuDelegate_website');
|
||||
this.showHelp = !currentSite.isFeatureDisabled('CoreMainMenuDelegate_help');
|
||||
|
||||
this.docsUrl = await currentSite.getDocsUrl();
|
||||
|
||||
this.customItems = await CoreMainMenu.instance.getCustomMenuItems();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a handler.
|
||||
*
|
||||
* @param handler Handler to open.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
openHandler(handler: CoreMainMenuHandlerData): void {
|
||||
// @todo
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an embedded custom item.
|
||||
*
|
||||
* @param item Item to open.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
openItem(item: CoreMainMenuCustomItem): void {
|
||||
// @todo
|
||||
}
|
||||
|
||||
/**
|
||||
* Open app settings page.
|
||||
*/
|
||||
openAppSettings(): void {
|
||||
// @todo
|
||||
}
|
||||
|
||||
/**
|
||||
* Open site settings page.
|
||||
*/
|
||||
openSitePreferences(): void {
|
||||
// @todo
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan and treat a QR code.
|
||||
*/
|
||||
async scanQR(): Promise<void> {
|
||||
// Scan for a QR code.
|
||||
// @todo
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout the user.
|
||||
*/
|
||||
logout(): void {
|
||||
CoreSites.instance.logout();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
$core-more-icon: $gray-darker !default;
|
||||
$core-more-background-ios: $list-ios-background-color !default;
|
||||
$core-more-background-md: $list-md-background-color !default;
|
||||
$core-more-activated-background-ios: color-shade($core-more-background-ios) !default;
|
||||
$core-more-activated-background-md: color-shade($core-more-background-md) !default;
|
||||
$core-more-divider-ios: $item-ios-divider-background !default;
|
||||
$core-more-divider-md: $item-md-divider-background !default;
|
||||
$core-more-border-ios: $list-ios-border-color !default;
|
||||
$core-more-border-md: $list-md-border-color !default;
|
||||
$core-more-color-ios: $list-ios-text-color!default;
|
||||
$core-more-color-md: $list-md-text-color !default;
|
||||
|
||||
.item-block {
|
||||
&.item-ios {
|
||||
background-color: $core-more-background-ios;
|
||||
color: $core-more-color-ios;
|
||||
p {
|
||||
color: $core-more-color-ios;
|
||||
}
|
||||
|
||||
.item-inner {
|
||||
border-bottom: $hairlines-width solid $core-more-border-ios;
|
||||
}
|
||||
}
|
||||
&.item-md {
|
||||
background-color: $core-more-background-md;
|
||||
color: $core-more-color-md;
|
||||
p {
|
||||
color: $core-more-color-md;
|
||||
}
|
||||
|
||||
.item-inner {
|
||||
border-bottom: 1px solid $core-more-border-md;
|
||||
}
|
||||
}
|
||||
|
||||
&.activated {
|
||||
&.item-ios {
|
||||
background-color: $core-more-activated-background-ios;
|
||||
}
|
||||
&.item-md {
|
||||
background-color: $core-more-activated-background-md;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ion-icon {
|
||||
color: $core-more-icon;
|
||||
}
|
||||
|
||||
.item-divider {
|
||||
&.item-ios {
|
||||
background-color: $core-more-divider-ios;
|
||||
}
|
||||
|
||||
&.item-md {
|
||||
background-color: $core-more-divider-md;
|
||||
border-bottom: $core-more-border-md;
|
||||
}
|
||||
}
|
||||
|
||||
@include darkmode() {
|
||||
ion-icon {
|
||||
color: $core-dark-text-color;
|
||||
}
|
||||
|
||||
.item-divider {
|
||||
&.item-ios,
|
||||
&.item-md {
|
||||
color: $core-dark-text-color;
|
||||
background-color: $core-dark-item-divider-bg-color;
|
||||
}
|
||||
}
|
||||
|
||||
.item-block {
|
||||
&.item-ios,
|
||||
&.item-md {
|
||||
color: $core-dark-text-color;
|
||||
background-color: $core-dark-item-bg-color;
|
||||
p {
|
||||
color: $core-dark-text-color;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.activated {
|
||||
&.item-ios {
|
||||
background-color: $core-more-activated-background-ios;
|
||||
}
|
||||
&.item-md {
|
||||
background-color: $core-more-activated-background-md;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -0,0 +1,177 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Params } from '@angular/router';
|
||||
import { Subject, BehaviorSubject } from 'rxjs';
|
||||
|
||||
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
|
||||
/**
|
||||
* Interface that all main menu handlers must implement.
|
||||
*/
|
||||
export interface CoreMainMenuHandler extends CoreDelegateHandler {
|
||||
/**
|
||||
* The highest priority is displayed first.
|
||||
*/
|
||||
priority?: number;
|
||||
|
||||
/**
|
||||
* Returns the data needed to render the handler.
|
||||
*
|
||||
* @return Data.
|
||||
*/
|
||||
getDisplayData(): CoreMainMenuHandlerData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data needed to render a main menu handler. It's returned by the handler.
|
||||
*/
|
||||
export interface CoreMainMenuHandlerData {
|
||||
/**
|
||||
* Name of the page to load for the handler.
|
||||
*/
|
||||
page: string;
|
||||
|
||||
/**
|
||||
* Title to display for the handler.
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Name of the icon to display for the handler.
|
||||
*/
|
||||
icon: string; // Name of the icon to display in the tab.
|
||||
|
||||
/**
|
||||
* Class to add to the displayed handler.
|
||||
*/
|
||||
class?: string;
|
||||
|
||||
/**
|
||||
* If the handler has badge to show or not.
|
||||
*/
|
||||
showBadge?: boolean;
|
||||
|
||||
/**
|
||||
* Text to display on the badge. Only used if showBadge is true.
|
||||
*/
|
||||
badge?: string;
|
||||
|
||||
/**
|
||||
* If true, the badge number is being loaded. Only used if showBadge is true.
|
||||
*/
|
||||
loading?: boolean;
|
||||
|
||||
/**
|
||||
* Params to pass to the page.
|
||||
*/
|
||||
pageParams?: Params;
|
||||
|
||||
/**
|
||||
* Whether the handler should only appear in More menu.
|
||||
*/
|
||||
onlyInMore?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data returned by the delegate for each handler.
|
||||
*/
|
||||
export interface CoreMainMenuHandlerToDisplay extends CoreMainMenuHandlerData {
|
||||
/**
|
||||
* Name of the handler.
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* Priority of the handler.
|
||||
*/
|
||||
priority?: number;
|
||||
|
||||
/**
|
||||
* Hide tab. Used then resizing.
|
||||
*/
|
||||
hide?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service to interact with plugins to be shown in the main menu. Provides functions to register a plugin
|
||||
* and notify an update in the data.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CoreMainMenuDelegate extends CoreDelegate {
|
||||
|
||||
protected loaded = false;
|
||||
protected siteHandlers: Subject<CoreMainMenuHandlerToDisplay[]> = new BehaviorSubject<CoreMainMenuHandlerToDisplay[]>([]);
|
||||
protected featurePrefix = 'CoreMainMenuDelegate_';
|
||||
|
||||
constructor() {
|
||||
super('CoreMainMenuDelegate', true);
|
||||
|
||||
CoreEvents.on(CoreEvents.LOGOUT, this.clearSiteHandlers.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if handlers are loaded.
|
||||
*
|
||||
* @return True if handlers are loaded, false otherwise.
|
||||
*/
|
||||
areHandlersLoaded(): boolean {
|
||||
return this.loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear current site handlers. Reserved for core use.
|
||||
*/
|
||||
protected clearSiteHandlers(): void {
|
||||
this.loaded = false;
|
||||
this.siteHandlers.next([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the handlers for the current site.
|
||||
*
|
||||
* @return An observable that will receive the handlers.
|
||||
*/
|
||||
getHandlers(): Subject<CoreMainMenuHandlerToDisplay[]> {
|
||||
return this.siteHandlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update handlers Data.
|
||||
*/
|
||||
updateData(): void {
|
||||
const displayData: CoreMainMenuHandlerToDisplay[] = [];
|
||||
|
||||
for (const name in this.enabledHandlers) {
|
||||
const handler = <CoreMainMenuHandler> this.enabledHandlers[name];
|
||||
const data = <CoreMainMenuHandlerToDisplay> handler.getDisplayData();
|
||||
|
||||
data.name = name;
|
||||
data.priority = handler.priority;
|
||||
|
||||
displayData.push(data);
|
||||
}
|
||||
|
||||
// Sort them by priority.
|
||||
displayData.sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
||||
|
||||
this.loaded = true;
|
||||
this.siteHandlers.next(displayData);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,275 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { CoreApp } from '@services/app';
|
||||
import { CoreLang } from '@services/lang';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreConstants } from '@core/constants';
|
||||
import { CoreMainMenuDelegate, CoreMainMenuHandlerToDisplay } from './delegate';
|
||||
import { makeSingleton } from '@singletons/core.singletons';
|
||||
|
||||
/**
|
||||
* Service that provides some features regarding Main Menu.
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class CoreMainMenuProvider {
|
||||
|
||||
static readonly NUM_MAIN_HANDLERS = 4;
|
||||
static readonly ITEM_MIN_WIDTH = 72; // Min with of every item, based on 5 items on a 360 pixel wide screen.
|
||||
|
||||
protected tablet = false;
|
||||
|
||||
constructor(protected menuDelegate: CoreMainMenuDelegate) {
|
||||
this.tablet = !!(window?.innerWidth && window.innerWidth >= 576 && window.innerHeight >= 576);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current main menu handlers.
|
||||
*
|
||||
* @return Promise resolved with the current main menu handlers.
|
||||
*/
|
||||
getCurrentMainMenuHandlers(): Promise<CoreMainMenuHandlerToDisplay[]> {
|
||||
const deferred = CoreUtils.instance.promiseDefer<CoreMainMenuHandlerToDisplay[]>();
|
||||
|
||||
const subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
|
||||
subscription?.unsubscribe();
|
||||
|
||||
// Remove the handlers that should only appear in the More menu.
|
||||
handlers = handlers.filter(handler => !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.
|
||||
*
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return List of custom menu items.
|
||||
*/
|
||||
async getCustomMenuItems(siteId?: string): Promise<CoreMainMenuCustomItem[]> {
|
||||
const site = await CoreSites.instance.getSite(siteId);
|
||||
|
||||
const itemsString = site.getStoredConfig('tool_mobile_custommenuitems');
|
||||
const map: CustomMenuItemsMap = {};
|
||||
const result: CoreMainMenuCustomItem[] = [];
|
||||
|
||||
let position = 0; // Position of each item, to keep the same order as it's configured.
|
||||
|
||||
if (!itemsString || typeof itemsString != 'string') {
|
||||
// Setting not valid.
|
||||
return result;
|
||||
}
|
||||
|
||||
// Add items to the map.
|
||||
const items = itemsString.split(/(?:\r\n|\r|\n)/);
|
||||
items.forEach((item) => {
|
||||
const values = item.split('|');
|
||||
const label = values[0] ? values[0].trim() : values[0];
|
||||
const url = values[1] ? values[1].trim() : values[1];
|
||||
const type = values[2] ? values[2].trim() : values[2];
|
||||
const lang = (values[3] ? values[3].trim() : values[3]) || 'none';
|
||||
let icon = values[4] ? values[4].trim() : values[4];
|
||||
|
||||
if (!label || !url || !type) {
|
||||
// Invalid item, ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
const id = url + '#' + type;
|
||||
if (!icon) {
|
||||
// Icon not defined, use default one.
|
||||
icon = type == 'embedded' ? 'fa-square-o' : 'fa-link'; // @todo: Find a better icon for embedded.
|
||||
}
|
||||
|
||||
if (!map[id]) {
|
||||
// New entry, add it to the map.
|
||||
map[id] = {
|
||||
url: url,
|
||||
type: type,
|
||||
position: position,
|
||||
labels: {},
|
||||
};
|
||||
position++;
|
||||
}
|
||||
|
||||
map[id].labels[lang.toLowerCase()] = {
|
||||
label: label,
|
||||
icon: icon,
|
||||
};
|
||||
});
|
||||
|
||||
if (!position) {
|
||||
// No valid items found, stop.
|
||||
return result;
|
||||
}
|
||||
|
||||
const currentLang = await CoreLang.instance.getCurrentLanguage();
|
||||
|
||||
const fallbackLang = CoreConstants.CONFIG.default_lang || 'en';
|
||||
|
||||
// Get the right label for each entry and add it to the result.
|
||||
for (const id in map) {
|
||||
const entry = map[id];
|
||||
let data = entry.labels[currentLang] || entry.labels[currentLang + '_only'] ||
|
||||
entry.labels.none || entry.labels[fallbackLang];
|
||||
|
||||
if (!data) {
|
||||
// No valid label found, get the first one that is not "_only".
|
||||
for (const lang in entry.labels) {
|
||||
if (lang.indexOf('_only') == -1) {
|
||||
data = entry.labels[lang];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
// No valid label, ignore this entry.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result[entry.position] = {
|
||||
url: entry.url,
|
||||
type: entry.type,
|
||||
label: data.label,
|
||||
icon: data.icon,
|
||||
};
|
||||
}
|
||||
|
||||
// Remove undefined values.
|
||||
return result.filter((entry) => typeof entry != 'undefined');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of items to be shown on the main menu bar.
|
||||
*
|
||||
* @return Number of items depending on the device width.
|
||||
*/
|
||||
getNumItems(): number {
|
||||
if (!this.isResponsiveMainMenuItemsDisabledInCurrentSite() && window && window.innerWidth) {
|
||||
let numElements: number;
|
||||
|
||||
if (this.tablet) {
|
||||
// Tablet, menu will be displayed vertically.
|
||||
numElements = Math.floor(window.innerHeight / CoreMainMenuProvider.ITEM_MIN_WIDTH);
|
||||
} else {
|
||||
numElements = Math.floor(window.innerWidth / CoreMainMenuProvider.ITEM_MIN_WIDTH);
|
||||
|
||||
// Set a maximum elements to show and skip more button.
|
||||
numElements = numElements >= 5 ? 5 : numElements;
|
||||
}
|
||||
|
||||
// Set a mínimum elements to show and skip more button.
|
||||
return numElements > 1 ? numElements - 1 : 1;
|
||||
}
|
||||
|
||||
return CoreMainMenuProvider.NUM_MAIN_HANDLERS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tabs placement depending on the device size.
|
||||
*
|
||||
* @return Tabs placement including side value.
|
||||
*/
|
||||
getTabPlacement(): string {
|
||||
const tablet = !!(window.innerWidth && window.innerWidth >= 576 && (window.innerHeight >= 576 ||
|
||||
((CoreApp.instance.isKeyboardVisible() || CoreApp.instance.isKeyboardOpening()) && window.innerHeight >= 200)));
|
||||
|
||||
if (tablet != this.tablet) {
|
||||
this.tablet = tablet;
|
||||
|
||||
// @todo Resize so content margins can be updated.
|
||||
}
|
||||
|
||||
return tablet ? 'side' : 'bottom';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a certain page is the root of a main menu handler currently displayed.
|
||||
*
|
||||
* @param page Name of the page.
|
||||
* @param pageParams Page params.
|
||||
* @return Promise resolved with boolean: whether it's the root of a main menu handler.
|
||||
*/
|
||||
async isCurrentMainMenuHandler(pageName: string): Promise<boolean> {
|
||||
const handlers = await this.getCurrentMainMenuHandlers();
|
||||
|
||||
const handler = handlers.find((handler) => handler.page == pageName);
|
||||
|
||||
return !!handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if responsive main menu items is disabled in the current site.
|
||||
*
|
||||
* @return Whether it's disabled.
|
||||
*/
|
||||
protected isResponsiveMainMenuItemsDisabledInCurrentSite(): boolean {
|
||||
const site = CoreSites.instance.getCurrentSite();
|
||||
|
||||
return !!site?.isFeatureDisabled('NoDelegate_ResponsiveMainMenuItems');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class CoreMainMenu extends makeSingleton(CoreMainMenuProvider) {}
|
||||
|
||||
/**
|
||||
* Custom main menu item.
|
||||
*/
|
||||
export interface CoreMainMenuCustomItem {
|
||||
/**
|
||||
* Type of the item: app, inappbrowser, browser or embedded.
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* Url of the item.
|
||||
*/
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* Label to display for the item.
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* Name of the icon to display for the item.
|
||||
*/
|
||||
icon: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map of custom menu items.
|
||||
*/
|
||||
type CustomMenuItemsMap = Record<string, {
|
||||
url: string;
|
||||
type: string;
|
||||
position: number;
|
||||
labels: {
|
||||
[lang: string]: {
|
||||
label: string;
|
||||
icon: string;
|
||||
};
|
||||
};
|
||||
}>;
|
|
@ -0,0 +1,47 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
|
||||
import { CoreSettingsAboutPage } from './about.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreSettingsAboutPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreSettingsAboutPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreSettingsAboutPageModule {}
|
|
@ -0,0 +1,47 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
|
||||
import { CoreSettingsAppPage } from './app.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreSettingsAppPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreSettingsAppPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreSettingsAppPageModule {}
|
|
@ -20,7 +20,7 @@ import { ActivatedRoute, Params, Router } from '@angular/router';
|
|||
selector: 'app-settings',
|
||||
templateUrl: 'app.html',
|
||||
})
|
||||
export class CoreAppSettingsPage {
|
||||
export class CoreSettingsAppPage {
|
||||
|
||||
// @ViewChild(CoreSplitViewComponent) splitviewCtrl?: CoreSplitViewComponent;
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
|
||||
import { CoreSettingsDeviceInfoPage } from './deviceinfo.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: CoreSettingsDeviceInfoPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
RouterModule.forChild(routes),
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
],
|
||||
declarations: [
|
||||
CoreSettingsDeviceInfoPage,
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreSettingsDeviceInfoPageModule {}
|
|
@ -14,22 +14,19 @@
|
|||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { CoreAppSettingsPage } from './pages/app/app.page';
|
||||
import { CoreSettingsAboutPage } from './pages/about/about.page';
|
||||
import { CoreSettingsDeviceInfoPage } from './pages/deviceinfo/deviceinfo.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: 'about',
|
||||
component: CoreSettingsAboutPage,
|
||||
loadChildren: () => import('./pages/about/about.page.module').then( m => m.CoreSettingsAboutPageModule),
|
||||
},
|
||||
{
|
||||
path: 'deviceinfo',
|
||||
component: CoreSettingsDeviceInfoPage,
|
||||
loadChildren: () => import('./pages/deviceinfo/deviceinfo.page.module').then( m => m.CoreSettingsDeviceInfoPageModule),
|
||||
},
|
||||
{
|
||||
path: 'app',
|
||||
component: CoreAppSettingsPage,
|
||||
loadChildren: () => import('./pages/app/app.page.module').then( m => m.CoreSettingsAppPageModule),
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
|
@ -42,4 +39,4 @@ const routes: Routes = [
|
|||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class CoreAppSettingsRoutingModule {}
|
||||
export class CoreSettingsRoutingModule {}
|
||||
|
|
|
@ -13,34 +13,13 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
import { CoreAppSettingsRoutingModule } from './settings-routing.module';
|
||||
import { CorePipesModule } from '@pipes/pipes.module';
|
||||
import { CoreComponentsModule } from '@components/components.module';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
|
||||
import { CoreAppSettingsPage } from './pages/app/app.page';
|
||||
import { CoreSettingsAboutPage } from './pages/about/about.page';
|
||||
import { CoreSettingsDeviceInfoPage } from './pages/deviceinfo/deviceinfo.page';
|
||||
import { CoreSettingsRoutingModule } from './settings-routing.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
CoreAppSettingsRoutingModule,
|
||||
CorePipesModule,
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
TranslateModule.forChild(),
|
||||
],
|
||||
declarations: [
|
||||
CoreAppSettingsPage,
|
||||
CoreSettingsAboutPage,
|
||||
CoreSettingsDeviceInfoPage,
|
||||
CoreSettingsRoutingModule,
|
||||
],
|
||||
declarations: [],
|
||||
})
|
||||
export class CoreAppSettingsPageModule {}
|
||||
export class CoreSettingsModule {}
|
||||
|
|
|
@ -16,16 +16,18 @@ import { NgModule } from '@angular/core';
|
|||
|
||||
import { CoreAutoFocusDirective } from './auto-focus';
|
||||
import { CoreExternalContentDirective } from './external-content';
|
||||
import { CoreFabDirective } from './fab';
|
||||
import { CoreFormatTextDirective } from './format-text';
|
||||
import { CoreLinkDirective } from './link';
|
||||
import { CoreLongPressDirective } from './long-press';
|
||||
import { CoreSupressEventsDirective } from './supress-events';
|
||||
import { CoreFabDirective } from './fab';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreAutoFocusDirective,
|
||||
CoreExternalContentDirective,
|
||||
CoreFormatTextDirective,
|
||||
CoreLinkDirective,
|
||||
CoreLongPressDirective,
|
||||
CoreSupressEventsDirective,
|
||||
CoreFabDirective,
|
||||
|
@ -35,6 +37,7 @@ import { CoreFabDirective } from './fab';
|
|||
CoreAutoFocusDirective,
|
||||
CoreExternalContentDirective,
|
||||
CoreFormatTextDirective,
|
||||
CoreLinkDirective,
|
||||
CoreLongPressDirective,
|
||||
CoreSupressEventsDirective,
|
||||
CoreFabDirective,
|
||||
|
|
|
@ -24,6 +24,7 @@ import { CoreUtils } from '@services/utils/utils';
|
|||
import { CoreSite } from '@classes/site';
|
||||
import { Translate } from '@singletons/core.singletons';
|
||||
import { CoreExternalContentDirective } from './external-content';
|
||||
import { CoreLinkDirective } from './link';
|
||||
|
||||
/**
|
||||
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
|
||||
|
@ -454,7 +455,9 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
// Important: We need to look for links first because in 'img' we add new links without core-link.
|
||||
anchors.forEach((anchor) => {
|
||||
// Angular 2 doesn't let adding directives dynamically. Create the CoreLinkDirective manually.
|
||||
// @todo
|
||||
const linkDir = new CoreLinkDirective(new ElementRef(anchor), this.content);
|
||||
linkDir.capture = true;
|
||||
linkDir.ngOnInit();
|
||||
|
||||
this.addExternalContent(anchor);
|
||||
});
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core';
|
||||
import { IonContent } from '@ionic/angular';
|
||||
|
||||
import { CoreFileHelper } from '@services/file-helper';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUrlUtils } from '@services/utils/url';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreTextUtils } from '@services/utils/text';
|
||||
import { CoreConstants } from '@core/constants';
|
||||
|
||||
/**
|
||||
* Directive to open a link in external browser or in the app.
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[core-link]',
|
||||
})
|
||||
export class CoreLinkDirective implements OnInit {
|
||||
|
||||
@Input() capture?: boolean | string; // If the link needs to be captured by the app.
|
||||
@Input() inApp?: boolean | string; // True to open in embedded browser, false to open in system browser.
|
||||
/* Whether the link should be opened with auto-login. Accepts the following values:
|
||||
"yes" -> Always auto-login.
|
||||
"no" -> Never auto-login.
|
||||
"check" -> Auto-login only if it points to the current site. Default value. */
|
||||
@Input() autoLogin = 'check';
|
||||
|
||||
protected element: Element;
|
||||
|
||||
constructor(
|
||||
element: ElementRef,
|
||||
@Optional() protected content: IonContent,
|
||||
) {
|
||||
this.element = element.nativeElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function executed when the component is initialized.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.inApp = typeof this.inApp == 'undefined' ? this.inApp : CoreUtils.instance.isTrueOrOne(this.inApp);
|
||||
|
||||
// @todo: Handle split view?
|
||||
|
||||
this.element.addEventListener('click', (event) => {
|
||||
if (event.defaultPrevented) {
|
||||
return; // Link already treated, stop.
|
||||
}
|
||||
|
||||
let href = this.element.getAttribute('href') || this.element.getAttribute('ng-reflect-href') ||
|
||||
this.element.getAttribute('xlink:href');
|
||||
|
||||
if (!href || CoreUrlUtils.instance.getUrlScheme(href) == 'javascript') {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const openIn = this.element.getAttribute('data-open-in');
|
||||
|
||||
if (CoreUtils.instance.isTrueOrOne(this.capture)) {
|
||||
href = CoreTextUtils.instance.decodeURI(href);
|
||||
|
||||
// @todo: Handle link.
|
||||
this.navigate(href, openIn);
|
||||
} else {
|
||||
this.navigate(href, openIn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to correctly navigate, open file or url in the browser.
|
||||
*
|
||||
* @param href HREF to be opened.
|
||||
* @param openIn Open In App value coming from data-open-in attribute.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async navigate(href: string, openIn?: string | null): Promise<void> {
|
||||
|
||||
if (CoreUrlUtils.instance.isLocalFileUrl(href)) {
|
||||
return this.openLocalFile(href);
|
||||
}
|
||||
|
||||
if (href.charAt(0) == '#') {
|
||||
// Look for id or name.
|
||||
href = href.substr(1);
|
||||
CoreDomUtils.instance.scrollToElementBySelector(this.content, '#' + href + ', [name=\'' + href + '\']');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo: Custom URL schemes.
|
||||
|
||||
return this.openExternalLink(href, openIn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a local file.
|
||||
*
|
||||
* @param path Path to the file.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async openLocalFile(path: string): Promise<void> {
|
||||
const filename = path.substr(path.lastIndexOf('/') + 1);
|
||||
|
||||
if (!CoreFileHelper.instance.isOpenableInApp({ filename })) {
|
||||
try {
|
||||
await CoreFileHelper.instance.showConfirmOpenUnsupportedFile();
|
||||
} catch (error) {
|
||||
return; // Cancelled, stop.
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await CoreUtils.instance.openFile(path);
|
||||
} catch (error) {
|
||||
CoreDomUtils.instance.showErrorModal(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an external link in the app or in browser.
|
||||
*
|
||||
* @param href HREF to be opened.
|
||||
* @param openIn Open In App value coming from data-open-in attribute.
|
||||
* @return Promise resolved when done.
|
||||
*/
|
||||
protected async openExternalLink(href: string, openIn?: string | null): Promise<void> {
|
||||
// It's an external link, we will open with browser. Check if we need to auto-login.
|
||||
if (!CoreSites.instance.isLoggedIn()) {
|
||||
// Not logged in, cannot auto-login.
|
||||
if (this.inApp) {
|
||||
CoreUtils.instance.openInApp(href);
|
||||
} else {
|
||||
CoreUtils.instance.openInBrowser(href);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if URL does not have any protocol, so it's a relative URL.
|
||||
if (!CoreUrlUtils.instance.isAbsoluteURL(href)) {
|
||||
// Add the site URL at the begining.
|
||||
if (href.charAt(0) == '/') {
|
||||
href = CoreSites.instance.getCurrentSite()!.getURL() + href;
|
||||
} else {
|
||||
href = CoreSites.instance.getCurrentSite()!.getURL() + '/' + href;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.autoLogin == 'yes') {
|
||||
if (this.inApp) {
|
||||
await CoreSites.instance.getCurrentSite()!.openInAppWithAutoLogin(href);
|
||||
} else {
|
||||
await CoreSites.instance.getCurrentSite()!.openInBrowserWithAutoLogin(href);
|
||||
}
|
||||
} else if (this.autoLogin == 'no') {
|
||||
if (this.inApp) {
|
||||
CoreUtils.instance.openInApp(href);
|
||||
} else {
|
||||
CoreUtils.instance.openInBrowser(href);
|
||||
}
|
||||
} else {
|
||||
// Priority order is: core-link inApp attribute > forceOpenLinksIn setting > data-open-in HTML attribute.
|
||||
let openInApp = this.inApp;
|
||||
if (typeof this.inApp == 'undefined') {
|
||||
if (CoreConstants.CONFIG.forceOpenLinksIn == 'browser') {
|
||||
openInApp = false;
|
||||
} else if (CoreConstants.CONFIG.forceOpenLinksIn == 'app' || openIn == 'app') {
|
||||
openInApp = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (openInApp) {
|
||||
await CoreSites.instance.getCurrentSite()!.openInAppWithAutoLoginIfSameSite(href);
|
||||
} else {
|
||||
await CoreSites.instance.getCurrentSite()!.openInBrowserWithAutoLoginIfSameSite(href);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1312,7 +1312,7 @@ export class CoreSitesProvider {
|
|||
async logout(): Promise<void> {
|
||||
await this.dbReady;
|
||||
|
||||
let siteId;
|
||||
let siteId: string | undefined;
|
||||
const promises: Promise<unknown>[] = [];
|
||||
|
||||
if (this.currentSite) {
|
||||
|
|
|
@ -208,3 +208,11 @@ export type CoreEventLoadingChangedData = {
|
|||
loaded: boolean;
|
||||
uniqueId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data passed to LOAD_PAGE_MAIN_MENU event.
|
||||
*/
|
||||
export type CoreEventLoadPageMainMenuData = {
|
||||
redirectPage: string;
|
||||
redirectParams?: Params;
|
||||
};
|
||||
|
|
|
@ -303,7 +303,48 @@
|
|||
"core.back": "Back",
|
||||
"core.browser": "Browser",
|
||||
"core.copiedtoclipboard": "Text copied to clipboard",
|
||||
"core.courses.addtofavourites": "Star this course",
|
||||
"core.courses.allowguests": "This course allows guest users to enter",
|
||||
"core.courses.availablecourses": "Available courses",
|
||||
"core.courses.cannotretrievemorecategories": "Categories deeper than level {{$a}} cannot be retrieved.",
|
||||
"core.courses.categories": "Course categories",
|
||||
"core.courses.confirmselfenrol": "Are you sure you want to enrol yourself in this course?",
|
||||
"core.courses.courses": "Courses",
|
||||
"core.courses.downloadcourses": "Download courses",
|
||||
"core.courses.enrolme": "Enrol me",
|
||||
"core.courses.errorloadcategories": "An error occurred while loading categories.",
|
||||
"core.courses.errorloadcourses": "An error occurred while loading courses.",
|
||||
"core.courses.errorloadplugins": "The plugins required by this course could not be loaded correctly. Please reload the app to try again.",
|
||||
"core.courses.errorsearching": "An error occurred while searching.",
|
||||
"core.courses.errorselfenrol": "An error occurred while self enrolling.",
|
||||
"core.courses.filtermycourses": "Filter my courses",
|
||||
"core.courses.frontpage": "Front page",
|
||||
"core.courses.hidecourse": "Remove from view",
|
||||
"core.courses.ignore": "Ignore",
|
||||
"core.courses.mycourses": "My courses",
|
||||
"core.courses.mymoodle": "Dashboard",
|
||||
"core.courses.nocourses": "No course information to show.",
|
||||
"core.courses.nocoursesyet": "No courses in this category",
|
||||
"core.courses.nosearchresults": "No results",
|
||||
"core.courses.notenroled": "You are not enrolled in this course",
|
||||
"core.courses.notenrollable": "You cannot enrol yourself in this course.",
|
||||
"core.courses.password": "Enrolment key",
|
||||
"core.courses.paymentrequired": "This course requires a payment for entry.",
|
||||
"core.courses.paypalaccepted": "PayPal payments accepted",
|
||||
"core.courses.reload": "Reload",
|
||||
"core.courses.removefromfavourites": "Unstar this course",
|
||||
"core.courses.search": "Search",
|
||||
"core.courses.searchcourses": "Search courses",
|
||||
"core.courses.searchcoursesadvice": "You can use the search courses button to find courses to access as a guest or enrol yourself in courses that allow it.",
|
||||
"core.courses.selfenrolment": "Self enrolment",
|
||||
"core.courses.sendpaymentbutton": "Send payment via PayPal",
|
||||
"core.courses.show": "Restore to view",
|
||||
"core.courses.totalcoursesearchresults": "Total courses: {{$a}}",
|
||||
"core.login.yourenteredsite": "Connect to your site",
|
||||
"core.mainmenu.changesite": "Change site",
|
||||
"core.mainmenu.help": "Help",
|
||||
"core.mainmenu.logout": "Log out",
|
||||
"core.mainmenu.website": "Website",
|
||||
"core.no": "No",
|
||||
"core.offline": "Offline",
|
||||
"core.ok": "OK",
|
||||
|
|
|
@ -66,6 +66,7 @@ declare global {
|
|||
appstores: Record<string, string>;
|
||||
displayqroncredentialscreen?: boolean;
|
||||
displayqronsitescreen?: boolean;
|
||||
forceOpenLinksIn: 'app' | 'browser';
|
||||
};
|
||||
|
||||
BUILD: {
|
||||
|
|
Loading…
Reference in New Issue