From f5a7da148ff23e7c8e8ab26769e37a15e5cdaa49 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Thu, 4 Mar 2021 08:15:44 +0100 Subject: [PATCH] MOBILE-3664 siteplugins: Implement directives --- src/core/features/compile/services/compile.ts | 49 +++---- .../classes/call-ws-click-directive.ts | 82 +++++++++++ .../siteplugins/classes/call-ws-directive.ts | 135 ++++++++++++++++++ .../directives/call-ws-new-content.ts | 119 +++++++++++++++ .../siteplugins/directives/call-ws-on-load.ts | 57 ++++++++ .../siteplugins/directives/call-ws.ts | 81 +++++++++++ .../directives/directives.module.ts | 37 +++++ .../siteplugins/directives/new-content.ts | 117 +++++++++++++++ 8 files changed, 651 insertions(+), 26 deletions(-) create mode 100644 src/core/features/siteplugins/classes/call-ws-click-directive.ts create mode 100644 src/core/features/siteplugins/classes/call-ws-directive.ts create mode 100644 src/core/features/siteplugins/directives/call-ws-new-content.ts create mode 100644 src/core/features/siteplugins/directives/call-ws-on-load.ts create mode 100644 src/core/features/siteplugins/directives/call-ws.ts create mode 100644 src/core/features/siteplugins/directives/directives.module.ts create mode 100644 src/core/features/siteplugins/directives/new-content.ts diff --git a/src/core/features/compile/services/compile.ts b/src/core/features/compile/services/compile.ts index 704968c10..b44a96b8c 100644 --- a/src/core/features/compile/services/compile.ts +++ b/src/core/features/compile/services/compile.ts @@ -64,7 +64,7 @@ import { CORE_SITEHOME_SERVICES } from '@features/sitehome/sitehome.module'; import { CORE_TAG_SERVICES } from '@features/tag/tag.module'; import { CORE_USER_SERVICES } from '@features/user/user.module'; import { CORE_XAPI_SERVICES } from '@features/xapi/xapi.module'; -// @todo import { CoreSitePluginsProvider } from '@features/siteplugins/services/siteplugins'; +import { CoreSitePluginsProvider } from '@features/siteplugins/services/siteplugins'; // Import other libraries and providers. import { DomSanitizer } from '@angular/platform-browser'; @@ -93,7 +93,7 @@ import { CoreSharedModule } from '@/core/shared.module'; import { CoreCourseComponentsModule } from '@features/course/components/components.module'; import { CoreCourseDirectivesModule } from '@features/course/directives/directives.module'; import { CoreCoursesComponentsModule } from '@features/courses/components/components.module'; -// @todo import { CoreSitePluginsDirectivesModule } from '@features/siteplugins/directives/directives.module'; +import { CoreSitePluginsDirectivesModule } from '@features/siteplugins/directives/directives.module'; import { CoreUserComponentsModule } from '@features/user/components/components.module'; import { CoreQuestionComponentsModule } from '@features/question/components/components.module'; import { CoreBlockComponentsModule } from '@features/block/components/components.module'; @@ -103,17 +103,15 @@ import { CoreSearchComponentsModule } from '@features/search/components/componen // Import some components so they can be injected dynamically. import { CoreCourseUnsupportedModuleComponent } from '@features/course/components/unsupported-module/unsupported-module'; import { CoreCourseFormatSingleActivityComponent } from '@features/course/format/singleactivity/components/singleactivity'; -// @todo -// import { CoreSitePluginsModuleIndexComponent } from '@features/siteplugins/components/module-index/module-index'; -// import { CoreSitePluginsBlockComponent } from '@features/siteplugins/components/block/block'; -// import { CoreSitePluginsCourseOptionComponent } from '@features/siteplugins/components/course-option/course-option'; -// import { CoreSitePluginsCourseFormatComponent } from '@features/siteplugins/components/course-format/course-format'; -// import { CoreSitePluginsQuestionComponent } from '@features/siteplugins/components/question/question'; -// import { CoreSitePluginsQuestionBehaviourComponent } from '@features/siteplugins/components/question-behaviour/question-behaviour'; -// import { CoreSitePluginsUserProfileFieldComponent } from '@features/siteplugins/components/user-profile-field/user-profile-field'; -// import { CoreSitePluginsQuizAccessRuleComponent } from '@features/siteplugins/components/quiz-access-rule/quiz-access-rule'; -// import { CoreSitePluginsAssignFeedbackComponent } from '@features/siteplugins/components/assign-feedback/assign-feedback'; -// import { CoreSitePluginsAssignSubmissionComponent } from '@features/siteplugins/components/assign-submission/assign-submission'; +import { CoreSitePluginsModuleIndexComponent } from '@features/siteplugins/components/module-index/module-index'; +import { CoreSitePluginsBlockComponent } from '@features/siteplugins/components/block/block'; +import { CoreSitePluginsCourseFormatComponent } from '@features/siteplugins/components/course-format/course-format'; +import { CoreSitePluginsQuestionComponent } from '@features/siteplugins/components/question/question'; +import { CoreSitePluginsQuestionBehaviourComponent } from '@features/siteplugins/components/question-behaviour/question-behaviour'; +import { CoreSitePluginsUserProfileFieldComponent } from '@features/siteplugins/components/user-profile-field/user-profile-field'; +import { CoreSitePluginsQuizAccessRuleComponent } from '@features/siteplugins/components/quiz-access-rule/quiz-access-rule'; +import { CoreSitePluginsAssignFeedbackComponent } from '@features/siteplugins/components/assign-feedback/assign-feedback'; +import { CoreSitePluginsAssignSubmissionComponent } from '@features/siteplugins/components/assign-submission/assign-submission'; // Import addon providers. Do not import database module because it causes circular dependencies. import { ADDON_BADGES_SERVICES } from '@addons/badges/badges.module'; @@ -169,8 +167,8 @@ export class CoreCompileProvider { protected readonly IMPORTS = [ CoreSharedModule, CoreCourseComponentsModule, CoreCoursesComponentsModule, CoreUserComponentsModule, CoreCourseDirectivesModule, CoreQuestionComponentsModule, AddonModAssignComponentsModule, - CoreBlockComponentsModule, CoreEditorComponentsModule, CoreSearchComponentsModule, - // @todo AddonModWorkshopComponentsModule, CoreSitePluginsDirectivesModule, + CoreBlockComponentsModule, CoreEditorComponentsModule, CoreSearchComponentsModule, CoreSitePluginsDirectivesModule, + // @todo AddonModWorkshopComponentsModule, ]; constructor(protected injector: Injector, compilerFactory: JitCompilerFactory) { @@ -272,7 +270,7 @@ export class CoreCompileProvider { ...CORE_SETTINGS_SERVICES, // @todo ...CORE_SHAREDFILES_SERVICES, ...CORE_SITEHOME_SERVICES, - // @todo ...CoreSitePluginsProvider, + CoreSitePluginsProvider, ...CORE_TAG_SERVICES, ...CORE_USER_SERVICES, ...CORE_XAPI_SERVICES, @@ -348,16 +346,15 @@ export class CoreCompileProvider { instance['CoreCourseResourcePrefetchHandlerBase'] = CoreCourseResourcePrefetchHandlerBase; instance['CoreCourseUnsupportedModuleComponent'] = CoreCourseUnsupportedModuleComponent; instance['CoreCourseFormatSingleActivityComponent'] = CoreCourseFormatSingleActivityComponent; - // @todo instance['CoreSitePluginsModuleIndexComponent'] = CoreSitePluginsModuleIndexComponent; - // instance['CoreSitePluginsBlockComponent'] = CoreSitePluginsBlockComponent; - // instance['CoreSitePluginsCourseOptionComponent'] = CoreSitePluginsCourseOptionComponent; - // instance['CoreSitePluginsCourseFormatComponent'] = CoreSitePluginsCourseFormatComponent; - // instance['CoreSitePluginsQuestionComponent'] = CoreSitePluginsQuestionComponent; - // instance['CoreSitePluginsQuestionBehaviourComponent'] = CoreSitePluginsQuestionBehaviourComponent; - // instance['CoreSitePluginsUserProfileFieldComponent'] = CoreSitePluginsUserProfileFieldComponent; - // instance['CoreSitePluginsQuizAccessRuleComponent'] = CoreSitePluginsQuizAccessRuleComponent; - // instance['CoreSitePluginsAssignFeedbackComponent'] = CoreSitePluginsAssignFeedbackComponent; - // instance['CoreSitePluginsAssignSubmissionComponent'] = CoreSitePluginsAssignSubmissionComponent; + instance['CoreSitePluginsModuleIndexComponent'] = CoreSitePluginsModuleIndexComponent; + instance['CoreSitePluginsBlockComponent'] = CoreSitePluginsBlockComponent; + instance['CoreSitePluginsCourseFormatComponent'] = CoreSitePluginsCourseFormatComponent; + instance['CoreSitePluginsQuestionComponent'] = CoreSitePluginsQuestionComponent; + instance['CoreSitePluginsQuestionBehaviourComponent'] = CoreSitePluginsQuestionBehaviourComponent; + instance['CoreSitePluginsUserProfileFieldComponent'] = CoreSitePluginsUserProfileFieldComponent; + instance['CoreSitePluginsQuizAccessRuleComponent'] = CoreSitePluginsQuizAccessRuleComponent; + instance['CoreSitePluginsAssignFeedbackComponent'] = CoreSitePluginsAssignFeedbackComponent; + instance['CoreSitePluginsAssignSubmissionComponent'] = CoreSitePluginsAssignSubmissionComponent; instance['CoreGeolocationError'] = CoreGeolocationError; instance['CoreGeolocationErrorReason'] = CoreGeolocationErrorReason; } diff --git a/src/core/features/siteplugins/classes/call-ws-click-directive.ts b/src/core/features/siteplugins/classes/call-ws-click-directive.ts new file mode 100644 index 000000000..4e822ba1c --- /dev/null +++ b/src/core/features/siteplugins/classes/call-ws-click-directive.ts @@ -0,0 +1,82 @@ +// (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 { Input, OnInit, ElementRef, Directive } from '@angular/core'; + +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreUtils } from '@services/utils/utils'; +import { Translate } from '@singletons'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; +import { CoreSitePluginsCallWSBaseDirective } from './call-ws-directive'; + +/** + * Base class for directives to call a WS when the element is clicked. + * + * The directives that inherit from this class will call a WS method when the element is clicked. + */ +@Directive() +export class CoreSitePluginsCallWSOnClickBaseDirective extends CoreSitePluginsCallWSBaseDirective implements OnInit { + + @Input() confirmMessage?: string; // Message to confirm the action. If not supplied, no confirmation. If empty, default message. + @Input() showError?: boolean | string; // Whether to show an error message if the WS call fails. Defaults to true. + + constructor( + element: ElementRef, + parentContent: CoreSitePluginsPluginContentComponent | null, + ) { + super(element, parentContent); + } + + /** + * @inheritdoc + */ + ngOnInit(): void { + super.ngOnInit(); + + this.element.addEventListener('click', async (ev: Event) => { + ev.preventDefault(); + ev.stopPropagation(); + + if (typeof this.confirmMessage != 'undefined') { + // Ask for confirm. + try { + await CoreDomUtils.showConfirm(this.confirmMessage || Translate.instant('core.areyousure')); + } catch { + // User cancelled, stop. + return; + } + } + + this.callWS(); + }); + } + + /** + * @inheritdoc + */ + protected async callWS(): Promise { + const modal = await CoreDomUtils.showModalLoading(); + + try { + await super.callWS(); + } catch (error) { + if (typeof this.showError == 'undefined' || CoreUtils.isTrueOrOne(this.showError)) { + CoreDomUtils.showErrorModalDefault(error, 'core.serverconnection', true); + } + } finally { + modal.dismiss(); + } + } + +} diff --git a/src/core/features/siteplugins/classes/call-ws-directive.ts b/src/core/features/siteplugins/classes/call-ws-directive.ts new file mode 100644 index 000000000..90ae3ab2a --- /dev/null +++ b/src/core/features/siteplugins/classes/call-ws-directive.ts @@ -0,0 +1,135 @@ +// (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 { Input, OnInit, OnDestroy, ElementRef, Output, EventEmitter, Directive } from '@angular/core'; +import { Subscription } from 'rxjs'; + +import { CoreSiteWSPreSets } from '@classes/site'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; +import { CoreSitePlugins } from '../services/siteplugins'; +import { CoreLogger } from '@singletons/logger'; + +/** + * Base class for directives that need to call a WS. + */ +@Directive() +export class CoreSitePluginsCallWSBaseDirective implements OnInit, OnDestroy { + + @Input() name!: string; // The name of the WS to call. + @Input() params?: Record; // The params for the WS call. + @Input() preSets?: CoreSiteWSPreSets; // The preSets for the WS call. + @Input() useOtherDataForWS?: string[] | unknown; // Whether to include other data in the params for the WS. + @Input() form?: string; // ID or name to identify a form. The form data will be retrieved and sent to the WS. + @Output() onSuccess = new EventEmitter(); // Sends the result when the WS call succeeds. + @Output() onError = new EventEmitter(); // Sends the error when the WS call fails. + @Output() onDone = new EventEmitter(); // Notifies when the WS call is done (either success or fail). + + protected logger: CoreLogger; + protected element: HTMLElement; + protected invalidateObserver?: Subscription; + + constructor( + element: ElementRef, + protected parentContent: CoreSitePluginsPluginContentComponent | null, + ) { + this.element = element.nativeElement || element; + this.logger = CoreLogger.getInstance('CoreSitePluginsCallWS'); + } + + /** + * @inheritdoc + */ + ngOnInit(): void { + if (!this.parentContent?.invalidateObservable) { + return; + } + + this.invalidateObserver = this.parentContent.invalidateObservable.subscribe(() => { + this.invalidate(); + }); + } + + /** + * Call a WS. + * + * @return Promise resolved when done. + */ + protected async callWS(): Promise { + try { + const params = this.getParamsForWS(); + + const result = await CoreSitePlugins.callWS(this.name, params, this.preSets); + + this.onSuccess.emit(result); + + return this.wsCallSuccess(result); + } catch (error) { + this.onError.emit(error); + this.logger.error(`Error calling WS ${this.name}`, error); + + throw error; + } finally { + this.onDone.emit(); + } + } + + /** + * Get the params for the WS call. + * + * @return Params. + */ + protected getParamsForWS(): Record { + let params = this.params || {}; + + if (this.parentContent) { + params = CoreSitePlugins.loadOtherDataInArgs(params, this.parentContent.otherData, this.useOtherDataForWS); + } + + if (this.form && document.forms[this.form]) { + params = Object.assign(params, CoreDomUtils.getDataFromForm(document.forms[this.form])); + } + + return params; + } + + /** + * Function called when the WS call is successful. + * + * @param result Result of the WS call. + */ + // eslint-disable-next-line @typescript-eslint/no-unused-vars + protected wsCallSuccess(result: unknown): void { + // Function to be overridden. + } + + /** + * Invalidate the WS call. + * + * @return Promise resolved when done. + */ + invalidate(): Promise { + const params = this.getParamsForWS(); + + return CoreSitePlugins.instance.invalidateCallWS(this.name, params, this.preSets); + } + + /** + * Directive destroyed. + */ + ngOnDestroy(): void { + this.invalidateObserver?.unsubscribe(); + } + +} diff --git a/src/core/features/siteplugins/directives/call-ws-new-content.ts b/src/core/features/siteplugins/directives/call-ws-new-content.ts new file mode 100644 index 000000000..54c773fd9 --- /dev/null +++ b/src/core/features/siteplugins/directives/call-ws-new-content.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 { Directive, Input, ElementRef, Optional } from '@angular/core'; +import { CoreSiteWSPreSets } from '@classes/site'; +import { CoreNavigator } from '@services/navigator'; +import { CoreUtils } from '@services/utils/utils'; +import { Md5 } from 'ts-md5'; + +import { CoreSitePluginsCallWSOnClickBaseDirective } from '../classes/call-ws-click-directive'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; +import { CoreSitePlugins } from '../services/siteplugins'; + +/** + * Directive to call a WS when the element is clicked and load a new content passing the WS result as args. This new content + * can be displayed in a new page or in the same page (only if current page is already displaying a site plugin content). + * + * If you don't need to load some new content when done, @see CoreSitePluginsCallWSDirective. + * + * @see CoreSitePluginsCallWSOnClickBaseDirective. + * + * Example usages: + * + * A button to get some data from the server without using cache, showing default confirm and displaying a new page: + * + * + * {{ 'plugin.mod_certificate_coursecertificate.getissued' | translate }} + * + * + * A button to get some data from the server using cache, without confirm, displaying new content in same page and using + * userid from otherdata: + * + * + * {{ 'plugin.mod_certificate_coursecertificate.getissued' | translate }} + * + */ +@Directive({ + selector: '[core-site-plugins-call-ws-new-content]', +}) +export class CoreSitePluginsCallWSNewContentDirective extends CoreSitePluginsCallWSOnClickBaseDirective { + + @Input() component?: string; // The component of the new content. If not provided, use the same component as current page. + @Input() method?: string; // The method to get the new content. If not provided, use the same method as current page. + @Input() args?: Record; // The params to get the new content. + @Input() title?: string; // The title to display with the new content. Only if samePage=false. + @Input() samePage?: boolean | string; // Whether to display the content in same page or open a new one. Defaults to new page. + @Input() useOtherData?: string[] | unknown; // Whether to include other data in the args. + @Input() form?: string; // ID or name to identify a form. The form data will be retrieved and sent to the WS. + // JS variables to pass to the new page so they can be used in the template or JS. + // If true is supplied instead of an object, all initial variables from current page will be copied. + @Input() jsData?: Record | boolean; + @Input() newContentPreSets?: CoreSiteWSPreSets; // The preSets for the WS call of the new content. + @Input() ptrEnabled?: boolean | string; // Whether PTR should be enabled in the new page. Defaults to true. + + constructor( + element: ElementRef, + @Optional() parentContent: CoreSitePluginsPluginContentComponent, + ) { + super(element, parentContent); + } + + /** + * Function called when the WS call is successful. + * + * @param result Result of the WS call. + */ + protected wsCallSuccess(result: unknown): void { + let args = this.args || {}; + + if (this.parentContent) { + args = CoreSitePlugins.loadOtherDataInArgs(this.args, this.parentContent.otherData, this.useOtherData); + } + + // Add the properties from the WS call result to the args. + args = Object.assign(args, result); + + let jsData = this.jsData || {}; + if (jsData === true) { + jsData = this.parentContent?.data || {}; + } + + if (CoreUtils.isTrueOrOne(this.samePage)) { + // Update the parent content (if it exists). + this.parentContent?.updateContent(args, this.component, this.method, jsData, this.newContentPreSets); + } else { + const component = this.component || this.parentContent?.component; + const method = this.method || this.parentContent?.method; + const hash = Md5.hashAsciiStr(JSON.stringify(args)); + + CoreNavigator.navigateToSitePath(`siteplugins/${component}/${method}/${hash}`, { + params: { + title: this.title || this.parentContent?.pageTitle, + args, + initResult: this.parentContent?.initResult, + jsData, + preSets: this.newContentPreSets, + ptrEnabled: this.ptrEnabled, + }, + }); + } + } + +} diff --git a/src/core/features/siteplugins/directives/call-ws-on-load.ts b/src/core/features/siteplugins/directives/call-ws-on-load.ts new file mode 100644 index 000000000..50c3d3d88 --- /dev/null +++ b/src/core/features/siteplugins/directives/call-ws-on-load.ts @@ -0,0 +1,57 @@ +// (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 { Directive, OnInit, ElementRef, Optional } from '@angular/core'; + +import { CoreSitePluginsCallWSBaseDirective } from '../classes/call-ws-directive'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; + +/** + * Directive to call a WS as soon as its loaded. + * This directive is meant for actions to do in the background, like calling logging WebServices. + * + * If you want to call a WS when the user clicks on a certain element, @see CoreSitePluginsCallWSDirective. + * + * @see CoreSitePluginsCallWSBaseDirective. + * + * Example usage: + * + * + */ +@Directive({ + selector: '[core-site-plugins-call-ws-on-load]', +}) +export class CoreSitePluginsCallWSOnLoadDirective extends CoreSitePluginsCallWSBaseDirective implements OnInit { + + constructor( + element: ElementRef, + @Optional() parentContent: CoreSitePluginsPluginContentComponent, + ) { + super(element, parentContent); + } + + /** + * @inheritdoc + */ + ngOnInit(): void { + super.ngOnInit(); + + // Call the WS immediately. + this.callWS().catch(() => { + // Ignore errors. + }); + } + +} diff --git a/src/core/features/siteplugins/directives/call-ws.ts b/src/core/features/siteplugins/directives/call-ws.ts new file mode 100644 index 000000000..71c5f82c3 --- /dev/null +++ b/src/core/features/siteplugins/directives/call-ws.ts @@ -0,0 +1,81 @@ +// (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 { Directive, Input, ElementRef, Optional } from '@angular/core'; + +import { Translate } from '@singletons'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreUtils } from '@services/utils/utils'; +import { CoreNavigator } from '@services/navigator'; +import { CoreSitePluginsCallWSOnClickBaseDirective } from '../classes/call-ws-click-directive'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; + +/** + * Directive to call a WS when the element is clicked. The action to do when the WS call is successful depends on the input data: + * display a message, go back or refresh current view. + * + * If you want to load a new content when the WS call is done, @see CoreSitePluginsCallWSNewContentDirective. + * + * @see CoreSitePluginsCallWSOnClickBaseDirective. + * + * Example usages: + * + * A button to send some data to the server without using cache, displaying default messages and refreshing on success: + * + * + * {{ 'plugin.mod_certificate_coursecertificate.senddata' | translate }} + * + * + * A button to send some data to the server using cache, without confirm, going back on success and using userid from otherdata: + * + * + * {{ 'plugin.mod_certificate_coursecertificate.senddata' | translate }} + * + */ +@Directive({ + selector: '[core-site-plugins-call-ws]', +}) +export class CoreSitePluginsCallWSDirective extends CoreSitePluginsCallWSOnClickBaseDirective { + + @Input() successMessage?: string; // Message to show on success. If not supplied, no message. If empty, default message. + @Input() goBackOnSuccess?: boolean | string; // Whether to go back if the WS call is successful. + @Input() refreshOnSuccess?: boolean | string; // Whether to refresh the current view if the WS call is successful. + + constructor( + element: ElementRef, + @Optional() parentContent: CoreSitePluginsPluginContentComponent, + ) { + super(element, parentContent); + } + + /** + * @inheritdoc + */ + protected wsCallSuccess(): void { + if (typeof this.successMessage != 'undefined') { + // Display the success message. + CoreDomUtils.showToast(this.successMessage || Translate.instant('core.success')); + } + + if (CoreUtils.isTrueOrOne(this.goBackOnSuccess)) { + CoreNavigator.back(); + } else if (CoreUtils.isTrueOrOne(this.refreshOnSuccess) && this.parentContent) { + this.parentContent.refreshContent(true); + } + } + +} diff --git a/src/core/features/siteplugins/directives/directives.module.ts b/src/core/features/siteplugins/directives/directives.module.ts new file mode 100644 index 000000000..d35d8e0fe --- /dev/null +++ b/src/core/features/siteplugins/directives/directives.module.ts @@ -0,0 +1,37 @@ +// (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 { NgModule } from '@angular/core'; + +import { CoreSitePluginsCallWSDirective } from './call-ws'; +import { CoreSitePluginsCallWSNewContentDirective } from './call-ws-new-content'; +import { CoreSitePluginsCallWSOnLoadDirective } from './call-ws-on-load'; +import { CoreSitePluginsNewContentDirective } from './new-content'; + +@NgModule({ + declarations: [ + CoreSitePluginsCallWSDirective, + CoreSitePluginsCallWSNewContentDirective, + CoreSitePluginsCallWSOnLoadDirective, + CoreSitePluginsNewContentDirective, + ], + imports: [], + exports: [ + CoreSitePluginsCallWSDirective, + CoreSitePluginsCallWSNewContentDirective, + CoreSitePluginsCallWSOnLoadDirective, + CoreSitePluginsNewContentDirective, + ], +}) +export class CoreSitePluginsDirectivesModule {} diff --git a/src/core/features/siteplugins/directives/new-content.ts b/src/core/features/siteplugins/directives/new-content.ts new file mode 100644 index 000000000..6faee7123 --- /dev/null +++ b/src/core/features/siteplugins/directives/new-content.ts @@ -0,0 +1,117 @@ +// (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 { Directive, Input, OnInit, ElementRef, Optional } from '@angular/core'; +import { Md5 } from 'ts-md5'; + +import { CoreSiteWSPreSets } from '@classes/site'; +import { CoreNavigator } from '@services/navigator'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreUtils } from '@services/utils/utils'; +import { CoreSitePluginsPluginContentComponent } from '../components/plugin-content/plugin-content'; +import { CoreSitePlugins } from '../services/siteplugins'; + +/** + * Directive to display a new site plugin content when clicked. This new content can be displayed in a new page or in the + * current page (only if the current page is already displaying a site plugin content). + * + * Example usages: + * + * A button to go to a new content page: + * + * + * {{ 'plugin.mod_certificate_coursecertificate.viewissued' | translate }} + * + * + * A button to load new content in current page using a param from otherdata: + * + * + * {{ 'plugin.mod_certificate_coursecertificate.viewissued' | translate }} + * + */ +@Directive({ + selector: '[core-site-plugins-new-content]', +}) +export class CoreSitePluginsNewContentDirective implements OnInit { + + @Input() component?: string; // The component of the new content. If not provided, use the same component as current page. + @Input() method?: string; // The method to get the new content. If not provided, use the same method as current page. + @Input() args?: Record; // The params to get the new content. + @Input() title?: string; // The title to display with the new content. Only if samePage=false. + @Input() samePage?: boolean | string; // Whether to display the content in same page or open a new one. Defaults to new page. + @Input() useOtherData?: string[] | unknown; // Whether to include other data in the args. + @Input() form?: string; // ID or name to identify a form. The form data will be retrieved and sent to the WS. + // JS variables to pass to the new page so they can be used in the template or JS. + // If true is supplied instead of an object, all initial variables from current page will be copied. + @Input() jsData?: Record | boolean; + @Input() preSets?: CoreSiteWSPreSets; // The preSets for the WS call of the new content. + @Input() ptrEnabled?: boolean | string; // Whether PTR should be enabled in the new page. Defaults to true. + + protected element: HTMLElement; + + constructor( + element: ElementRef, + @Optional() protected parentContent: CoreSitePluginsPluginContentComponent, + ) { + this.element = element.nativeElement || element; + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.element.addEventListener('click', (ev: Event): void => { + ev.preventDefault(); + ev.stopPropagation(); + + let args = this.args || {}; + + if (this.parentContent) { + args = CoreSitePlugins.loadOtherDataInArgs(this.args, this.parentContent.otherData, this.useOtherData); + } + + if (this.form && document.forms[this.form]) { + args = Object.assign(args, CoreDomUtils.getDataFromForm(document.forms[this.form])); + } + + let jsData = this.jsData || {}; + if (jsData === true) { + jsData = this.parentContent?.data || {}; + } + + if (CoreUtils.isTrueOrOne(this.samePage)) { + // Update the parent content (if it exists). + this.parentContent?.updateContent(args, this.component, this.method, jsData, this.preSets); + } else { + const component = this.component || this.parentContent?.component; + const method = this.method || this.parentContent?.method; + const hash = Md5.hashAsciiStr(JSON.stringify(args)); + + CoreNavigator.navigateToSitePath(`siteplugins/${component}/${method}/${hash}`, { + params: { + title: this.title || this.parentContent?.pageTitle, + args, + initResult: this.parentContent?.initResult, + jsData, + preSets: this.preSets, + ptrEnabled: this.ptrEnabled, + }, + }); + } + }); + } + +}