Merge pull request #2737 from dpalou/MOBILE-3698

Mobile 3698
main
Pau Ferrer Ocaña 2021-05-03 11:45:42 +02:00 committed by GitHub
commit 21ccad82f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 169 additions and 71 deletions

View File

@ -0,0 +1,80 @@
// (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, Input } from '@angular/core';
import { CoreCanceledError } from '@classes/errors/cancelederror';
import { CoreError } from '@classes/errors/error';
import { ModalController } from '@singletons';
import { AddonModAssignEditFeedbackModalComponent } from '../components/edit-feedback-modal/edit-feedback-modal';
import { AddonModAssignFeedbackCommentsTextData } from '../feedback/comments/services/handler';
import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } from '../services/assign';
/**
* Base class for component to render a feedback plugin.
*/
@Component({
template: '',
})
export class AddonModAssignFeedbackPluginBaseComponent {
@Input() assign!: AddonModAssignAssign; // The assignment.
@Input() submission!: AddonModAssignSubmission; // The submission.
@Input() plugin!: AddonModAssignPlugin; // The plugin object.
@Input() userId!: number; // The user ID of the submission.
@Input() configs?: Record<string,string>; // The configs for the plugin.
@Input() canEdit = false; // Whether the user can edit.
@Input() edit = false; // Whether the user is editing.
/**
* Open a modal to edit the feedback plugin.
*
* @return Promise resolved with the input data, rejected if cancelled.
*/
async editFeedback(): Promise<AddonModAssignFeedbackCommentsTextData> {
if (!this.canEdit) {
throw new CoreError('Cannot edit feedback');
}
// Create the navigation modal.
const modal = await ModalController.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') {
throw new CoreCanceledError(); // User cancelled.
} else {
return result.data;
}
}
/**
* Invalidate the data.
*
* @return Promise resolved when done.
*/
async invalidate(): Promise<void> {
return;
}
}

View File

@ -0,0 +1,42 @@
// (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, Input } from '@angular/core';
import { AddonModAssignAssign, AddonModAssignPlugin, AddonModAssignSubmission } from '../services/assign';
/**
* Base class for component to render a submission plugin.
*/
@Component({
template: '',
})
export class AddonModAssignSubmissionPluginBaseComponent {
@Input() assign!: AddonModAssignAssign; // The assignment.
@Input() submission!: AddonModAssignSubmission; // The submission.
@Input() plugin!: AddonModAssignPlugin; // The plugin object.
@Input() configs?: Record<string, string>; // The configs for the plugin.
@Input() edit = false; // Whether the user is editing.
@Input() allowOffline = false; // Whether to allow offline.
/**
* Invalidate the data.
*
* @return Promise resolved when done.
*/
async invalidate(): Promise<void> {
return;
}
}

View File

@ -13,11 +13,8 @@
// 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 { CoreWSFile } from '@services/ws';
import { ModalController } from '@singletons';
import { AddonModAssignFeedbackCommentsTextData } from '../../feedback/comments/services/handler';
import {
AddonModAssignAssign,
AddonModAssignSubmission,
@ -27,7 +24,6 @@ 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.
@ -99,38 +95,6 @@ export class AddonModAssignFeedbackPluginComponent implements OnInit {
}
}
/**
* Open a modal to edit the feedback plugin.
*
* @return Promise resolved with the input data, rejected if cancelled.
*/
async editFeedback(): Promise<AddonModAssignFeedbackCommentsTextData> {
if (!this.canEdit) {
throw new CoreError('Cannot edit feedback');
}
// Create the navigation modal.
const modal = await ModalController.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') {
throw null; // User cancelled.
} else {
return result.data;
}
}
/**
* Invalidate the plugin data.
*

View File

@ -14,7 +14,6 @@
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 {
@ -25,6 +24,7 @@ import {
import { AddonModAssignFeedbackDelegate } from '@addons/mod/assign/services/feedback-delegate';
import { AddonModAssignOffline } from '@addons/mod/assign/services/assign-offline';
import { CoreUtils } from '@services/utils/utils';
import { AddonModAssignFeedbackPluginBaseComponent } from '@addons/mod/assign/classes/base-feedback-plugin-component';
/**
* Component to render a comments feedback plugin.
*/
@ -32,7 +32,7 @@ import { CoreUtils } from '@services/utils/utils';
selector: 'addon-mod-assign-feedback-comments',
templateUrl: 'addon-mod-assign-feedback-comments.html',
})
export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedbackPluginComponent implements OnInit {
export class AddonModAssignFeedbackCommentsComponent extends AddonModAssignFeedbackPluginBaseComponent implements OnInit {
control?: FormControl;
component = AddonModAssignProvider.COMPONENT;

View File

@ -12,7 +12,7 @@
// 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 { AddonModAssignFeedbackPluginBaseComponent } from '@addons/mod/assign/classes/base-feedback-plugin-component';
import { AddonModAssignProvider, AddonModAssign } from '@addons/mod/assign/services/assign';
import { Component, OnInit } from '@angular/core';
import { CoreWSFile } from '@services/ws';
@ -24,7 +24,7 @@ import { CoreWSFile } from '@services/ws';
selector: 'addon-mod-assign-feedback-edit-pdf',
templateUrl: 'addon-mod-assign-feedback-editpdf.html',
})
export class AddonModAssignFeedbackEditPdfComponent extends AddonModAssignFeedbackPluginComponent implements OnInit {
export class AddonModAssignFeedbackEditPdfComponent extends AddonModAssignFeedbackPluginBaseComponent implements OnInit {
component = AddonModAssignProvider.COMPONENT;
files: CoreWSFile[] = [];

View File

@ -12,7 +12,7 @@
// 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 { AddonModAssignFeedbackPluginBaseComponent } from '@addons/mod/assign/classes/base-feedback-plugin-component';
import { AddonModAssign, AddonModAssignProvider } from '@addons/mod/assign/services/assign';
import { Component, OnInit } from '@angular/core';
import { CoreWSFile } from '@services/ws';
@ -24,7 +24,7 @@ import { CoreWSFile } from '@services/ws';
selector: 'addon-mod-assign-feedback-file',
templateUrl: 'addon-mod-assign-feedback-file.html',
})
export class AddonModAssignFeedbackFileComponent extends AddonModAssignFeedbackPluginComponent implements OnInit {
export class AddonModAssignFeedbackFileComponent extends AddonModAssignFeedbackPluginBaseComponent implements OnInit {
component = AddonModAssignProvider.COMPONENT;
files: CoreWSFile[] = [];

View File

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { AddonModAssignSubmissionPluginBaseComponent } from '@addons/mod/assign/classes/base-submission-plugin-component';
import { Component, ViewChild } from '@angular/core';
import { AddonModAssignSubmissionPluginComponent } from '@addons/mod/assign/components/submission-plugin/submission-plugin';
import { CoreCommentsCommentsComponent } from '@features/comments/components/comments/comments';
import { CoreComments } from '@features/comments/services/comments';
@ -24,7 +24,7 @@ import { CoreComments } from '@features/comments/services/comments';
selector: 'addon-mod-assign-submission-comments',
templateUrl: 'addon-mod-assign-submission-comments.html',
})
export class AddonModAssignSubmissionCommentsComponent extends AddonModAssignSubmissionPluginComponent {
export class AddonModAssignSubmissionCommentsComponent extends AddonModAssignSubmissionPluginBaseComponent {
@ViewChild(CoreCommentsCommentsComponent) commentsComponent!: CoreCommentsCommentsComponent;

View File

@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { AddonModAssignSubmissionPluginComponent } from '@addons/mod/assign/components/submission-plugin/submission-plugin';
import { AddonModAssign, AddonModAssignProvider } from '@addons/mod/assign/services/assign';
import { AddonModAssignHelper } from '@addons/mod/assign/services/assign-helper';
import { AddonModAssignOffline } from '@addons/mod/assign/services/assign-offline';
@ -22,6 +21,8 @@ import { CoreFileSession } from '@services/file-session';
import { CoreUtils } from '@services/utils/utils';
import { AddonModAssignSubmissionFileHandlerService } from '../services/handler';
import { FileEntry } from '@ionic-native/file/ngx';
import { AddonModAssignSubmissionPluginBaseComponent } from '@addons/mod/assign/classes/base-submission-plugin-component';
import { CoreFileEntry } from '@services/file-helper';
/**
* Component to render a file submission plugin.
@ -30,9 +31,10 @@ import { FileEntry } from '@ionic-native/file/ngx';
selector: 'addon-mod-assign-submission-file',
templateUrl: 'addon-mod-assign-submission-file.html',
})
export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmissionPluginComponent implements OnInit {
export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmissionPluginBaseComponent implements OnInit {
component = AddonModAssignProvider.COMPONENT;
files: CoreFileEntry[] = [];
maxSize?: number;
acceptedTypes?: string;
@ -48,12 +50,12 @@ export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmiss
undefined,
);
this.acceptedTypes = this.data?.configs.filetypeslist;
this.maxSize = this.data?.configs.maxsubmissionsizebytes
? parseInt(this.data?.configs.maxsubmissionsizebytes, 10)
this.acceptedTypes = this.configs?.filetypeslist;
this.maxSize = this.configs?.maxsubmissionsizebytes
? parseInt(this.configs.maxsubmissionsizebytes, 10)
: undefined;
this.maxSubmissions = this.data?.configs.maxfilesubmissions
? parseInt(this.data?.configs.maxfilesubmissions, 10)
this.maxSubmissions = this.configs?.maxfilesubmissions
? parseInt(this.configs.maxfilesubmissions, 10)
: undefined;
try {

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { AddonModAssignSubmissionPluginComponent } from '@addons/mod/assign/components/submission-plugin/submission-plugin';
import { AddonModAssignSubmissionPluginBaseComponent } from '@addons/mod/assign/classes/base-submission-plugin-component';
import { AddonModAssignProvider, AddonModAssign } from '@addons/mod/assign/services/assign';
import { AddonModAssignOffline } from '@addons/mod/assign/services/assign-offline';
import { Component, OnInit, ElementRef } from '@angular/core';
@ -29,7 +29,7 @@ import { AddonModAssignSubmissionOnlineTextPluginData } from '../services/handle
selector: 'addon-mod-assign-submission-online-text',
templateUrl: 'addon-mod-assign-submission-onlinetext.html',
})
export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignSubmissionPluginComponent implements OnInit {
export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignSubmissionPluginBaseComponent implements OnInit {
control?: FormControl;
words = 0;
@ -62,8 +62,8 @@ export class AddonModAssignSubmissionOnlineTextComponent extends AddonModAssignS
undefined,
);
this.wordLimitEnabled = !!parseInt(this.data?.configs.wordlimitenabled || '0', 10);
this.wordLimit = parseInt(this.data?.configs.wordlimit || '0');
this.wordLimitEnabled = !!parseInt(this.configs?.wordlimitenabled || '0', 10);
this.wordLimit = parseInt(this.configs?.wordlimit || '0');
try {
if (offlineData && offlineData.plugindata && offlineData.plugindata.onlinetext_editor) {

View File

@ -31,6 +31,7 @@ export class CoreSitePluginsAssignFeedbackComponent extends CoreSitePluginsCompi
@Input() submission!: AddonModAssignSubmission; // The submission.
@Input() plugin!: AddonModAssignPlugin; // The plugin object.
@Input() userId!: number; // The user ID of the submission.
@Input() configs?: Record<string,string>; // The configs for the plugin.
@Input() canEdit = false; // Whether the user can edit.
@Input() edit = false; // Whether the user is editing.
@ -43,6 +44,7 @@ export class CoreSitePluginsAssignFeedbackComponent extends CoreSitePluginsCompi
this.jsData.submission = this.submission;
this.jsData.plugin = this.plugin;
this.jsData.userId = this.userId;
this.jsData.configs = this.configs;
this.jsData.edit = this.edit;
this.jsData.canEdit = this.canEdit;

View File

@ -30,6 +30,7 @@ export class CoreSitePluginsAssignSubmissionComponent extends CoreSitePluginsCom
@Input() assign!: AddonModAssignAssign; // The assignment.
@Input() submission!: AddonModAssignSubmission; // The submission.
@Input() plugin!: AddonModAssignPlugin; // The plugin object.
@Input() configs?: Record<string, string>; // The configs for the plugin.
@Input() edit = false; // Whether the user is editing.
@Input() allowOffline = false; // Whether to allow offline.
@ -41,6 +42,7 @@ export class CoreSitePluginsAssignSubmissionComponent extends CoreSitePluginsCom
this.jsData.assign = this.assign;
this.jsData.submission = this.submission;
this.jsData.plugin = this.plugin;
this.jsData.configs = this.configs;
this.jsData.edit = this.edit;
this.jsData.allowOffline = this.allowOffline;

View File

@ -304,6 +304,7 @@ export class CoreTextUtilsProvider {
/**
* Count words in a text.
* This function is based on Moodle's count_words.
*
* @param text Text to count.
* @return Number of words.
@ -312,27 +313,32 @@ export class CoreTextUtilsProvider {
if (!text || typeof text != 'string') {
return 0;
}
const blockTags = ['address', 'article', 'aside', 'blockquote', 'br', ' details', 'dialog', 'dd', 'div', 'dl', 'dt',
'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'hr',
'li', 'main', 'nav', 'ol', 'p', 'pre', 'section', 'table', 'ul'];
// Clean HTML scripts and tags.
text = text.replace(/<script[^>]*>([\S\s]*?)<\/script>/gmi, '');
// Replace block tags by space to get word count aware of line break and remove inline tags.
text = text.replace(/<(\/[ ]*)?([a-zA-Z0-9]+)[^>]*>/gi, (str, p1, match) => {
if (blockTags.indexOf(match) >= 0) {
return ' ';
}
// Before stripping tags, add a space after the close tag of anything that is not obviously inline.
// Also, br is a special case because it definitely delimits a word, but has no close tag.
text = text.replace(/(<\/(?!a>|b>|del>|em>|i>|ins>|s>|small>|strong>|sub>|sup>|u>)\w+>|<br>|<br\s*\/>)/ig, '$1 ');
return '';
});
// Now remove HTML tags.
text = text.replace(/(<([^>]+)>)/ig, '');
// Decode HTML entities.
text = this.decodeHTMLEntities(text);
// Replace underscores (which are classed as word characters) with spaces.
text = text.replace(/_/gi, ' ');
// This RegEx will detect any word change including Unicode chars. Some languages without spaces won't be counted fine.
return text.match(/\S+/gi)?.length || 0;
// Now, the word count is the number of blocks of characters separated
// by any sort of space. That seems to be the definition used by all other systems.
// To be precise about what is considered to separate words:
// * Anything that Unicode considers a 'Separator'
// * Anything that Unicode considers a 'Control character'
// * An em- or en- dash.
let words: string[];
try {
words = text.split(/[\p{Z}\p{Cc}]+/u);
} catch {
// Unicode-aware flag not supported.
words = text.split(/\s+/);
}
// Filter empty words.
return words.filter(word => word).length;
}
/**