MOBILE-2333 siteaddons: Display site addon page
parent
ec4b76b439
commit
5cb8437936
|
@ -13,7 +13,8 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Component, NgModule, Input, OnInit, OnDestroy, ViewContainerRef, Compiler, ViewChild, ComponentRef, Injector, ChangeDetectorRef
|
Component, NgModule, Input, OnInit, OnChanges, OnDestroy, ViewContainerRef, Compiler, ViewChild, ComponentRef, Injector,
|
||||||
|
SimpleChange, ChangeDetectorRef
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
IonicModule, NavController, Platform, ActionSheetController, AlertController, LoadingController, ModalController,
|
IonicModule, NavController, Platform, ActionSheetController, AlertController, LoadingController, ModalController,
|
||||||
|
@ -75,7 +76,7 @@ import { Md5 } from 'ts-md5/dist/md5';
|
||||||
selector: 'core-compile-html',
|
selector: 'core-compile-html',
|
||||||
template: '<ng-container #dynamicComponent></ng-container>'
|
template: '<ng-container #dynamicComponent></ng-container>'
|
||||||
})
|
})
|
||||||
export class CoreCompileHtmlComponent implements OnInit, OnDestroy {
|
export class CoreCompileHtmlComponent implements OnChanges, OnDestroy {
|
||||||
// List of imports for dynamic module. Since the template can have any component we need to import all core components modules.
|
// List of imports for dynamic module. Since the template can have any component we need to import all core components modules.
|
||||||
protected IMPORTS = [
|
protected IMPORTS = [
|
||||||
IonicModule, TranslateModule.forChild(), CoreComponentsModule, CoreDirectivesModule, CorePipesModule,
|
IonicModule, TranslateModule.forChild(), CoreComponentsModule, CoreDirectivesModule, CorePipesModule,
|
||||||
|
@ -103,10 +104,10 @@ export class CoreCompileHtmlComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component being initialized.
|
* Detect changes on input properties.
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnChanges(changes: { [name: string]: SimpleChange }): void {
|
||||||
if (this.text) {
|
if ((changes.text || changes.javascript) && this.text) {
|
||||||
// Create a new component and a new module.
|
// Create a new component and a new module.
|
||||||
const component = this.createComponent(),
|
const component = this.createComponent(),
|
||||||
module = NgModule({imports: this.IMPORTS, declarations: [component]})(class {});
|
module = NgModule({imports: this.IMPORTS, declarations: [component]})(class {});
|
||||||
|
@ -123,6 +124,9 @@ export class CoreCompileHtmlComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Destroy previous components.
|
||||||
|
this.componentRef && this.componentRef.destroy();
|
||||||
|
|
||||||
// Create the component.
|
// Create the component.
|
||||||
this.componentRef = this.container.createComponent(componentFactory);
|
this.componentRef = this.container.createComponent(componentFactory);
|
||||||
});
|
});
|
||||||
|
|
|
@ -88,6 +88,8 @@ export class CoreLoginReconnectPage {
|
||||||
|
|
||||||
return site.getPublicConfig().then((config) => {
|
return site.getPublicConfig().then((config) => {
|
||||||
this.logoUrl = config.logourl || config.compactlogourl;
|
this.logoUrl = config.logourl || config.compactlogourl;
|
||||||
|
}).catch(() => {
|
||||||
|
// Ignore errors.
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar>
|
||||||
|
<ion-title>{{ title }}</ion-title>
|
||||||
|
|
||||||
|
<ion-buttons end>
|
||||||
|
<!-- If the site addon defines some buttons, they will be added here. -->
|
||||||
|
</ion-buttons>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="dataLoaded" (ionRefresh)="refreshData($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="dataLoaded">
|
||||||
|
<core-compile-html [text]="content"></core-compile-html>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
|
@ -0,0 +1,36 @@
|
||||||
|
// (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 { CoreSiteAddonsAddonPage } from './addon-page';
|
||||||
|
import { CoreComponentsModule } from '../../../../components/components.module';
|
||||||
|
import { CoreCompileHtmlComponentsModule } from '../../../../components/compile-html/compile-html.module';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module to lazy load the page.
|
||||||
|
*/
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
CoreSiteAddonsAddonPage
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreCompileHtmlComponentsModule,
|
||||||
|
IonicPageModule.forChild(CoreSiteAddonsAddonPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CoreSiteAddonsAddonPageModule {}
|
|
@ -0,0 +1,78 @@
|
||||||
|
// (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 } from '@angular/core';
|
||||||
|
import { IonicPage, NavParams } from 'ionic-angular';
|
||||||
|
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||||
|
import { CoreSiteAddonsProvider } from '../../providers/siteaddons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page to render a site addon page.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'core-site-addons-addon-page' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-core-site-addons-addon',
|
||||||
|
templateUrl: 'addon-page.html',
|
||||||
|
})
|
||||||
|
export class CoreSiteAddonsAddonPage {
|
||||||
|
title: string; // Page title.
|
||||||
|
content: string; // Page content.
|
||||||
|
dataLoaded: boolean;
|
||||||
|
|
||||||
|
protected component: string;
|
||||||
|
protected method: string;
|
||||||
|
protected args: any;
|
||||||
|
|
||||||
|
constructor(params: NavParams, protected domUtils: CoreDomUtilsProvider, protected siteAddonsProvider: CoreSiteAddonsProvider) {
|
||||||
|
this.title = params.get('title');
|
||||||
|
this.component = params.get('component');
|
||||||
|
this.method = params.get('method');
|
||||||
|
this.args = params.get('args');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ionViewDidLoad(): void {
|
||||||
|
this.fetchContent().finally(() => {
|
||||||
|
this.dataLoaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the content of the page.
|
||||||
|
*
|
||||||
|
* @return {Promise<any>} Promise resolved when done.
|
||||||
|
*/
|
||||||
|
fetchContent(): Promise<any> {
|
||||||
|
return this.siteAddonsProvider.getContent(this.component, this.method, this.args).then((result) => {
|
||||||
|
this.content = result.html;
|
||||||
|
}).catch((error) => {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'core.errorloadingcontent', true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh the data.
|
||||||
|
*
|
||||||
|
* @param {any} refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshData(refresher: any): void {
|
||||||
|
this.siteAddonsProvider.invalidatePageContent(this.component, this.method, this.args).finally(() => {
|
||||||
|
this.fetchContent().finally(() => {
|
||||||
|
refresher.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { NavController, NavOptions } from 'ionic-angular';
|
import { NavController, NavOptions } from 'ionic-angular';
|
||||||
|
import { CoreLangProvider } from '../../../providers/lang';
|
||||||
import { CoreLoggerProvider } from '../../../providers/logger';
|
import { CoreLoggerProvider } from '../../../providers/logger';
|
||||||
import { CoreSite } from '../../../classes/site';
|
import { CoreSite } from '../../../classes/site';
|
||||||
import { CoreSitesProvider } from '../../../providers/sites';
|
import { CoreSitesProvider } from '../../../providers/sites';
|
||||||
|
@ -24,6 +25,7 @@ import {
|
||||||
} from '../../../core/course/providers/module-delegate';
|
} from '../../../core/course/providers/module-delegate';
|
||||||
import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../../core/user/providers/user-delegate';
|
import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../../core/user/providers/user-delegate';
|
||||||
import { CoreDelegateHandler } from '../../../classes/delegate';
|
import { CoreDelegateHandler } from '../../../classes/delegate';
|
||||||
|
import { CoreConfigConstants } from '../../../configconstants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to provide functionalities regarding site addons.
|
* Service to provide functionalities regarding site addons.
|
||||||
|
@ -36,7 +38,7 @@ export class CoreSiteAddonsProvider {
|
||||||
|
|
||||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
|
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
|
||||||
private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate,
|
private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate,
|
||||||
private userDelegate: CoreUserDelegate) {
|
private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider) {
|
||||||
this.logger = logger.getInstance('CoreUserProvider');
|
this.logger = logger.getInstance('CoreUserProvider');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +57,54 @@ export class CoreSiteAddonsProvider {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a certain content for a site addon.
|
||||||
|
*
|
||||||
|
* @param {string} component Component where the class is. E.g. mod_assign.
|
||||||
|
* @param {string} method Method to execute in the class.
|
||||||
|
* @param {any} args The params for the method.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<{html: string, javascript: string}>} Promise resolved with the content and the javascript.
|
||||||
|
*/
|
||||||
|
getContent(component: string, method: string, args: any, siteId?: string): Promise<{html: string, javascript: string}> {
|
||||||
|
this.logger.debug(`Get content for component '${component}' and method '${method}'`);
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
// Get current language to be added to params.
|
||||||
|
return this.langProvider.getCurrentLanguage().then((lang) => {
|
||||||
|
// Add some params that will always be sent. Clone the object so the original one isn't modified.
|
||||||
|
const argsToSend = this.utils.clone(args);
|
||||||
|
argsToSend.userid = args.userid || site.getUserId();
|
||||||
|
argsToSend.appid = CoreConfigConstants.app_id;
|
||||||
|
argsToSend.versionname = CoreConfigConstants.versionname;
|
||||||
|
argsToSend.lang = lang;
|
||||||
|
|
||||||
|
// Now call the WS.
|
||||||
|
const data = {
|
||||||
|
component: component,
|
||||||
|
method: method,
|
||||||
|
args: this.utils.objectToArrayOfObjects(argsToSend, 'name', 'value', true)
|
||||||
|
}, preSets = {
|
||||||
|
cacheKey: this.getContentCacheKey(component, method, args)
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.sitesProvider.getCurrentSite().read('tool_mobile_get_content', data, preSets);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for get content WS calls.
|
||||||
|
*
|
||||||
|
* @param {string} component Component where the class is. E.g. mod_assign.
|
||||||
|
* @param {string} method Method to execute in the class.
|
||||||
|
* @param {any} args The params for the method.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getContentCacheKey(component: string, method: string, args: any): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'content:' + component + ':' + method + ':' + JSON.stringify(args);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a handler's unique name and the key of a string, return the full string key (prefixed).
|
* Given a handler's unique name and the key of a string, return the full string key (prefixed).
|
||||||
*
|
*
|
||||||
|
@ -81,6 +131,32 @@ export class CoreSiteAddonsProvider {
|
||||||
return addon.addon + '_' + handlerName;
|
return addon.addon + '_' + handlerName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate a page content.
|
||||||
|
*
|
||||||
|
* @param {string} component Component where the class is. E.g. mod_assign.
|
||||||
|
* @param {string} method Method to execute in the class.
|
||||||
|
* @param {any} args The params for the method.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidatePageContent(component: string, callback: string, args: any, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return site.invalidateWsCacheForKey(this.getContentCacheKey(component, callback, args));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the get content WS is available.
|
||||||
|
*
|
||||||
|
* @param {CoreSite} site The site to check. If not defined, current site.
|
||||||
|
*/
|
||||||
|
isGetContentAvailable(site?: CoreSite): boolean {
|
||||||
|
site = site || this.sitesProvider.getCurrentSite();
|
||||||
|
|
||||||
|
return site.wsAvailable('tool_mobile_get_content');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a certain addon is a site addon and it's enabled in a certain site.
|
* Check if a certain addon is a site addon and it's enabled in a certain site.
|
||||||
*
|
*
|
||||||
|
@ -181,8 +257,7 @@ export class CoreSiteAddonsProvider {
|
||||||
pageParams: {
|
pageParams: {
|
||||||
title: prefixedTitle,
|
title: prefixedTitle,
|
||||||
component: addon.component,
|
component: addon.component,
|
||||||
callback: handlerSchema.mainfunction,
|
method: handlerSchema.method,
|
||||||
contextId: handlerSchema.contextid
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -223,10 +298,9 @@ export class CoreSiteAddonsProvider {
|
||||||
navCtrl.push('CoreSiteAddonsAddonPage', {
|
navCtrl.push('CoreSiteAddonsAddonPage', {
|
||||||
title: module.name,
|
title: module.name,
|
||||||
component: addon.component,
|
component: addon.component,
|
||||||
callback: handlerSchema.mainfunction,
|
method: handlerSchema.method,
|
||||||
contextId: handlerSchema.contextid,
|
|
||||||
args: {
|
args: {
|
||||||
course: courseId,
|
courseid: courseId,
|
||||||
cmid: module.id
|
cmid: module.id
|
||||||
}
|
}
|
||||||
}, options);
|
}, options);
|
||||||
|
@ -282,10 +356,9 @@ export class CoreSiteAddonsProvider {
|
||||||
navCtrl.push('CoreSiteAddonsAddonPage', {
|
navCtrl.push('CoreSiteAddonsAddonPage', {
|
||||||
title: prefixedTitle,
|
title: prefixedTitle,
|
||||||
component: addon.component,
|
component: addon.component,
|
||||||
callback: handlerSchema.mainfunction,
|
method: handlerSchema.method,
|
||||||
contextId: handlerSchema.contextid,
|
|
||||||
args: {
|
args: {
|
||||||
course: courseId,
|
courseid: courseId,
|
||||||
userid: user.id
|
userid: user.id
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -36,7 +36,7 @@ export class CoreAddonManagerProvider {
|
||||||
const siteId = this.sitesProvider.getCurrentSiteId();
|
const siteId = this.sitesProvider.getCurrentSiteId();
|
||||||
this.fetchSiteAddons(siteId).then((addons) => {
|
this.fetchSiteAddons(siteId).then((addons) => {
|
||||||
// Addons fetched, check that site hasn't changed.
|
// Addons fetched, check that site hasn't changed.
|
||||||
if (siteId == this.sitesProvider.getCurrentSiteId()) {
|
if (siteId == this.sitesProvider.getCurrentSiteId() && addons.length) {
|
||||||
// Site is still the same. Load the addons and trigger the event.
|
// Site is still the same. Load the addons and trigger the event.
|
||||||
this.loadSiteAddons(addons);
|
this.loadSiteAddons(addons);
|
||||||
|
|
||||||
|
@ -61,6 +61,11 @@ export class CoreAddonManagerProvider {
|
||||||
const addons = [];
|
const addons = [];
|
||||||
|
|
||||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
if (!this.siteAddonsProvider.isGetContentAvailable(site)) {
|
||||||
|
// Cannot load site addons, so there's no point to fetch them.
|
||||||
|
return addons;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the list of addons. Try not to use cache.
|
// Get the list of addons. Try not to use cache.
|
||||||
return site.read('tool_mobile_get_plugins_supporting_mobile', {}, { getFromCache: false }).then((data) => {
|
return site.read('tool_mobile_get_plugins_supporting_mobile', {}, { getFromCache: false }).then((data) => {
|
||||||
data.plugins.forEach((addon: any) => {
|
data.plugins.forEach((addon: any) => {
|
||||||
|
|
Loading…
Reference in New Issue