From c4d37b00741a6aa0a187e9fd0a56532b60320694 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Thu, 18 Feb 2021 09:55:59 +0100 Subject: [PATCH] MOBILE-3636 assign: Feedback plugins --- .../feedback-plugin/feedback-plugin.ts | 45 +++ .../feedback/comments/comments.module.ts | 49 ++++ .../addon-mod-assign-feedback-comments.html | 34 +++ .../feedback/comments/component/comments.ts | 161 +++++++++++ .../mod/assign/feedback/comments/lang.json | 3 + .../feedback/comments/services/handler.ts | 266 ++++++++++++++++++ .../addon-mod-assign-feedback-editpdf.html | 11 + .../feedback/editpdf/component/editpdf.ts | 41 +++ .../assign/feedback/editpdf/editpdf.module.ts | 46 +++ .../mod/assign/feedback/editpdf/lang.json | 3 + .../feedback/editpdf/services/handler.ts | 73 +++++ .../mod/assign/feedback/feedback.module.ts | 27 ++ .../addon-mod-assign-feedback-file.html | 11 + .../assign/feedback/file/component/file.ts | 41 +++ .../mod/assign/feedback/file/file.module.ts | 46 +++ src/addons/mod/assign/feedback/file/lang.json | 3 + .../assign/feedback/file/services/handler.ts | 73 +++++ .../mod/assign/services/feedback-delegate.ts | 28 +- .../services/handlers/default-feedback.ts | 3 +- 19 files changed, 954 insertions(+), 10 deletions(-) create mode 100644 src/addons/mod/assign/feedback/comments/comments.module.ts create mode 100644 src/addons/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html create mode 100644 src/addons/mod/assign/feedback/comments/component/comments.ts create mode 100644 src/addons/mod/assign/feedback/comments/lang.json create mode 100644 src/addons/mod/assign/feedback/comments/services/handler.ts create mode 100644 src/addons/mod/assign/feedback/editpdf/component/addon-mod-assign-feedback-editpdf.html create mode 100644 src/addons/mod/assign/feedback/editpdf/component/editpdf.ts create mode 100644 src/addons/mod/assign/feedback/editpdf/editpdf.module.ts create mode 100644 src/addons/mod/assign/feedback/editpdf/lang.json create mode 100644 src/addons/mod/assign/feedback/editpdf/services/handler.ts create mode 100644 src/addons/mod/assign/feedback/feedback.module.ts create mode 100644 src/addons/mod/assign/feedback/file/component/addon-mod-assign-feedback-file.html create mode 100644 src/addons/mod/assign/feedback/file/component/file.ts create mode 100644 src/addons/mod/assign/feedback/file/file.module.ts create mode 100644 src/addons/mod/assign/feedback/file/lang.json create mode 100644 src/addons/mod/assign/feedback/file/services/handler.ts diff --git a/src/addons/mod/assign/components/feedback-plugin/feedback-plugin.ts b/src/addons/mod/assign/components/feedback-plugin/feedback-plugin.ts index 709c8aa72..b84b54808 100644 --- a/src/addons/mod/assign/components/feedback-plugin/feedback-plugin.ts +++ b/src/addons/mod/assign/components/feedback-plugin/feedback-plugin.ts @@ -13,8 +13,11 @@ // limitations under the License. import { Component, Input, OnInit, ViewChild, Type } from '@angular/core'; +import { CoreError } from '@classes/errors/error'; import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component'; import { CoreWSExternalFile } from '@services/ws'; +import { ModalController } from '@singletons'; +import { AddonModAssignFeedbackCommentsPluginData } from '../../feedback/comments/services/handler'; import { AddonModAssignAssign, AddonModAssignSubmission, @@ -24,6 +27,7 @@ import { } from '../../services/assign'; import { AddonModAssignHelper, AddonModAssignPluginConfig } from '../../services/assign-helper'; import { AddonModAssignFeedbackDelegate } from '../../services/feedback-delegate'; +import { AddonModAssignEditFeedbackModalComponent } from '../edit-feedback-modal/edit-feedback-modal'; /** * Component that displays an assignment feedback plugin. @@ -95,6 +99,47 @@ export class AddonModAssignFeedbackPluginComponent implements OnInit { } } + /** + * Open a modal to edit the feedback plugin. + * + * @return Promise resolved with the input data, rejected if cancelled. + */ + editFeedback(): Promise { + if (!this.canEdit) { + throw new CoreError('Cannot edit feedback'); + } + + return new Promise((resolve, reject): void => { + this.showEditFeedbackModal(resolve, reject); + }); + } + + protected async showEditFeedbackModal( + resolve: (value: AddonModAssignFeedbackCommentsPluginData | PromiseLike) => void, + reject: () => void, + ): Promise < void> { + // Create the navigation modal. + const modal = await ModalController.instance.create({ + component: AddonModAssignEditFeedbackModalComponent, + componentProps: { + assign: this.assign, + submission: this.submission, + plugin: this.plugin, + userId: this.userId, + }, + }); + + await modal.present(); + + const result = await modal.onDidDismiss(); + + if (typeof result.data == 'undefined') { + reject(); + } else { + resolve(result.data); + } + } + /** * Invalidate the plugin data. * diff --git a/src/addons/mod/assign/feedback/comments/comments.module.ts b/src/addons/mod/assign/feedback/comments/comments.module.ts new file mode 100644 index 000000000..f0f79618b --- /dev/null +++ b/src/addons/mod/assign/feedback/comments/comments.module.ts @@ -0,0 +1,49 @@ +// (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 { APP_INITIALIZER, NgModule } from '@angular/core'; +import { AddonModAssignFeedbackCommentsHandler } from './services/handler'; +import { AddonModAssignFeedbackCommentsComponent } from './component/comments'; +import { CoreSharedModule } from '@/core/shared.module'; +import { CoreEditorComponentsModule } from '@features/editor/components/components.module'; +import { AddonModAssignFeedbackDelegate } from '../../services/feedback-delegate'; +import { AddonModAssignSubmissionFileHandler } from '../../submission/file/services/handler'; + +@NgModule({ + declarations: [ + AddonModAssignFeedbackCommentsComponent, + ], + imports: [ + CoreSharedModule, + CoreEditorComponentsModule, + ], + providers: [ + AddonModAssignFeedbackCommentsHandler, + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModAssignFeedbackDelegate.instance.registerHandler(AddonModAssignSubmissionFileHandler.instance); + }, + }, + ], + exports: [ + AddonModAssignFeedbackCommentsComponent, + ], + entryComponents: [ + AddonModAssignFeedbackCommentsComponent, + ], +}) +export class AddonModAssignFeedbackCommentsModule {} diff --git a/src/addons/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html b/src/addons/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html new file mode 100644 index 000000000..5b01232c3 --- /dev/null +++ b/src/addons/mod/assign/feedback/comments/component/addon-mod-assign-feedback-comments.html @@ -0,0 +1,34 @@ + + + +

{{ plugin.name }}

+

+ + +

+
+
+
+ + + +
+ + + {{ 'core.notsent' | translate }} + +
+
+ + + + + + + + diff --git a/src/addons/mod/assign/feedback/comments/component/comments.ts b/src/addons/mod/assign/feedback/comments/component/comments.ts new file mode 100644 index 000000000..0bfbf4384 --- /dev/null +++ b/src/addons/mod/assign/feedback/comments/component/comments.ts @@ -0,0 +1,161 @@ +// (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 { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { AddonModAssignFeedbackPluginComponent } from '@addons/mod/assign/components/feedback-plugin/feedback-plugin'; +import { AddonModAssign, AddonModAssignProvider } from '@addons/mod/assign/services/assign'; +import { CoreTextUtils } from '@services/utils/text'; +import { + AddonModAssignFeedbackCommentsDraftData, + AddonModAssignFeedbackCommentsHandler, + AddonModAssignFeedbackCommentsPluginData, +} from '../services/handler'; +import { AddonModAssignFeedbackDelegate } from '@addons/mod/assign/services/feedback-delegate'; +import { AddonModAssignOffline } from '@addons/mod/assign/services/assign-offline'; +import { CoreUtils } from '@services/utils/utils'; +/** + * Component to render a comments feedback plugin. + */ +@Component({ + selector: 'addon-mod-assign-feedback-comments', + templateUrl: 'addon-mod-assign-feedback-comments.html', +}) +export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedbackPluginComponent implements OnInit { + + control?: FormControl; + component = AddonModAssignProvider.COMPONENT; + text = ''; + isSent = false; + loaded = false; + + protected element: HTMLElement; + + constructor( + element: ElementRef, + protected fb: FormBuilder, + ) { + super(); + this.element = element.nativeElement; + } + + /** + * Component being initialized. + */ + async ngOnInit(): Promise { + try { + this.text = await this.getText(); + + if (!this.canEdit && !this.edit) { + // User cannot edit the comment. Show it full when clicked. + this.element.addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + + if (this.text) { + // Open a new state with the text. + CoreTextUtils.instance.viewText(this.plugin.name, this.text, { + component: this.component, + componentId: this.assign.cmid, + filter: true, + contextLevel: 'module', + instanceId: this.assign.cmid, + courseId: this.assign.course, + }); + } + }); + } else if (this.edit) { + this.control = this.fb.control(this.text); + } + } finally { + this.loaded = true; + } + } + + /** + * Edit the comment. + */ + async editComment(): Promise { + try { + const inputData = await this.editFeedback(); + const text = AddonModAssignFeedbackCommentsHandler.instance.getTextFromInputData(this.plugin, inputData); + + // Update the text and save it as draft. + this.isSent = false; + this.text = this.replacePluginfileUrls(text); + AddonModAssignFeedbackDelegate.instance.saveFeedbackDraft(this.assign.id, this.userId, this.plugin, { + text: text, + format: 1, + }); + } catch { + // User cancelled, nothing to do. + } + } + + /** + * Get the text for the plugin. + * + * @return Promise resolved with the text. + */ + protected async getText(): Promise { + // Check if the user already modified the comment. + const draft: AddonModAssignFeedbackCommentsDraftData | undefined = + await AddonModAssignFeedbackDelegate.instance.getPluginDraftData(this.assign.id, this.userId, this.plugin); + + if (draft) { + this.isSent = false; + + return this.replacePluginfileUrls(draft.text); + } + + // There is no draft saved. Check if we have anything offline. + const offlineData = await CoreUtils.instance.ignoreErrors( + AddonModAssignOffline.instance.getSubmissionGrade(this.assign.id, this.userId), + undefined, + ); + + if (offlineData && offlineData.plugindata && offlineData.plugindata.assignfeedbackcomments_editor) { + const pluginData = offlineData.plugindata; + + // Save offline as draft. + this.isSent = false; + AddonModAssignFeedbackDelegate.instance.saveFeedbackDraft( + this.assign.id, + this.userId, + this.plugin, + pluginData.assignfeedbackcomments_editor, + ); + + return this.replacePluginfileUrls(pluginData.assignfeedbackcomments_editor.text); + } + + // No offline data found, return online text. + this.isSent = true; + + return AddonModAssign.instance.getSubmissionPluginText(this.plugin); + } + + /** + * Replace @@PLUGINFILE@@ wildcards with the real URL of embedded files. + * + * @param Text to treat. + * @return Treated text. + */ + replacePluginfileUrls(text: string): string { + const files = this.plugin.fileareas && this.plugin.fileareas[0] && this.plugin.fileareas[0].files; + + return CoreTextUtils.instance.replacePluginfileUrls(text, files || []); + } + +} diff --git a/src/addons/mod/assign/feedback/comments/lang.json b/src/addons/mod/assign/feedback/comments/lang.json new file mode 100644 index 000000000..637363859 --- /dev/null +++ b/src/addons/mod/assign/feedback/comments/lang.json @@ -0,0 +1,3 @@ +{ + "pluginname": "Feedback comments" +} \ No newline at end of file diff --git a/src/addons/mod/assign/feedback/comments/services/handler.ts b/src/addons/mod/assign/feedback/comments/services/handler.ts new file mode 100644 index 000000000..58c98ff34 --- /dev/null +++ b/src/addons/mod/assign/feedback/comments/services/handler.ts @@ -0,0 +1,266 @@ +// (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 { + AddonModAssignPlugin, + AddonModAssignAssign, + AddonModAssignSubmission, + AddonModAssign, + AddonModAssignSavePluginData, +} from '@addons/mod/assign/services/assign'; +import { AddonModAssignOffline } from '@addons/mod/assign/services/assign-offline'; +import { AddonModAssignFeedbackHandler } from '@addons/mod/assign/services/feedback-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreSites } from '@services/sites'; +import { CoreTextUtils } from '@services/utils/text'; +import { CoreUtils } from '@services/utils/utils'; +import { CoreWSExternalFile } from '@services/ws'; +import { makeSingleton } from '@singletons'; +import { AddonModAssignFeedbackCommentsComponent } from '../component/comments'; + +/** + * Handler for comments feedback plugin. + */ +@Injectable( { providedIn: 'root' }) +export class AddonModAssignFeedbackCommentsHandlerService implements AddonModAssignFeedbackHandler { + + name = 'AddonModAssignFeedbackCommentsHandler'; + type = 'comments'; + + // Store the data in this service so it isn't lost if the user performs a PTR in the page. + protected drafts: { [draftId: string]: AddonModAssignFeedbackCommentsDraftData } = {}; + + /** + * Get the text to submit. + * + * @param textUtils Text utils instance. + * @param plugin Plugin. + * @param inputData Data entered in the feedback edit form. + * @return Text to submit. + */ + getTextFromInputData(plugin: AddonModAssignPlugin, inputData: AddonModAssignFeedbackCommentsPluginData): string { + const files = plugin.fileareas && plugin.fileareas[0] ? plugin.fileareas[0].files : []; + let text = ''; + + // The input data can have a string or an object with text and format. Get the text. + if (inputData.assignfeedbackcomments_editor && inputData.assignfeedbackcomments_editor.text) { + text = inputData.assignfeedbackcomments_editor.text; + } + + return CoreTextUtils.instance.restorePluginfileUrls(text, files || []); + } + + /** + * Discard the draft data of the feedback plugin. + * + * @param assignId The assignment ID. + * @param userId User ID. + * @param siteId Site ID. If not defined, current site. + * @return If the function is async, it should return a Promise resolved when done. + */ + discardDraft(assignId: number, userId: number, siteId?: string): void { + const id = this.getDraftId(assignId, userId, siteId); + if (typeof this.drafts[id] != 'undefined') { + delete this.drafts[id]; + } + } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @return The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(): Type { + return AddonModAssignFeedbackCommentsComponent; + } + + /** + * Return the draft saved data of the feedback plugin. + * + * @param assignId The assignment ID. + * @param userId User ID. + * @param siteId Site ID. If not defined, current site. + * @return Data (or promise resolved with the data). + */ + getDraft(assignId: number, userId: number, siteId?: string): AddonModAssignFeedbackCommentsDraftData | undefined { + const id = this.getDraftId(assignId, userId, siteId); + + if (typeof this.drafts[id] != 'undefined') { + return this.drafts[id]; + } + } + + /** + * Get a draft ID. + * + * @param assignId The assignment ID. + * @param userId User ID. + * @param siteId Site ID. If not defined, current site. + * @return Draft ID. + */ + protected getDraftId(assignId: number, userId: number, siteId?: string): string { + siteId = siteId || CoreSites.instance.getCurrentSiteId(); + + return siteId + '#' + assignId + '#' + userId; + } + + /** + * Get files used by this plugin. + * The files returned by this function will be prefetched when the user prefetches the assign. + * + * @param assign The assignment. + * @param submission The submission. + * @param plugin The plugin object. + * @return The files (or promise resolved with the files). + */ + getPluginFiles( + assign: AddonModAssignAssign, + submission: AddonModAssignSubmission, + plugin: AddonModAssignPlugin, + ): CoreWSExternalFile[] { + return AddonModAssign.instance.getSubmissionPluginAttachments(plugin); + } + + /** + * Check if the feedback data has changed for this plugin. + * + * @param assign The assignment. + * @param submission The submission. + * @param plugin The plugin object. + * @param inputData Data entered by the user for the feedback. + * @param userId User ID of the submission. + * @return Boolean (or promise resolved with boolean): whether the data has changed. + */ + async hasDataChanged( + assign: AddonModAssignAssign, + submission: AddonModAssignSubmission, + plugin: AddonModAssignPlugin, + inputData: AddonModAssignFeedbackCommentsPluginData, + userId: number, + ): Promise { + // Get it from plugin or offline. + const offlineData = await CoreUtils.instance.ignoreErrors( + AddonModAssignOffline.instance.getSubmissionGrade(assign.id, userId), + undefined, + ); + + if (offlineData?.plugindata?.assignfeedbackcomments_editor) { + const pluginData = offlineData.plugindata; + + return !!pluginData.assignfeedbackcomments_editor.text; + } + + // No offline data found, get text from plugin. + const initialText = AddonModAssign.instance.getSubmissionPluginText(plugin); + const newText = AddonModAssignFeedbackCommentsHandler.instance.getTextFromInputData(plugin, inputData); + + if (typeof newText == 'undefined') { + return false; + } + + // Check if text has changed. + return initialText != newText; + } + + /** + * Check whether the plugin has draft data stored. + * + * @param assignId The assignment ID. + * @param userId User ID. + * @param siteId Site ID. If not defined, current site. + * @return Boolean or promise resolved with boolean: whether the plugin has draft data. + */ + hasDraftData(assignId: number, userId: number, siteId?: string): boolean | Promise { + const draft = this.getDraft(assignId, userId, siteId); + + return !!draft; + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return True or promise resolved with true if enabled. + */ + async isEnabled(): Promise { + // In here we should check if comments is not disabled in site. + // But due to this is not a common comments place and it can be disabled separately into Moodle (disabling the plugin). + // We are leaving it always enabled. It's also a teacher's feature. + return true; + } + + /** + * Prepare and add to pluginData the data to send to the server based on the draft data saved. + * + * @param assignId The assignment ID. + * @param userId User ID. + * @param plugin The plugin object. + * @param pluginData Object where to store the data to send. + * @param siteId Site ID. If not defined, current site. + * @return If the function is async, it should return a Promise resolved when done. + */ + prepareFeedbackData( + assignId: number, + userId: number, + plugin: AddonModAssignPlugin, + pluginData: AddonModAssignSavePluginData, + siteId?: string, + ): void { + + const draft = this.getDraft(assignId, userId, siteId); + + if (draft) { + // Add some HTML to the text if needed. + draft.text = CoreTextUtils.instance.formatHtmlLines(draft.text); + + pluginData.assignfeedbackcomments_editor = draft; + } + } + + /** + * Save draft data of the feedback plugin. + * + * @param assignId The assignment ID. + * @param userId User ID. + * @param plugin The plugin object. + * @param data The data to save. + * @param siteId Site ID. If not defined, current site. + * @return If the function is async, it should return a Promise resolved when done. + */ + saveDraft( + assignId: number, + userId: number, + plugin: AddonModAssignPlugin, + data: AddonModAssignFeedbackCommentsDraftData, + siteId?: string, + ): void { + + if (data) { + this.drafts[this.getDraftId(assignId, userId, siteId)] = data; + } + } + +} +export const AddonModAssignFeedbackCommentsHandler = makeSingleton(AddonModAssignFeedbackCommentsHandlerService); + +export type AddonModAssignFeedbackCommentsDraftData = { + text: string; // The text for this feedback. + format: number; // The format for this feedback. +}; + +export type AddonModAssignFeedbackCommentsPluginData = { + // Editor structure. + // eslint-disable-next-line @typescript-eslint/naming-convention + assignfeedbackcomments_editor: AddonModAssignFeedbackCommentsDraftData; +}; diff --git a/src/addons/mod/assign/feedback/editpdf/component/addon-mod-assign-feedback-editpdf.html b/src/addons/mod/assign/feedback/editpdf/component/addon-mod-assign-feedback-editpdf.html new file mode 100644 index 000000000..d6f66aad3 --- /dev/null +++ b/src/addons/mod/assign/feedback/editpdf/component/addon-mod-assign-feedback-editpdf.html @@ -0,0 +1,11 @@ + + + +

{{plugin.name}}

+ + + + +
+
diff --git a/src/addons/mod/assign/feedback/editpdf/component/editpdf.ts b/src/addons/mod/assign/feedback/editpdf/component/editpdf.ts new file mode 100644 index 000000000..f77089ec6 --- /dev/null +++ b/src/addons/mod/assign/feedback/editpdf/component/editpdf.ts @@ -0,0 +1,41 @@ +// (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 { AddonModAssignFeedbackPluginComponent } from '@addons/mod/assign/components/feedback-plugin/feedback-plugin'; +import { AddonModAssignProvider, AddonModAssign } from '@addons/mod/assign/services/assign'; +import { Component, OnInit } from '@angular/core'; +import { CoreWSExternalFile } from '@services/ws'; + +/** + * Component to render a edit pdf feedback plugin. + */ +@Component({ + selector: 'addon-mod-assign-feedback-edit-pdf', + templateUrl: 'addon-mod-assign-feedback-editpdf.html', +}) +export class AddonModAssignFeedbackEditPdfComponent extends AddonModAssignFeedbackPluginComponent implements OnInit { + + component = AddonModAssignProvider.COMPONENT; + files: CoreWSExternalFile[] = []; + + /** + * Component being initialized. + */ + async ngOnInit(): Promise { + if (this.plugin) { + this.files = AddonModAssign.instance.getSubmissionPluginAttachments(this.plugin); + } + } + +} diff --git a/src/addons/mod/assign/feedback/editpdf/editpdf.module.ts b/src/addons/mod/assign/feedback/editpdf/editpdf.module.ts new file mode 100644 index 000000000..1db4ecd21 --- /dev/null +++ b/src/addons/mod/assign/feedback/editpdf/editpdf.module.ts @@ -0,0 +1,46 @@ +// (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 { APP_INITIALIZER, NgModule } from '@angular/core'; +import { AddonModAssignFeedbackEditPdfHandler } from './services/handler'; +import { AddonModAssignFeedbackEditPdfComponent } from './component/editpdf'; +import { CoreSharedModule } from '@/core/shared.module'; +import { AddonModAssignFeedbackDelegate } from '../../services/feedback-delegate'; + +@NgModule({ + declarations: [ + AddonModAssignFeedbackEditPdfComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + AddonModAssignFeedbackEditPdfHandler, + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModAssignFeedbackDelegate.instance.registerHandler(AddonModAssignFeedbackEditPdfHandler.instance); + }, + }, + ], + exports: [ + AddonModAssignFeedbackEditPdfComponent, + ], + entryComponents: [ + AddonModAssignFeedbackEditPdfComponent, + ], +}) +export class AddonModAssignFeedbackEditPdfModule {} diff --git a/src/addons/mod/assign/feedback/editpdf/lang.json b/src/addons/mod/assign/feedback/editpdf/lang.json new file mode 100644 index 000000000..a98c70fd9 --- /dev/null +++ b/src/addons/mod/assign/feedback/editpdf/lang.json @@ -0,0 +1,3 @@ +{ + "pluginname": "Annotate PDF" +} \ No newline at end of file diff --git a/src/addons/mod/assign/feedback/editpdf/services/handler.ts b/src/addons/mod/assign/feedback/editpdf/services/handler.ts new file mode 100644 index 000000000..216ba11c1 --- /dev/null +++ b/src/addons/mod/assign/feedback/editpdf/services/handler.ts @@ -0,0 +1,73 @@ +// (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 { + AddonModAssignPlugin, + AddonModAssignAssign, + AddonModAssignSubmission, + AddonModAssign, +} from '@addons/mod/assign/services/assign'; +import { AddonModAssignFeedbackHandler } from '@addons/mod/assign/services/feedback-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreWSExternalFile } from '@services/ws'; +import { makeSingleton } from '@singletons'; +import { AddonModAssignFeedbackEditPdfComponent } from '../component/editpdf'; + +/** + * Handler for edit pdf feedback plugin. + */ +@Injectable( { providedIn: 'root' }) +export class AddonModAssignFeedbackEditPdfHandlerService implements AddonModAssignFeedbackHandler { + + name = 'AddonModAssignFeedbackEditPdfHandler'; + type = 'editpdf'; + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @return The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(): Type { + return AddonModAssignFeedbackEditPdfComponent; + } + + /** + * Get files used by this plugin. + * The files returned by this function will be prefetched when the user prefetches the assign. + * + * @param assign The assignment. + * @param submission The submission. + * @param plugin The plugin object. + * @return The files (or promise resolved with the files). + */ + getPluginFiles( + assign: AddonModAssignAssign, + submission: AddonModAssignSubmission, + plugin: AddonModAssignPlugin, + ): CoreWSExternalFile[] { + return AddonModAssign.instance.getSubmissionPluginAttachments(plugin); + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return True or promise resolved with true if enabled. + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModAssignFeedbackEditPdfHandler = makeSingleton(AddonModAssignFeedbackEditPdfHandlerService); diff --git a/src/addons/mod/assign/feedback/feedback.module.ts b/src/addons/mod/assign/feedback/feedback.module.ts new file mode 100644 index 000000000..dd76de25c --- /dev/null +++ b/src/addons/mod/assign/feedback/feedback.module.ts @@ -0,0 +1,27 @@ +// (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 { AddonModAssignFeedbackCommentsModule } from './comments/comments.module'; +import { AddonModAssignFeedbackEditPdfModule } from './editpdf/editpdf.module'; +import { AddonModAssignFeedbackFileModule } from './file/file.module'; + +@NgModule({ + imports: [ + AddonModAssignFeedbackCommentsModule, + AddonModAssignFeedbackEditPdfModule, + AddonModAssignFeedbackFileModule, + ], +}) +export class AddonModAssignFeedbackModule { } diff --git a/src/addons/mod/assign/feedback/file/component/addon-mod-assign-feedback-file.html b/src/addons/mod/assign/feedback/file/component/addon-mod-assign-feedback-file.html new file mode 100644 index 000000000..d6f66aad3 --- /dev/null +++ b/src/addons/mod/assign/feedback/file/component/addon-mod-assign-feedback-file.html @@ -0,0 +1,11 @@ + + + +

{{plugin.name}}

+ + + + +
+
diff --git a/src/addons/mod/assign/feedback/file/component/file.ts b/src/addons/mod/assign/feedback/file/component/file.ts new file mode 100644 index 000000000..45c0bb9b3 --- /dev/null +++ b/src/addons/mod/assign/feedback/file/component/file.ts @@ -0,0 +1,41 @@ +// (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 { AddonModAssignFeedbackPluginComponent } from '@addons/mod/assign/components/feedback-plugin/feedback-plugin'; +import { AddonModAssign, AddonModAssignProvider } from '@addons/mod/assign/services/assign'; +import { Component, OnInit } from '@angular/core'; +import { CoreWSExternalFile } from '@services/ws'; + +/** + * Component to render a file feedback plugin. + */ +@Component({ + selector: 'addon-mod-assign-feedback-file', + templateUrl: 'addon-mod-assign-feedback-file.html', +}) +export class AddonModAssignFeedbackFileComponent extends AddonModAssignFeedbackPluginComponent implements OnInit { + + component = AddonModAssignProvider.COMPONENT; + files: CoreWSExternalFile[] = []; + + /** + * Component being initialized. + */ + async ngOnInit(): Promise { + if (this.plugin) { + this.files = AddonModAssign.instance.getSubmissionPluginAttachments(this.plugin); + } + } + +} diff --git a/src/addons/mod/assign/feedback/file/file.module.ts b/src/addons/mod/assign/feedback/file/file.module.ts new file mode 100644 index 000000000..c4e59e063 --- /dev/null +++ b/src/addons/mod/assign/feedback/file/file.module.ts @@ -0,0 +1,46 @@ +// (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 { APP_INITIALIZER, NgModule } from '@angular/core'; +import { AddonModAssignFeedbackFileHandler } from './services/handler'; +import { AddonModAssignFeedbackFileComponent } from './component/file'; +import { CoreSharedModule } from '@/core/shared.module'; +import { AddonModAssignFeedbackDelegate } from '../../services/feedback-delegate'; + +@NgModule({ + declarations: [ + AddonModAssignFeedbackFileComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + AddonModAssignFeedbackFileHandler, + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModAssignFeedbackDelegate.instance.registerHandler(AddonModAssignFeedbackFileHandler.instance); + }, + }, + ], + exports: [ + AddonModAssignFeedbackFileComponent, + ], + entryComponents: [ + AddonModAssignFeedbackFileComponent, + ], +}) +export class AddonModAssignFeedbackFileModule {} diff --git a/src/addons/mod/assign/feedback/file/lang.json b/src/addons/mod/assign/feedback/file/lang.json new file mode 100644 index 000000000..e5e6aeb98 --- /dev/null +++ b/src/addons/mod/assign/feedback/file/lang.json @@ -0,0 +1,3 @@ +{ + "pluginname": "File feedback" +} \ No newline at end of file diff --git a/src/addons/mod/assign/feedback/file/services/handler.ts b/src/addons/mod/assign/feedback/file/services/handler.ts new file mode 100644 index 000000000..9e12e4fd2 --- /dev/null +++ b/src/addons/mod/assign/feedback/file/services/handler.ts @@ -0,0 +1,73 @@ +// (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 { + AddonModAssignPlugin, + AddonModAssignAssign, + AddonModAssignSubmission, + AddonModAssign, +} from '@addons/mod/assign/services/assign'; +import { AddonModAssignFeedbackHandler } from '@addons/mod/assign/services/feedback-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreWSExternalFile } from '@services/ws'; +import { makeSingleton } from '@singletons'; +import { AddonModAssignFeedbackFileComponent } from '../component/file'; + +/** + * Handler for file feedback plugin. + */ +@Injectable( { providedIn: 'root' }) +export class AddonModAssignFeedbackFileHandlerService implements AddonModAssignFeedbackHandler { + + name = 'AddonModAssignFeedbackFileHandler'; + type = 'file'; + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @return The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(): Type { + return AddonModAssignFeedbackFileComponent; + } + + /** + * Get files used by this plugin. + * The files returned by this function will be prefetched when the user prefetches the assign. + * + * @param assign The assignment. + * @param submission The submission. + * @param plugin The plugin object. + * @return The files (or promise resolved with the files). + */ + getPluginFiles( + assign: AddonModAssignAssign, + submission: AddonModAssignSubmission, + plugin: AddonModAssignPlugin, + ): CoreWSExternalFile[] { + return AddonModAssign.instance.getSubmissionPluginAttachments(plugin); + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return True or promise resolved with true if enabled. + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModAssignFeedbackFileHandler = makeSingleton(AddonModAssignFeedbackFileHandlerService); diff --git a/src/addons/mod/assign/services/feedback-delegate.ts b/src/addons/mod/assign/services/feedback-delegate.ts index 8f78851ff..03e462fb0 100644 --- a/src/addons/mod/assign/services/feedback-delegate.ts +++ b/src/addons/mod/assign/services/feedback-delegate.ts @@ -58,7 +58,11 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler { * @param siteId Site ID. If not defined, current site. * @return Data (or promise resolved with the data). */ - getDraft?(assignId: number, userId: number, siteId?: string): any | Promise; + getDraft?( + assignId: number, + userId: number, + siteId?: string, + ): Record | Promise | undefined> | undefined; /** * Get files used by this plugin. @@ -99,7 +103,7 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler { assign: AddonModAssignAssign, submission: AddonModAssignSubmission, plugin: AddonModAssignPlugin, - inputData: any, + inputData: Record, userId: number, ): boolean | Promise; @@ -158,7 +162,13 @@ export interface AddonModAssignFeedbackHandler extends CoreDelegateHandler { * @param siteId Site ID. If not defined, current site. * @return If the function is async, it should return a Promise resolved when done. */ - saveDraft?(assignId: number, userId: number, plugin: AddonModAssignPlugin, data: any, siteId?: string): void | Promise; + saveDraft?( + assignId: number, + userId: number, + plugin: AddonModAssignPlugin, + data: Record, + siteId?: string, + ): void | Promise; } /** @@ -189,7 +199,7 @@ export class AddonModAssignFeedbackDelegateService extends CoreDelegate { + ): Promise { return await this.executeFunctionOnEnabled(plugin.type, 'discardDraft', [assignId, userId, siteId]); } @@ -212,12 +222,12 @@ export class AddonModAssignFeedbackDelegateService extends CoreDelegate( assignId: number, userId: number, plugin: AddonModAssignPlugin, siteId?: string, - ): Promise { + ): Promise { return await this.executeFunctionOnEnabled(plugin.type, 'getDraft', [assignId, userId, siteId]); } @@ -267,7 +277,7 @@ export class AddonModAssignFeedbackDelegateService extends CoreDelegate, userId: number, ): Promise { return await this.executeFunctionOnEnabled( @@ -362,9 +372,9 @@ export class AddonModAssignFeedbackDelegateService extends CoreDelegate, siteId?: string, - ): Promise { + ): Promise { return await this.executeFunctionOnEnabled( plugin.type, 'saveDraft', diff --git a/src/addons/mod/assign/services/handlers/default-feedback.ts b/src/addons/mod/assign/services/handlers/default-feedback.ts index 02451fd2c..de0cdd3f3 100644 --- a/src/addons/mod/assign/services/handlers/default-feedback.ts +++ b/src/addons/mod/assign/services/handlers/default-feedback.ts @@ -41,8 +41,9 @@ export class AddonModAssignDefaultFeedbackHandler implements AddonModAssignFeedb * * @return Data (or promise resolved with the data). */ - getDraft(): void { + getDraft(): undefined { // Nothing to do. + return; } /**