From 55d3af30c74aace462c9b741dc84fa3f85910854 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Fri, 12 Jun 2020 13:54:50 +0200 Subject: [PATCH] MOBILE-3447 lti: Support disable InAppBrowser --- src/addon/mod/lti/components/index/index.ts | 15 +-- src/addon/mod/lti/lti.module.ts | 7 +- src/addon/mod/lti/providers/helper.ts | 119 ++++++++++++++++++ src/addon/mod/lti/providers/lti.ts | 28 +++++ src/addon/mod/lti/providers/module-handler.ts | 25 +--- 5 files changed, 157 insertions(+), 37 deletions(-) create mode 100644 src/addon/mod/lti/providers/helper.ts diff --git a/src/addon/mod/lti/components/index/index.ts b/src/addon/mod/lti/components/index/index.ts index ca933a8e5..f078e2070 100644 --- a/src/addon/mod/lti/components/index/index.ts +++ b/src/addon/mod/lti/components/index/index.ts @@ -16,6 +16,7 @@ import { Component, Optional, Injector } from '@angular/core'; import { Content } from 'ionic-angular'; import { CoreCourseModuleMainActivityComponent } from '@core/course/classes/main-activity-component'; import { AddonModLtiProvider, AddonModLtiLti } from '../../providers/lti'; +import { AddonModLtiHelper } from '../../providers/helper'; /** * Component that displays an LTI entry page. @@ -92,18 +93,6 @@ export class AddonModLtiIndexComponent extends CoreCourseModuleMainActivityCompo * Launch the LTI. */ launch(): void { - this.ltiProvider.getLtiLaunchData(this.lti.id).then((launchData) => { - // "View" LTI. - this.ltiProvider.logView(this.lti.id, this.lti.name).then(() => { - this.checkCompletion(); - }).catch((error) => { - // Ignore errors. - }); - - // Launch LTI. - return this.ltiProvider.launch(launchData.endpoint, launchData.parameters); - }).catch((message) => { - this.domUtils.showErrorModalDefault(message, 'core.error', true); - }); + AddonModLtiHelper.instance.getDataAndLaunch(this.courseId, this.module, this.lti); } } diff --git a/src/addon/mod/lti/lti.module.ts b/src/addon/mod/lti/lti.module.ts index 4e2c83878..20649b794 100644 --- a/src/addon/mod/lti/lti.module.ts +++ b/src/addon/mod/lti/lti.module.ts @@ -16,6 +16,7 @@ import { NgModule } from '@angular/core'; import { AddonModLtiComponentsModule } from './components/components.module'; import { AddonModLtiModuleHandler } from './providers/module-handler'; import { AddonModLtiProvider } from './providers/lti'; +import { AddonModLtiHelperProvider } from './providers/helper'; import { AddonModLtiLinkHandler } from './providers/link-handler'; import { AddonModLtiListLinkHandler } from './providers/list-link-handler'; import { AddonModLtiPrefetchHandler } from './providers/prefetch-handler'; @@ -25,7 +26,8 @@ import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module- // List of providers (without handlers). export const ADDON_MOD_LTI_PROVIDERS: any[] = [ - AddonModLtiProvider + AddonModLtiProvider, + AddonModLtiHelperProvider, ]; @NgModule({ @@ -36,10 +38,11 @@ export const ADDON_MOD_LTI_PROVIDERS: any[] = [ ], providers: [ AddonModLtiProvider, + AddonModLtiHelperProvider, AddonModLtiModuleHandler, AddonModLtiLinkHandler, AddonModLtiListLinkHandler, - AddonModLtiPrefetchHandler + AddonModLtiPrefetchHandler, ] }) export class AddonModLtiModule { diff --git a/src/addon/mod/lti/providers/helper.ts b/src/addon/mod/lti/providers/helper.ts new file mode 100644 index 000000000..24a771c69 --- /dev/null +++ b/src/addon/mod/lti/providers/helper.ts @@ -0,0 +1,119 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// 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 { Platform } from 'ionic-angular'; +import { CoreEvents, CoreEventsProvider } from '@providers/events'; +import { CoreSites } from '@providers/sites'; +import { CoreDomUtils } from '@providers/utils/dom'; +import { CoreCourse } from '@core/course/providers/course'; +import { AddonModLti, AddonModLtiLti } from './lti'; + +import { makeSingleton } from '@singletons/core.singletons'; + +/** + * Service that provides some helper functions for LTI. + */ +@Injectable() +export class AddonModLtiHelperProvider { + + protected pendingCheckCompletion: {[moduleId: string]: {courseId: number, module: any}} = {}; + + constructor(platform: Platform) { + + platform.resume.subscribe(() => { + // User went back to the app, check pending completions. + for (const moduleId in this.pendingCheckCompletion) { + const data = this.pendingCheckCompletion[moduleId]; + + CoreCourse.instance.checkModuleCompletion(data.courseId, data.module.completiondata); + } + }); + + // Clear pending completion on logout. + CoreEvents.instance.on(CoreEventsProvider.LOGOUT, () => { + this.pendingCheckCompletion = {}; + }); + } + + /** + * Get needed data and launch the LTI. + * + * @param courseId Course ID. + * @param module Module. + * @param lti LTI instance. If not provided it will be obtained. + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved when done. + */ + async getDataAndLaunch(courseId: number, module: any, lti?: AddonModLtiLti, siteId?: string): Promise { + siteId = siteId || CoreSites.instance.getCurrentSiteId(); + + const modal = CoreDomUtils.instance.showModalLoading(); + + try { + const openInBrowser = await AddonModLti.instance.isOpenInAppBrowserDisabled(siteId); + + if (openInBrowser) { + const site = await CoreSites.instance.getSite(siteId); + + // The view event is triggered by the browser, mark the module as pending to check completion. + this.pendingCheckCompletion[module.id] = { + courseId, + module, + }; + + await site.openInBrowserWithAutoLogin(module.url); + } else { + // Open in app. + if (!lti) { + lti = await AddonModLti.instance.getLti(courseId, module.id); + } + + const launchData = await AddonModLti.instance.getLtiLaunchData(lti.id); + + // "View" LTI without blocking the UI. + this.logViewAndCheckCompletion(courseId, module, lti.id, lti.name, siteId); + + // Launch LTI. + return AddonModLti.instance.launch(launchData.endpoint, launchData.parameters); + } + } catch (error) { + CoreDomUtils.instance.showErrorModalDefault(error, 'addon.mod_lti.errorgetlti', true); + } finally { + modal.dismiss(); + } + } + + /** + * Report the LTI as being viewed and check completion. + * + * @param courseId Course ID. + * @param module Module. + * @param ltiId LTI id. + * @param name Name of the lti. + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved when done. + */ + async logViewAndCheckCompletion(courseId: number, module: any, ltiId: number, name?: string, siteId?: string): Promise { + try { + await AddonModLti.instance.logView(ltiId, name); + + CoreCourse.instance.checkModuleCompletion(courseId, module.completiondata); + } catch (error) { + // Ignore errors. + } + } +} + +export class AddonModLtiHelper extends makeSingleton(AddonModLtiHelperProvider) {} diff --git a/src/addon/mod/lti/providers/lti.ts b/src/addon/mod/lti/providers/lti.ts index 7a6e4904e..acae1b745 100644 --- a/src/addon/mod/lti/providers/lti.ts +++ b/src/addon/mod/lti/providers/lti.ts @@ -24,6 +24,8 @@ import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper'; import { CoreSite } from '@classes/site'; import { CoreWSExternalWarning, CoreWSExternalFile } from '@providers/ws'; +import { makeSingleton } from '@singletons/core.singletons'; + /** * Service that provides some features for LTI. */ @@ -193,6 +195,30 @@ export class AddonModLtiProvider { return this.sitesProvider.getCurrentSite().invalidateWsCacheForKey(this.getLtiLaunchDataCacheKey(id)); } + /** + * Check if open in InAppBrowser is disabled. + * + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved with boolean: whether it's disabled. + */ + async isOpenInAppBrowserDisabled(siteId?: string): Promise { + const site = await this.sitesProvider.getSite(siteId); + + return this.isOpenInAppBrowserDisabledInSite(site); + } + + /** + * Check if open in InAppBrowser is disabled. + * + * @param site Site. If not defined, current site. + * @return Whether it's disabled. + */ + isOpenInAppBrowserDisabledInSite(site?: CoreSite): boolean { + site = site || this.sitesProvider.getCurrentSite(); + + return site.isFeatureDisabled('CoreCourseModuleDelegate_AddonModLti:openInAppBrowser'); + } + /** * Launch LTI. * @@ -233,6 +259,8 @@ export class AddonModLtiProvider { } } +export class AddonModLti extends makeSingleton(AddonModLtiProvider) {} + /** * LTI returned by mod_lti_get_ltis_by_courses. */ diff --git a/src/addon/mod/lti/providers/module-handler.ts b/src/addon/mod/lti/providers/module-handler.ts index e6aa945c5..eba92ce10 100644 --- a/src/addon/mod/lti/providers/module-handler.ts +++ b/src/addon/mod/lti/providers/module-handler.ts @@ -18,11 +18,11 @@ import { DomSanitizer } from '@angular/platform-browser'; 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'; +import { AddonModLtiHelper } from './helper'; import { CoreConstants } from '@core/constants'; /** @@ -46,7 +46,6 @@ export class AddonModLtiModuleHandler implements CoreCourseModuleHandler { constructor(private appProvider: CoreAppProvider, private courseProvider: CoreCourseProvider, - private domUtils: CoreDomUtilsProvider, private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider, private ltiProvider: AddonModLtiProvider, @@ -85,26 +84,8 @@ export class AddonModLtiModuleHandler implements CoreCourseModuleHandler { 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, ltiData.name).then(() => { - this.courseProvider.checkModuleCompletion(courseId, module.completiondata); - }).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(); - }); + // Launch the LTI. + AddonModLtiHelper.instance.getDataAndLaunch(courseId, module); } }] };