MOBILE-2333 siteaddons: Display site addon page

main
Dani Palou 2018-02-14 10:29:12 +01:00
parent ec4b76b439
commit 5cb8437936
7 changed files with 230 additions and 15 deletions

View File

@ -13,7 +13,8 @@
// limitations under the License.
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';
import {
IonicModule, NavController, Platform, ActionSheetController, AlertController, LoadingController, ModalController,
@ -75,7 +76,7 @@ import { Md5 } from 'ts-md5/dist/md5';
selector: 'core-compile-html',
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.
protected IMPORTS = [
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 {
if (this.text) {
ngOnChanges(changes: { [name: string]: SimpleChange }): void {
if ((changes.text || changes.javascript) && this.text) {
// Create a new component and a new module.
const component = this.createComponent(),
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.
this.componentRef = this.container.createComponent(componentFactory);
});

View File

@ -88,6 +88,8 @@ export class CoreLoginReconnectPage {
return site.getPublicConfig().then((config) => {
this.logoUrl = config.logourl || config.compactlogourl;
}).catch(() => {
// Ignore errors.
});
}
}).catch(() => {

View File

@ -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>

View File

@ -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 {}

View File

@ -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();
});
});
}
}

View File

@ -14,6 +14,7 @@
import { Injectable } from '@angular/core';
import { NavController, NavOptions } from 'ionic-angular';
import { CoreLangProvider } from '../../../providers/lang';
import { CoreLoggerProvider } from '../../../providers/logger';
import { CoreSite } from '../../../classes/site';
import { CoreSitesProvider } from '../../../providers/sites';
@ -24,6 +25,7 @@ import {
} from '../../../core/course/providers/module-delegate';
import { CoreUserDelegate, CoreUserProfileHandler, CoreUserProfileHandlerData } from '../../../core/user/providers/user-delegate';
import { CoreDelegateHandler } from '../../../classes/delegate';
import { CoreConfigConstants } from '../../../configconstants';
/**
* Service to provide functionalities regarding site addons.
@ -36,7 +38,7 @@ export class CoreSiteAddonsProvider {
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private utils: CoreUtilsProvider,
private mainMenuDelegate: CoreMainMenuDelegate, private moduleDelegate: CoreCourseModuleDelegate,
private userDelegate: CoreUserDelegate) {
private userDelegate: CoreUserDelegate, private langProvider: CoreLangProvider) {
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).
*
@ -81,6 +131,32 @@ export class CoreSiteAddonsProvider {
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.
*
@ -181,8 +257,7 @@ export class CoreSiteAddonsProvider {
pageParams: {
title: prefixedTitle,
component: addon.component,
callback: handlerSchema.mainfunction,
contextId: handlerSchema.contextid
method: handlerSchema.method,
}
};
}
@ -223,10 +298,9 @@ export class CoreSiteAddonsProvider {
navCtrl.push('CoreSiteAddonsAddonPage', {
title: module.name,
component: addon.component,
callback: handlerSchema.mainfunction,
contextId: handlerSchema.contextid,
method: handlerSchema.method,
args: {
course: courseId,
courseid: courseId,
cmid: module.id
}
}, options);
@ -282,10 +356,9 @@ export class CoreSiteAddonsProvider {
navCtrl.push('CoreSiteAddonsAddonPage', {
title: prefixedTitle,
component: addon.component,
callback: handlerSchema.mainfunction,
contextId: handlerSchema.contextid,
method: handlerSchema.method,
args: {
course: courseId,
courseid: courseId,
userid: user.id
}
});

View File

@ -36,7 +36,7 @@ export class CoreAddonManagerProvider {
const siteId = this.sitesProvider.getCurrentSiteId();
this.fetchSiteAddons(siteId).then((addons) => {
// 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.
this.loadSiteAddons(addons);
@ -61,6 +61,11 @@ export class CoreAddonManagerProvider {
const addons = [];
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.
return site.read('tool_mobile_get_plugins_supporting_mobile', {}, { getFromCache: false }).then((data) => {
data.plugins.forEach((addon: any) => {