MOBILE-3636 assign: Feedback plugins
parent
85f79bb944
commit
c4d37b0074
|
@ -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<AddonModAssignFeedbackCommentsPluginData> {
|
||||
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<AddonModAssignFeedbackCommentsPluginData>) => 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.
|
||||
*
|
||||
|
|
|
@ -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 {}
|
|
@ -0,0 +1,34 @@
|
|||
<!-- Read only. -->
|
||||
<ion-item class="ion-text-wrap" *ngIf="(text || canEdit) && !edit">
|
||||
<ion-label>
|
||||
<h2>{{ plugin.name }}</h2>
|
||||
<p>
|
||||
<core-format-text [component]="component" [componentId]="assign.cmid" [maxHeight]="80" [fullOnClick]="true"
|
||||
[fullTitle]="plugin.name" [text]="text" contextLevel="module" [contextInstanceId]="assign.cmid"
|
||||
[courseId]="assign.course">
|
||||
</core-format-text>
|
||||
</p>
|
||||
</ion-label>
|
||||
<div slot="end">
|
||||
<div class="ion-text-end">
|
||||
<ion-button fill="clear" *ngIf="canEdit" (click)="editComment()" color="dark">
|
||||
<ion-icon name="fas-pen" slot="icon-only"></ion-icon>
|
||||
</ion-button>
|
||||
</div>
|
||||
<ion-note *ngIf="!isSent" color="dark">
|
||||
<ion-icon name="far-clock"></ion-icon>
|
||||
{{ 'core.notsent' | translate }}
|
||||
</ion-note>
|
||||
</div>
|
||||
</ion-item>
|
||||
|
||||
<!-- Edit -->
|
||||
<ion-item class="ion-text-wrap" *ngIf="edit && loaded">
|
||||
<ion-label>
|
||||
<core-rich-text-editor item-content [control]="control" [placeholder]="plugin.name"
|
||||
name="assignfeedbackcomments_editor" [component]="component" [componentId]="assign.cmid" [autoSave]="true"
|
||||
contextLevel="module" [contextInstanceId]="assign.cmid" elementId="assignfeedbackcomments_editor"
|
||||
[draftExtraParams]="{userid: userId, action: 'grade'}">
|
||||
</core-rich-text-editor>
|
||||
</ion-label>
|
||||
</ion-item>
|
|
@ -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<void> {
|
||||
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<void> {
|
||||
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<string> {
|
||||
// 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 = <AddonModAssignFeedbackCommentsPluginData>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 || []);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Feedback comments"
|
||||
}
|
|
@ -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<unknown> {
|
||||
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<boolean> {
|
||||
// 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 = <AddonModAssignFeedbackCommentsPluginData>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<boolean> {
|
||||
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<boolean> {
|
||||
// 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;
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Read only. -->
|
||||
<ion-item class="ion-text-wrap" *ngIf="files && files.length">
|
||||
<ion-label>
|
||||
<h2>{{plugin.name}}</h2>
|
||||
<ng-container>
|
||||
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="assign.cmid"
|
||||
[alwaysDownload]="true">
|
||||
</core-file>
|
||||
</ng-container>
|
||||
</ion-label>
|
||||
</ion-item>
|
|
@ -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<void> {
|
||||
if (this.plugin) {
|
||||
this.files = AddonModAssign.instance.getSubmissionPluginAttachments(this.plugin);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "Annotate PDF"
|
||||
}
|
|
@ -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<unknown> {
|
||||
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<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
export const AddonModAssignFeedbackEditPdfHandler = makeSingleton(AddonModAssignFeedbackEditPdfHandlerService);
|
|
@ -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 { }
|
|
@ -0,0 +1,11 @@
|
|||
<!-- Read only. -->
|
||||
<ion-item class="ion-text-wrap" *ngIf="files && files.length">
|
||||
<ion-label>
|
||||
<h2>{{plugin.name}}</h2>
|
||||
<ng-container>
|
||||
<core-file *ngFor="let file of files" [file]="file" [component]="component" [componentId]="assign.cmid"
|
||||
[alwaysDownload]="true">
|
||||
</core-file>
|
||||
</ng-container>
|
||||
</ion-label>
|
||||
</ion-item>
|
|
@ -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<void> {
|
||||
if (this.plugin) {
|
||||
this.files = AddonModAssign.instance.getSubmissionPluginAttachments(this.plugin);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"pluginname": "File feedback"
|
||||
}
|
|
@ -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<unknown> {
|
||||
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<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
export const AddonModAssignFeedbackFileHandler = makeSingleton(AddonModAssignFeedbackFileHandlerService);
|
|
@ -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<any>;
|
||||
getDraft?(
|
||||
assignId: number,
|
||||
userId: number,
|
||||
siteId?: string,
|
||||
): Record<string, unknown> | Promise<Record<string, unknown> | 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<string, unknown>,
|
||||
userId: number,
|
||||
): boolean | Promise<boolean>;
|
||||
|
||||
|
@ -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<any>;
|
||||
saveDraft?(
|
||||
assignId: number,
|
||||
userId: number,
|
||||
plugin: AddonModAssignPlugin,
|
||||
data: Record<string, unknown>,
|
||||
siteId?: string,
|
||||
): void | Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,7 +199,7 @@ export class AddonModAssignFeedbackDelegateService extends CoreDelegate<AddonMod
|
|||
userId: number,
|
||||
plugin: AddonModAssignPlugin,
|
||||
siteId?: string,
|
||||
): Promise<any | undefined> {
|
||||
): Promise<void> {
|
||||
return await this.executeFunctionOnEnabled(plugin.type, 'discardDraft', [assignId, userId, siteId]);
|
||||
}
|
||||
|
||||
|
@ -212,12 +222,12 @@ export class AddonModAssignFeedbackDelegateService extends CoreDelegate<AddonMod
|
|||
* @param siteId Site ID. If not defined, current site.
|
||||
* @return Promise resolved with the draft data.
|
||||
*/
|
||||
async getPluginDraftData(
|
||||
async getPluginDraftData<T>(
|
||||
assignId: number,
|
||||
userId: number,
|
||||
plugin: AddonModAssignPlugin,
|
||||
siteId?: string,
|
||||
): Promise<any | undefined> {
|
||||
): Promise<T | undefined> {
|
||||
return await this.executeFunctionOnEnabled(plugin.type, 'getDraft', [assignId, userId, siteId]);
|
||||
}
|
||||
|
||||
|
@ -267,7 +277,7 @@ export class AddonModAssignFeedbackDelegateService extends CoreDelegate<AddonMod
|
|||
assign: AddonModAssignAssign,
|
||||
submission: AddonModAssignSubmission | AddonModAssignSubmissionFormatted,
|
||||
plugin: AddonModAssignPlugin,
|
||||
inputData: any,
|
||||
inputData: Record<string, unknown>,
|
||||
userId: number,
|
||||
): Promise<boolean | undefined> {
|
||||
return await this.executeFunctionOnEnabled(
|
||||
|
@ -362,9 +372,9 @@ export class AddonModAssignFeedbackDelegateService extends CoreDelegate<AddonMod
|
|||
assignId: number,
|
||||
userId: number,
|
||||
plugin: AddonModAssignPlugin,
|
||||
inputData: any,
|
||||
inputData: Record<string, unknown>,
|
||||
siteId?: string,
|
||||
): Promise<any> {
|
||||
): Promise<void> {
|
||||
return await this.executeFunctionOnEnabled(
|
||||
plugin.type,
|
||||
'saveDraft',
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue