diff --git a/src/components/compile-html/compile-html.ts b/src/components/compile-html/compile-html.ts
index 2798df424..26b3cc480 100644
--- a/src/components/compile-html/compile-html.ts
+++ b/src/components/compile-html/compile-html.ts
@@ -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: ''
})
-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);
});
diff --git a/src/core/login/pages/reconnect/reconnect.ts b/src/core/login/pages/reconnect/reconnect.ts
index 1dc684011..99e1ad423 100644
--- a/src/core/login/pages/reconnect/reconnect.ts
+++ b/src/core/login/pages/reconnect/reconnect.ts
@@ -88,6 +88,8 @@ export class CoreLoginReconnectPage {
return site.getPublicConfig().then((config) => {
this.logoUrl = config.logourl || config.compactlogourl;
+ }).catch(() => {
+ // Ignore errors.
});
}
}).catch(() => {
diff --git a/src/core/siteaddons/pages/addon-page/addon-page.html b/src/core/siteaddons/pages/addon-page/addon-page.html
new file mode 100644
index 000000000..15297abc7
--- /dev/null
+++ b/src/core/siteaddons/pages/addon-page/addon-page.html
@@ -0,0 +1,17 @@
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/core/siteaddons/pages/addon-page/addon-page.module.ts b/src/core/siteaddons/pages/addon-page/addon-page.module.ts
new file mode 100644
index 000000000..f7dcf227a
--- /dev/null
+++ b/src/core/siteaddons/pages/addon-page/addon-page.module.ts
@@ -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 {}
diff --git a/src/core/siteaddons/pages/addon-page/addon-page.ts b/src/core/siteaddons/pages/addon-page/addon-page.ts
new file mode 100644
index 000000000..0ae787794
--- /dev/null
+++ b/src/core/siteaddons/pages/addon-page/addon-page.ts
@@ -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} Promise resolved when done.
+ */
+ fetchContent(): Promise {
+ 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();
+ });
+ });
+ }
+}
diff --git a/src/core/siteaddons/providers/siteaddons.ts b/src/core/siteaddons/providers/siteaddons.ts
index e483a992c..d5e030e97 100644
--- a/src/core/siteaddons/providers/siteaddons.ts
+++ b/src/core/siteaddons/providers/siteaddons.ts
@@ -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} Promise resolved when the data is invalidated.
+ */
+ invalidatePageContent(component: string, callback: string, args: any, siteId?: string): Promise {
+ 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
}
});
diff --git a/src/providers/addonmanager.ts b/src/providers/addonmanager.ts
index 106e3cc25..0b8b933c1 100644
--- a/src/providers/addonmanager.ts
+++ b/src/providers/addonmanager.ts
@@ -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) => {