MOBILE-2302 mainmenu: Implement More page
parent
704e3796a0
commit
c950d7dd40
|
@ -853,10 +853,10 @@ export class CoreSite {
|
|||
/**
|
||||
* Returns the URL to the documentation of the app, based on Moodle version and current language.
|
||||
*
|
||||
* @param {string} [page] Docs page to go to.
|
||||
* @return {Promise} Promise resolved with the Moodle docs URL.
|
||||
* @param {string} [page] Docs page to go to.
|
||||
* @return {Promise<string>} Promise resolved with the Moodle docs URL.
|
||||
*/
|
||||
getDocsUrl(page: string) : Promise<string> {
|
||||
getDocsUrl(page?: string) : Promise<string> {
|
||||
const release = this.infos.release ? this.infos.release : undefined;
|
||||
return this.urlUtils.getDocsUrl(release, page);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CoreMainMenuDelegate } from './providers/delegate';
|
||||
import { CoreMainMenuProvider } from './providers/mainmenu';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -22,6 +23,7 @@ import { CoreMainMenuDelegate } from './providers/delegate';
|
|||
],
|
||||
providers: [
|
||||
CoreMainMenuDelegate,
|
||||
CoreMainMenuProvider
|
||||
]
|
||||
})
|
||||
export class CoreMainMenuModule {}
|
||||
|
|
|
@ -16,6 +16,7 @@ import { Component, OnDestroy } from '@angular/core';
|
|||
import { IonicPage, NavController } from 'ionic-angular';
|
||||
import { CoreEventsProvider } from '../../../../providers/events';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreMainMenuProvider } from '../../providers/mainmenu';
|
||||
import { CoreMainMenuDelegate, CoreMainMenuHandlerData } from '../../providers/delegate';
|
||||
|
||||
/**
|
||||
|
@ -56,7 +57,7 @@ export class CoreMainMenuPage implements OnDestroy {
|
|||
}
|
||||
|
||||
this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
|
||||
this.tabs = handlers.slice(0, 4); // Get first 4.
|
||||
this.tabs = handlers.slice(0, CoreMainMenuProvider.NUM_MAIN_HANDLERS); // Get main handlers.
|
||||
this.tabs.push(this.moreTabData); // Add "More" tab.
|
||||
this.loaded = this.menuDelegate.areHandlersLoaded();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ siteInfo.sitename }}</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<a ion-item core-user-link>
|
||||
<ion-avatar item-start>
|
||||
<img [src]="siteInfo.userpictureurl" core-external-content alt="{{ 'core.pictureof' | translate:{$a: siteInfo.fullname} }}" role="presentation">
|
||||
</ion-avatar>
|
||||
<p>{{siteInfo.fullname}}</p>
|
||||
</a>
|
||||
<ion-item-divider color="light"></ion-item-divider>
|
||||
<ion-item class="text-center" *ngIf="(!handlers || !handlers.length) && !handlersLoaded">
|
||||
<ion-spinner></ion-spinner>
|
||||
</ion-item>
|
||||
<ion-item *ngFor="let handler of handlers" class="mm-sidemenu-handler {{handler.class}}" (click)="openHandler(handler)" title="{{ handler.title | translate }}">
|
||||
<ion-icon [name]="handler.icon" item-start></ion-icon>
|
||||
<p>{{ handler.title | translate}}</p>
|
||||
<!-- @todo: Badge. -->
|
||||
<!-- <span ng-show="!loading && badge" class="badge badge-positive">{{badge}}</span>
|
||||
<ion-spinner ng-if="loading" class="icon"></ion-spinner> -->
|
||||
</ion-item>
|
||||
<div *ngFor="let item of customItems" class="mm-sidemenu-customitem">
|
||||
<a ion-item *ngIf="item.type != 'embedded'" [href]="item.url" core-link [capture]="item.type == 'app'" [inApp]="item.type == 'inappbrowser'" title="{{item.label}}">
|
||||
<ion-icon [name]="item.icon" item-start></ion-icon>
|
||||
<p>{{item.label}}</p>
|
||||
</a>
|
||||
<a ion-item *ngIf="item.type == 'embedded'" (click)="openItem(item)" title="{{item.label}}">
|
||||
<ion-icon [name]="item.icon" item-start></ion-icon>
|
||||
<p>{{item.label}}</p>
|
||||
</a>
|
||||
</div>
|
||||
<a *ngIf="showWeb" ion-item [href]="siteInfo.siteurl" core-link autoLogin="yes" title="{{ 'core.mainmenu.website' | translate }}">
|
||||
<ion-icon name="globe" item-start></ion-icon>
|
||||
<p>{{ 'core.mainmenu.website' | translate }}</p>
|
||||
</a>
|
||||
<a *ngIf="showHelp" ion-item [href]="docsUrl" core-link autoLogin="no" title="{{ 'core.mainmenu.help' | translate }}">
|
||||
<ion-icon name="help-buoy" item-start></ion-icon>
|
||||
<p>{{ 'core.mainmenu.help' | translate }}</p>
|
||||
</a>
|
||||
<a ion-item (click)="openSettings()" title="{{ 'core.mainmenu.appsettings' | translate }}">
|
||||
<ion-icon name="cog" item-start></ion-icon>
|
||||
<p>{{ 'core.mainmenu.appsettings' | translate }}</p>
|
||||
</a>
|
||||
<a ion-item (click)="logout()" title="{{ logoutLabel | translate }}">
|
||||
<ion-icon name="log-out" item-start></ion-icon>
|
||||
<p>{{ logoutLabel | translate }}</p>
|
||||
</a>
|
||||
</ion-list>
|
||||
</ion-content>
|
|
@ -0,0 +1,35 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// 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 { IonicPageModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreMainMenuMorePage } from './more';
|
||||
import { CoreMainMenuModule } from '../../mainmenu.module';
|
||||
import { CoreComponentsModule } from '../../../../components/components.module';
|
||||
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreMainMenuMorePage,
|
||||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CoreMainMenuModule,
|
||||
IonicPageModule.forChild(CoreMainMenuMorePage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
})
|
||||
export class CoreMainMenuPageModule {}
|
|
@ -0,0 +1,3 @@
|
|||
page-core-mainmenu-more {
|
||||
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// 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, OnDestroy } from '@angular/core';
|
||||
import { IonicPage, NavController } from 'ionic-angular';
|
||||
import { CoreEventsProvider } from '../../../../providers/events';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreMainMenuDelegate, CoreMainMenuHandlerData } from '../../providers/delegate';
|
||||
import { CoreMainMenuProvider, CoreMainMenuCustomItem } from '../../providers/mainmenu';
|
||||
|
||||
/**
|
||||
* Page that displays the list of main menu options that aren't in the tabs.
|
||||
*/
|
||||
@IonicPage()
|
||||
@Component({
|
||||
selector: 'page-core-mainmenu-more',
|
||||
templateUrl: 'more.html',
|
||||
})
|
||||
export class CoreMainMenuMorePage implements OnDestroy {
|
||||
handlers: CoreMainMenuHandlerData[];
|
||||
handlersLoaded: boolean;
|
||||
siteInfo: any;
|
||||
logoutLabel: string;
|
||||
showWeb: boolean;
|
||||
showHelp: boolean;
|
||||
docsUrl: string;
|
||||
customItems: CoreMainMenuCustomItem[];
|
||||
|
||||
protected subscription;
|
||||
protected langObserver;
|
||||
protected updateSiteObserver;
|
||||
|
||||
constructor(private menuDelegate: CoreMainMenuDelegate, private sitesProvider: CoreSitesProvider,
|
||||
private navCtrl: NavController, private mainMenuProvider: CoreMainMenuProvider, eventsProvider: CoreEventsProvider) {
|
||||
|
||||
this.langObserver = eventsProvider.on(CoreEventsProvider.LANGUAGE_CHANGED, this.loadSiteInfo.bind(this));
|
||||
this.updateSiteObserver = eventsProvider.on(CoreEventsProvider.SITE_UPDATED, (data) => {
|
||||
if (sitesProvider.getCurrentSiteId() == data.siteId) {
|
||||
this.loadSiteInfo();
|
||||
}
|
||||
});
|
||||
|
||||
this.loadSiteInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad() {
|
||||
// Load the handlers.
|
||||
this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => {
|
||||
this.handlers = handlers.slice(CoreMainMenuProvider.NUM_MAIN_HANDLERS); // Remove the main handlers.
|
||||
this.handlersLoaded = this.menuDelegate.areHandlersLoaded();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Page destroyed.
|
||||
*/
|
||||
ngOnDestroy() {
|
||||
if (this.subscription) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the site info required by the view.
|
||||
*/
|
||||
protected loadSiteInfo() {
|
||||
const currentSite = this.sitesProvider.getCurrentSite(),
|
||||
config = currentSite.getStoredConfig();
|
||||
|
||||
this.siteInfo = currentSite.getInfo();
|
||||
this.logoutLabel = 'core.mainmenu.' + (config && config.tool_mobile_forcelogout == '1' ? 'logout': 'changesite');
|
||||
this.showWeb = !currentSite.isFeatureDisabled('$mmSideMenuDelegate_website');
|
||||
this.showHelp = !currentSite.isFeatureDisabled('$mmSideMenuDelegate_help');
|
||||
|
||||
currentSite.getDocsUrl().then((docsUrl) => {
|
||||
this.docsUrl = docsUrl;
|
||||
});
|
||||
|
||||
this.mainMenuProvider.getCustomMenuItems().then((items) => {
|
||||
this.customItems = items;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a handler.
|
||||
*
|
||||
* @param {CoreMainMenuHandlerData} handler Handler to open.
|
||||
*/
|
||||
openHandler(handler: CoreMainMenuHandlerData) {
|
||||
// @todo.
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an embedded custom item.
|
||||
*
|
||||
* @param {CoreMainMenuCustomItem} item Item to open.
|
||||
*/
|
||||
openItem(item: CoreMainMenuCustomItem) {
|
||||
// @todo.
|
||||
}
|
||||
|
||||
/**
|
||||
* Open settings page.
|
||||
*/
|
||||
openSettings() {
|
||||
this.navCtrl.push('CoreSettingsListPage');
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout the user.
|
||||
*/
|
||||
logout() {
|
||||
this.sitesProvider.logout();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// 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 { CoreLangProvider } from '../../../providers/lang';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
import { CoreConfigConstants } from '../../../configconstants';
|
||||
|
||||
export interface CoreMainMenuCustomItem {
|
||||
type: string;
|
||||
url: string;
|
||||
label: string;
|
||||
icon: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Service that provides some features regarding Main Menu.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreMainMenuProvider {
|
||||
public static NUM_MAIN_HANDLERS = 4;
|
||||
|
||||
constructor(private langProvider: CoreLangProvider, private sitesProvider: CoreSitesProvider) {}
|
||||
|
||||
/**
|
||||
* Get a list of custom menu items for a certain site.
|
||||
*
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {Promise<CoreMainMenuCustomItem[]>} List of custom menu items.
|
||||
*/
|
||||
getCustomMenuItems(siteId?: string) : Promise<CoreMainMenuCustomItem[]> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
let itemsString = site.getStoredConfig('tool_mobile_custommenuitems'),
|
||||
items,
|
||||
position = 0, // Position of each item, to keep the same order as it's configured.
|
||||
map = {},
|
||||
result = [];
|
||||
|
||||
if (!itemsString || typeof itemsString != 'string') {
|
||||
// Setting not valid.
|
||||
return result;
|
||||
}
|
||||
|
||||
// Add items to the map.
|
||||
items = itemsString.split(/(?:\r\n|\r|\n)/);
|
||||
items.forEach((item) => {
|
||||
let values = item.split('|'),
|
||||
id,
|
||||
label = values[0] ? values[0].trim() : values[0],
|
||||
url = values[1] ? values[1].trim() : values[1],
|
||||
type = values[2] ? values[2].trim() : values[2],
|
||||
lang = (values[3] ? values[3].trim() : values[3]) || 'none',
|
||||
icon = values[4] ? values[4].trim() : values[4];
|
||||
|
||||
if (!label || !url || !type) {
|
||||
// Invalid item, ignore it.
|
||||
return;
|
||||
}
|
||||
|
||||
id = url + '#' + type;
|
||||
if (!icon) {
|
||||
// Icon not defined, use default one.
|
||||
icon = type == 'embedded' ? 'qr-scanner' : 'link';
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return this.langProvider.getCurrentLanguage().then((currentLang) => {
|
||||
const fallbackLang = CoreConfigConstants.default_lang || 'en';
|
||||
|
||||
// Get the right label for each entry and add it to the result.
|
||||
for (let id in map) {
|
||||
let entry = map[id],
|
||||
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 (let lang in entry.labels) {
|
||||
if (lang.indexOf('_only') == -1) {
|
||||
data = entry.labels[lang];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
// No valid label, ignore this entry.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
result[entry.position] = {
|
||||
url: entry.url,
|
||||
type: entry.type,
|
||||
label: data.label,
|
||||
icon: data.icon
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue