From 519256b0bbab2c786f7e908d5ecf2c6c99cfa7bd Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Tue, 15 May 2018 13:28:18 +0200 Subject: [PATCH 1/2] MOBILE-2346 lti: Migrate LTI (external tool) --- .../mod/lti/components/components.module.ts | 45 ++++ src/addon/mod/lti/components/index/index.html | 23 ++ src/addon/mod/lti/components/index/index.ts | 118 ++++++++++ src/addon/mod/lti/lang/en.json | 5 + src/addon/mod/lti/lti.module.ts | 41 ++++ src/addon/mod/lti/pages/index/index.html | 12 + src/addon/mod/lti/pages/index/index.module.ts | 33 +++ src/addon/mod/lti/pages/index/index.ts | 48 ++++ src/addon/mod/lti/providers/link-handler.ts | 29 +++ src/addon/mod/lti/providers/lti.ts | 216 ++++++++++++++++++ src/addon/mod/lti/providers/module-handler.ts | 126 ++++++++++ src/app/app.module.ts | 2 + 12 files changed, 698 insertions(+) create mode 100644 src/addon/mod/lti/components/components.module.ts create mode 100644 src/addon/mod/lti/components/index/index.html create mode 100644 src/addon/mod/lti/components/index/index.ts create mode 100644 src/addon/mod/lti/lang/en.json create mode 100644 src/addon/mod/lti/lti.module.ts create mode 100644 src/addon/mod/lti/pages/index/index.html create mode 100644 src/addon/mod/lti/pages/index/index.module.ts create mode 100644 src/addon/mod/lti/pages/index/index.ts create mode 100644 src/addon/mod/lti/providers/link-handler.ts create mode 100644 src/addon/mod/lti/providers/lti.ts create mode 100644 src/addon/mod/lti/providers/module-handler.ts diff --git a/src/addon/mod/lti/components/components.module.ts b/src/addon/mod/lti/components/components.module.ts new file mode 100644 index 000000000..d734384b9 --- /dev/null +++ b/src/addon/mod/lti/components/components.module.ts @@ -0,0 +1,45 @@ +// (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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreCourseComponentsModule } from '@core/course/components/components.module'; +import { AddonModLtiIndexComponent } from './index/index'; + +@NgModule({ + declarations: [ + AddonModLtiIndexComponent, + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule, + CoreCourseComponentsModule + ], + providers: [ + ], + exports: [ + AddonModLtiIndexComponent, + ], + entryComponents: [ + AddonModLtiIndexComponent, + ] +}) +export class AddonModLtiComponentsModule {} diff --git a/src/addon/mod/lti/components/index/index.html b/src/addon/mod/lti/components/index/index.html new file mode 100644 index 000000000..e80a89554 --- /dev/null +++ b/src/addon/mod/lti/components/index/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + +
+ +
+ + {{ 'addon.mod_lti.errorinvalidlaunchurl' | translate }} +
diff --git a/src/addon/mod/lti/components/index/index.ts b/src/addon/mod/lti/components/index/index.ts new file mode 100644 index 000000000..4c81f8786 --- /dev/null +++ b/src/addon/mod/lti/components/index/index.ts @@ -0,0 +1,118 @@ +// (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, Optional, Injector } from '@angular/core'; +import { Content } from 'ionic-angular'; +import { CoreUrlUtilsProvider } from '@providers/utils/url'; +import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; +import { AddonModLtiProvider } from '../../providers/lti'; + +/** + * Component that displays an LTI entry page. + */ +@Component({ + selector: 'addon-mod-lti-index', + templateUrl: 'index.html', +}) +export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityComponent { + component = AddonModLtiProvider.COMPONENT; + moduleName = 'lti'; + + lti: any; // The LTI object. + isValidUrl: boolean; + + protected fetchContentDefaultError = 'addon.mod_lti.errorgetlti'; + + constructor(injector: Injector, + @Optional() protected content: Content, + private ltiProvider: AddonModLtiProvider, + private urlUtils: CoreUrlUtilsProvider) { + super(injector, content); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + super.ngOnInit(); + + this.loadContent(false, true); + } + + /** + * Check the completion. + */ + protected checkCompletion(): void { + this.courseProvider.checkModuleCompletion(this.courseId, this.module.completionstatus); + } + + /** + * Get the LTI data. + * + * @param {boolean} [refresh=false] If it's refreshing content. + * @param {boolean} [sync=false] If the refresh is needs syncing. + * @param {boolean} [showErrors=false] If show errors to the user of hide them. + * @return {Promise} Promise resolved when done. + */ + protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { + return this.ltiProvider.getLti(this.courseId, this.module.id).then((ltiData) => { + this.lti = ltiData; + + return this.ltiProvider.getLtiLaunchData(ltiData.id).then((launchData) => { + this.lti.launchdata = launchData; + this.description = this.lti.intro || this.description; + this.isValidUrl = this.urlUtils.isHttpURL(launchData.endpoint); + this.dataRetrieved.emit(this.lti); + }); + }).then(() => { + // All data obtained, now fill the context menu. + this.fillContextMenu(refresh); + }); + } + + /** + * Perform the invalidate content function. + * + * @return {Promise} Resolved when done. + */ + protected invalidateContent(): Promise { + const promises = []; + + promises.push(this.ltiProvider.invalidateLti(this.courseId)); + if (this.lti) { + promises.push(this.ltiProvider.invalidateLtiLaunchData(this.lti.id)); + } + + return Promise.all(promises); + } + + /** + * Launch the LTI. + */ + launch(): void { + // "View" LTI. + this.ltiProvider.logView(this.lti.id).then(() => { + this.checkCompletion(); + }).catch((error) => { + // Ignore errors. + }); + + // Launch LTI. + this.ltiProvider.launch(this.lti.launchdata.endpoint, this.lti.launchdata.parameters).catch((message) => { + if (message) { + this.domUtils.showErrorModal(message); + } + }); + } +} diff --git a/src/addon/mod/lti/lang/en.json b/src/addon/mod/lti/lang/en.json new file mode 100644 index 000000000..3dc7ad1a5 --- /dev/null +++ b/src/addon/mod/lti/lang/en.json @@ -0,0 +1,5 @@ +{ + "errorgetlti": "Error getting module data.", + "errorinvalidlaunchurl": "The launch URL is not valid.", + "launchactivity": "Launch the activity" +} \ No newline at end of file diff --git a/src/addon/mod/lti/lti.module.ts b/src/addon/mod/lti/lti.module.ts new file mode 100644 index 000000000..557775b5c --- /dev/null +++ b/src/addon/mod/lti/lti.module.ts @@ -0,0 +1,41 @@ +// (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 { AddonModLtiComponentsModule } from './components/components.module'; +import { AddonModLtiModuleHandler } from './providers/module-handler'; +import { AddonModLtiProvider } from './providers/lti'; +import { AddonModLtiLinkHandler } from './providers/link-handler'; +import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; +import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate'; + +@NgModule({ + declarations: [ + ], + imports: [ + AddonModLtiComponentsModule + ], + providers: [ + AddonModLtiProvider, + AddonModLtiModuleHandler, + AddonModLtiLinkHandler, + ] +}) +export class AddonModLtiModule { + constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModLtiModuleHandler, + contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModLtiLinkHandler) { + moduleDelegate.registerHandler(moduleHandler); + contentLinksDelegate.registerHandler(linkHandler); + } +} diff --git a/src/addon/mod/lti/pages/index/index.html b/src/addon/mod/lti/pages/index/index.html new file mode 100644 index 000000000..456646402 --- /dev/null +++ b/src/addon/mod/lti/pages/index/index.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/addon/mod/lti/pages/index/index.module.ts b/src/addon/mod/lti/pages/index/index.module.ts new file mode 100644 index 000000000..dda226660 --- /dev/null +++ b/src/addon/mod/lti/pages/index/index.module.ts @@ -0,0 +1,33 @@ +// (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 { CoreDirectivesModule } from '@directives/directives.module'; +import { AddonModLtiComponentsModule } from '../../components/components.module'; +import { AddonModLtiIndexPage } from './index'; + +@NgModule({ + declarations: [ + AddonModLtiIndexPage, + ], + imports: [ + CoreDirectivesModule, + AddonModLtiComponentsModule, + IonicPageModule.forChild(AddonModLtiIndexPage), + TranslateModule.forChild() + ], +}) +export class AddonModLtiIndexPageModule {} diff --git a/src/addon/mod/lti/pages/index/index.ts b/src/addon/mod/lti/pages/index/index.ts new file mode 100644 index 000000000..ea6ab0034 --- /dev/null +++ b/src/addon/mod/lti/pages/index/index.ts @@ -0,0 +1,48 @@ +// (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, ViewChild } from '@angular/core'; +import { IonicPage, NavParams } from 'ionic-angular'; +import { AddonModLtiIndexComponent } from '../../components/index/index'; + +/** + * Page that displays an LTI. + */ +@IonicPage({ segment: 'addon-mod-lti-index' }) +@Component({ + selector: 'page-addon-mod-lti-index', + templateUrl: 'index.html', +}) +export class AddonModLtiIndexPage { + @ViewChild(AddonModLtiIndexComponent) ltiComponent: AddonModLtiIndexComponent; + + title: string; + module: any; + courseId: number; + + constructor(navParams: NavParams) { + this.module = navParams.get('module') || {}; + this.courseId = navParams.get('courseId'); + this.title = this.module.name; + } + + /** + * Update some data based on the LTI instance. + * + * @param {any} lti LTI instance. + */ + updateData(lti: any): void { + this.title = lti.name || this.title; + } +} diff --git a/src/addon/mod/lti/providers/link-handler.ts b/src/addon/mod/lti/providers/link-handler.ts new file mode 100644 index 000000000..2c69182cb --- /dev/null +++ b/src/addon/mod/lti/providers/link-handler.ts @@ -0,0 +1,29 @@ +// (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 { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler'; +import { CoreCourseHelperProvider } from '@core/course/providers/helper'; + +/** + * Handler to treat links to LTI. + */ +@Injectable() +export class AddonModLtiLinkHandler extends CoreContentLinksModuleIndexHandler { + name = 'AddonModLtiLinkHandler'; + + constructor(courseHelper: CoreCourseHelperProvider) { + super(courseHelper, 'AddonModLti', 'lti'); + } +} diff --git a/src/addon/mod/lti/providers/lti.ts b/src/addon/mod/lti/providers/lti.ts new file mode 100644 index 000000000..be87ee4ac --- /dev/null +++ b/src/addon/mod/lti/providers/lti.ts @@ -0,0 +1,216 @@ +// (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 { TranslateService } from '@ngx-translate/core'; +import { CoreFileProvider } from '@providers/file'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { CoreUtilsProvider } from '@providers/utils/utils'; +import { CoreUrlUtilsProvider } from '@providers/utils/url'; + +export interface AddonModLtiParam { + name: string; + value: string; +} + +/** + * Service that provides some features for LTI. + */ +@Injectable() +export class AddonModLtiProvider { + static COMPONENT = 'mmaModLti'; + + protected ROOT_CACHE_KEY = 'mmaModLti:'; + protected LAUNCHER_FILE_NAME = 'lti_launcher.html'; + + constructor(private fileProvider: CoreFileProvider, + private sitesProvider: CoreSitesProvider, + private textUtils: CoreTextUtilsProvider, + private urlUtils: CoreUrlUtilsProvider, + private utils: CoreUtilsProvider, + private translate: TranslateService) {} + + /** + * Delete launcher. + * + * @return {Promise} Promise resolved when the launcher file is deleted. + */ + deleteLauncher(): Promise { + return this.fileProvider.removeFile(this.LAUNCHER_FILE_NAME); + } + + /** + * Generates a launcher file. + * + * @param {string} url Launch URL. + * @param {AddonModLtiParam[]} params Launch params. + * @return {Promise} Promise resolved with the file URL. + */ + generateLauncher(url: string, params: AddonModLtiParam[]): Promise { + if (!this.fileProvider.isAvailable()) { + return Promise.resolve(url); + } + + // Generate a form with the params. + let text = '
\n'; + params.forEach((p) => { + if (p.name == 'ext_submit') { + text += ' \n'; + }); + text += '
\n'; + + // Add an in-line script to automatically submit the form. + text += ' \n'; + + return this.fileProvider.writeFile(this.LAUNCHER_FILE_NAME, text).then((entry) => { + return entry.toURL(); + }); + } + + /** + * Get a LTI. + * + * @param {number} courseId Course ID. + * @param {number} cmId Course module ID. + * @return {Promise} Promise resolved when the LTI is retrieved. + */ + getLti(courseId: number, cmId: number): Promise { + const params: any = { + courseids: [courseId] + }; + const preSets: any = { + cacheKey: this.getLtiCacheKey(courseId) + }; + + return this.sitesProvider.getCurrentSite().read('mod_lti_get_ltis_by_courses', params, preSets).then((response) => { + if (response.ltis) { + const currentLti = response.ltis.find((lti) => lti.coursemodule == cmId); + if (currentLti) { + return currentLti; + } + } + + return Promise.reject(null); + }); + } + + /** + * Get cache key for LTI data WS calls. + * + * @param {number} courseId Course ID. + * @return {string} Cache key. + */ + protected getLtiCacheKey(courseId: number): string { + return this.ROOT_CACHE_KEY + 'lti:' + courseId; + } + + /** + * Get a LTI launch data. + * + * @param {number} id LTI id. + * @return {Promise} Promise resolved when the launch data is retrieved. + */ + getLtiLaunchData(id: number): Promise { + const params: any = { + toolid: id + }; + const preSets = { + cacheKey: this.getLtiLaunchDataCacheKey(id) + }; + + return this.sitesProvider.getCurrentSite().read('mod_lti_get_tool_launch_data', params, preSets).then((response) => { + if (response.endpoint) { + return response; + } + + return Promise.reject(null); + }); + } + + /** + * Get cache key for LTI launch data WS calls. + * + * @param {number} id LTI id. + * @return {string} Cache key. + */ + protected getLtiLaunchDataCacheKey(id: number): string { + return this.ROOT_CACHE_KEY + 'launch:' + id; + } + + /** + * Invalidates LTI data. + * + * @param {number} courseId Course ID. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateLti(courseId: number): Promise { + return this.sitesProvider.getCurrentSite().invalidateWsCacheForKey(this.getLtiCacheKey(courseId)); + } + + /** + * Invalidates options. + * + * @param {number} id LTI id. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateLtiLaunchData(id: number): Promise { + return this.sitesProvider.getCurrentSite().invalidateWsCacheForKey(this.getLtiLaunchDataCacheKey(id)); + } + + /** + * Launch LTI. + * + * @param {string} url Launch URL. + * @param {AddonModLtiParam[]} params Launch params. + * @return {Promise} Promise resolved when the WS call is successful. + */ + launch(url: string, params: AddonModLtiParam[]): Promise { + if (!this.urlUtils.isHttpURL(url)) { + return Promise.reject(this.translate.instant('addon.mod_lti.errorinvalidlaunchurl')); + } + + // Generate launcher and open it. + return this.generateLauncher(url, params).then((url) => { + this.utils.openInApp(url).show(); + }); + } + + /** + * Report the LTI as being viewed. + * + * @param {string} id LTI id. + * @return {Promise} Promise resolved when the WS call is successful. + */ + logView(id: string): Promise { + if (id) { + const params: any = { + ltiid: id + }; + + return this.sitesProvider.getCurrentSite().write('mod_lti_view_lti', params); + } + + return Promise.reject(null); + } +} diff --git a/src/addon/mod/lti/providers/module-handler.ts b/src/addon/mod/lti/providers/module-handler.ts new file mode 100644 index 000000000..dfeff3028 --- /dev/null +++ b/src/addon/mod/lti/providers/module-handler.ts @@ -0,0 +1,126 @@ +// (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 { NavController, NavOptions } from 'ionic-angular'; +import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@core/course/providers/module-delegate'; +import { CoreAppProvider } from '@providers/app'; +import { CoreCourseProvider } from '@core/course/providers/course'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreFilepoolProvider } from '@providers/filepool'; +import { CoreSitesProvider } from '@providers/sites'; +import { AddonModLtiIndexComponent } from '../components/index/index'; +import { AddonModLtiProvider } from './lti'; + +/** + * Handler to support LTI modules. + */ +@Injectable() +export class AddonModLtiModuleHandler implements CoreCourseModuleHandler { + name = 'AddonModLti'; + modName = 'lti'; + + constructor(private appProvider: CoreAppProvider, + private courseProvider: CoreCourseProvider, + private domUtils: CoreDomUtilsProvider, + private filepoolProvider: CoreFilepoolProvider, + private sitesProvider: CoreSitesProvider, + private ltiProvider: AddonModLtiProvider) {} + + /** + * Check if the handler is enabled on a site level. + * + * @return {boolean|Promise} Whether or not the handler is enabled on a site level. + */ + isEnabled(): boolean | Promise { + return true; + } + + /** + * Get the data required to display the module in the course contents view. + * + * @param {any} module The module object. + * @param {number} courseId The course ID. + * @param {number} sectionId The section ID. + * @return {CoreCourseModuleHandlerData} Data to render the module. + */ + getData(module: any, courseId: number, sectionId: number): CoreCourseModuleHandlerData { + const data: CoreCourseModuleHandlerData = { + icon: this.courseProvider.getModuleIconSrc('lti'), + title: module.name, + class: 'addon-mod_lti-handler', + action(event: Event, navCtrl: NavController, module: any, courseId: number, options: NavOptions): void { + navCtrl.push('AddonModLtiIndexPage', {module: module, courseId: courseId}, options); + }, + buttons: [{ + icon: 'link', + label: 'addon.mod_lti.launchactivity', + action: (event: Event, navCtrl: NavController, module: any, courseId: number): void => { + const modal = this.domUtils.showModalLoading(); + + // Get LTI and launch data. + this.ltiProvider.getLti(courseId, module.id).then((ltiData) => { + return this.ltiProvider.getLtiLaunchData(ltiData.id).then((launchData) => { + // "View" LTI. + this.ltiProvider.logView(ltiData.id).then(() => { + this.courseProvider.checkModuleCompletion(courseId, module.completionstatus); + }).catch(() => { + // Ignore errors. + }); + + // Launch LTI. + return this.ltiProvider.launch(launchData.endpoint, launchData.parameters); + }); + }).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'addon.mod_lti.errorgetlti', true); + }).finally(() => { + modal.dismiss(); + }); + } + }] + }; + + // Handle custom icons. + this.ltiProvider.getLti(courseId, module.id).then((ltiData) => { + const icon = ltiData.secureicon || ltiData.icon; + if (icon) { + const siteId = this.sitesProvider.getCurrentSiteId(); + this.filepoolProvider.downloadUrl(siteId, icon, false, AddonModLtiProvider.COMPONENT, module.id).then((url) => { + data.icon = url; + }).catch(() => { + // Error downloading. If we're online we'll set the online url. + if (this.appProvider.isOnline()) { + data.icon = icon; + } + }); + } + }).catch(() => { + // Ignore errors. + }); + + return data; + } + + /** + * Get the component to render the module. This is needed to support singleactivity course format. + * The component returned must implement CoreCourseModuleMainComponent. + * + * @param {any} course The course object. + * @param {any} module The module object. + * @return {any} The component to use, undefined if not found. + */ + getMainComponent(course: any, module: any): any { + return AddonModLtiIndexComponent; + } +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 37abd9b1d..7c565ae77 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -82,6 +82,7 @@ import { AddonModBookModule } from '@addon/mod/book/book.module'; import { AddonModChatModule } from '@addon/mod/chat/chat.module'; import { AddonModChoiceModule } from '@addon/mod/choice/choice.module'; import { AddonModLabelModule } from '@addon/mod/label/label.module'; +import { AddonModLtiModule } from '@addon/mod/lti/lti.module'; import { AddonModResourceModule } from '@addon/mod/resource/resource.module'; import { AddonModFeedbackModule } from '@addon/mod/feedback/feedback.module'; import { AddonModFolderModule } from '@addon/mod/folder/folder.module'; @@ -187,6 +188,7 @@ export const CORE_PROVIDERS: any[] = [ AddonModFeedbackModule, AddonModFolderModule, AddonModForumModule, + AddonModLtiModule, AddonModPageModule, AddonModQuizModule, AddonModScormModule, From 11b1a55e0482da7c19f85b0d598a066c8772b183 Mon Sep 17 00:00:00 2001 From: Albert Gasset Date: Wed, 16 May 2018 13:25:23 +0200 Subject: [PATCH 2/2] MOBILE-2346 lti: PR fixes --- src/addon/mod/lti/components/index/index.html | 6 +-- src/addon/mod/lti/components/index/index.ts | 38 ++++++++----------- src/addon/mod/lti/pages/index/index.html | 4 ++ src/addon/mod/lti/providers/lti.ts | 19 +++++----- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/addon/mod/lti/components/index/index.html b/src/addon/mod/lti/components/index/index.html index e80a89554..049830e41 100644 --- a/src/addon/mod/lti/components/index/index.html +++ b/src/addon/mod/lti/components/index/index.html @@ -3,7 +3,7 @@ - + @@ -12,12 +12,10 @@ -
+
- - {{ 'addon.mod_lti.errorinvalidlaunchurl' | translate }} diff --git a/src/addon/mod/lti/components/index/index.ts b/src/addon/mod/lti/components/index/index.ts index 4c81f8786..8f0168a99 100644 --- a/src/addon/mod/lti/components/index/index.ts +++ b/src/addon/mod/lti/components/index/index.ts @@ -14,7 +14,6 @@ import { Component, Optional, Injector } from '@angular/core'; import { Content } from 'ionic-angular'; -import { CoreUrlUtilsProvider } from '@providers/utils/url'; import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; import { AddonModLtiProvider } from '../../providers/lti'; @@ -30,14 +29,12 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo moduleName = 'lti'; lti: any; // The LTI object. - isValidUrl: boolean; protected fetchContentDefaultError = 'addon.mod_lti.errorgetlti'; constructor(injector: Injector, @Optional() protected content: Content, - private ltiProvider: AddonModLtiProvider, - private urlUtils: CoreUrlUtilsProvider) { + private ltiProvider: AddonModLtiProvider) { super(injector, content); } @@ -47,7 +44,7 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo ngOnInit(): void { super.ngOnInit(); - this.loadContent(false, true); + this.loadContent(); } /** @@ -68,13 +65,8 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo protected fetchContent(refresh: boolean = false, sync: boolean = false, showErrors: boolean = false): Promise { return this.ltiProvider.getLti(this.courseId, this.module.id).then((ltiData) => { this.lti = ltiData; - - return this.ltiProvider.getLtiLaunchData(ltiData.id).then((launchData) => { - this.lti.launchdata = launchData; - this.description = this.lti.intro || this.description; - this.isValidUrl = this.urlUtils.isHttpURL(launchData.endpoint); - this.dataRetrieved.emit(this.lti); - }); + this.description = this.lti.intro || this.description; + this.dataRetrieved.emit(this.lti); }).then(() => { // All data obtained, now fill the context menu. this.fillContextMenu(refresh); @@ -101,18 +93,18 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo * Launch the LTI. */ launch(): void { - // "View" LTI. - this.ltiProvider.logView(this.lti.id).then(() => { - this.checkCompletion(); - }).catch((error) => { - // Ignore errors. - }); + this.ltiProvider.getLtiLaunchData(this.lti.id).then((launchData) => { + // "View" LTI. + this.ltiProvider.logView(this.lti.id).then(() => { + this.checkCompletion(); + }).catch((error) => { + // Ignore errors. + }); - // Launch LTI. - this.ltiProvider.launch(this.lti.launchdata.endpoint, this.lti.launchdata.parameters).catch((message) => { - if (message) { - this.domUtils.showErrorModal(message); - } + // Launch LTI. + return this.ltiProvider.launch(launchData.endpoint, launchData.parameters); + }).catch((message) => { + this.domUtils.showErrorModalDefault(message, 'core.error', true); }); } } diff --git a/src/addon/mod/lti/pages/index/index.html b/src/addon/mod/lti/pages/index/index.html index 456646402..ffcf3f1f0 100644 --- a/src/addon/mod/lti/pages/index/index.html +++ b/src/addon/mod/lti/pages/index/index.html @@ -8,5 +8,9 @@ + + + + diff --git a/src/addon/mod/lti/providers/lti.ts b/src/addon/mod/lti/providers/lti.ts index be87ee4ac..f2a453ce6 100644 --- a/src/addon/mod/lti/providers/lti.ts +++ b/src/addon/mod/lti/providers/lti.ts @@ -135,7 +135,12 @@ export class AddonModLtiProvider { const params: any = { toolid: id }; + + // Try to avoid using cache since the "nonce" parameter is set to a timestamp. const preSets = { + getFromCache: false, + saveToCache: true, + emergencyCache: true, cacheKey: this.getLtiLaunchDataCacheKey(id) }; @@ -192,7 +197,7 @@ export class AddonModLtiProvider { // Generate launcher and open it. return this.generateLauncher(url, params).then((url) => { - this.utils.openInApp(url).show(); + this.utils.openInApp(url); }); } @@ -203,14 +208,10 @@ export class AddonModLtiProvider { * @return {Promise} Promise resolved when the WS call is successful. */ logView(id: string): Promise { - if (id) { - const params: any = { - ltiid: id - }; + const params: any = { + ltiid: id + }; - return this.sitesProvider.getCurrentSite().write('mod_lti_view_lti', params); - } - - return Promise.reject(null); + return this.sitesProvider.getCurrentSite().write('mod_lti_view_lti', params); } }