From 8febbe3ea77691b322f00418fbefadae7f3d14fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 31 Mar 2021 09:24:48 +0200 Subject: [PATCH] MOBILE-3640 database: Add fields components --- .../data/classes/field-plugin-component.ts | 116 +++++++++++ .../addon-mod-data-field-plugin.html | 5 + .../components/field-plugin/field-plugin.ts | 103 ++++++++++ .../data/fields/checkbox/checkbox.module.ts | 42 ++++ .../addon-mod-data-field-checkbox.html | 16 ++ .../fields/checkbox/component/checkbox.ts | 70 +++++++ .../data/fields/checkbox/services/handler.ts | 131 +++++++++++++ .../component/addon-mod-data-field-date.html | 16 ++ .../mod/data/fields/date/component/date.ts | 70 +++++++ .../mod/data/fields/date/date.module.ts | 42 ++++ .../mod/data/fields/date/services/handler.ts | 165 ++++++++++++++++ src/addons/mod/data/fields/field.module.ts | 45 +++++ .../component/addon-mod-data-field-file.html | 17 ++ .../mod/data/fields/file/component/file.ts | 81 ++++++++ .../mod/data/fields/file/file.module.ts | 42 ++++ .../mod/data/fields/file/services/handler.ts | 136 +++++++++++++ .../addon-mod-data-field-latlong.html | 27 +++ .../data/fields/latlong/component/latlong.ts | 164 ++++++++++++++++ .../mod/data/fields/latlong/latlong.module.ts | 42 ++++ .../data/fields/latlong/services/handler.ts | 138 +++++++++++++ .../component/addon-mod-data-field-menu.html | 11 ++ .../mod/data/fields/menu/component/menu.ts | 47 +++++ .../mod/data/fields/menu/menu.module.ts | 42 ++++ .../mod/data/fields/menu/services/handler.ts | 116 +++++++++++ .../addon-mod-data-field-multimenu.html | 17 ++ .../fields/multimenu/component/multimenu.ts | 70 +++++++ .../data/fields/multimenu/multimenu.module.ts | 42 ++++ .../data/fields/multimenu/services/handler.ts | 127 ++++++++++++ .../addon-mod-data-field-number.html | 7 + .../data/fields/number/component/number.ts | 44 +++++ .../mod/data/fields/number/number.module.ts | 42 ++++ .../data/fields/number/services/handler.ts | 63 ++++++ .../addon-mod-data-field-picture.html | 22 +++ .../data/fields/picture/component/picture.ts | 142 ++++++++++++++ .../mod/data/fields/picture/picture.module.ts | 42 ++++ .../data/fields/picture/services/handler.ts | 181 ++++++++++++++++++ .../addon-mod-data-field-radiobutton.html | 11 ++ .../radiobutton/component/radiobutton.ts | 46 +++++ .../fields/radiobutton/radiobutton.module.ts | 42 ++++ .../fields/radiobutton/services/handler.ts | 114 +++++++++++ .../component/addon-mod-data-field-text.html | 7 + .../mod/data/fields/text/component/text.ts | 43 +++++ .../mod/data/fields/text/services/handler.ts | 117 +++++++++++ .../mod/data/fields/text/text.module.ts | 42 ++++ .../addon-mod-data-field-textarea.html | 14 ++ .../fields/textarea/component/textarea.ts | 65 +++++++ .../data/fields/textarea/services/handler.ts | 127 ++++++++++++ .../data/fields/textarea/textarea.module.ts | 44 +++++ .../component/addon-mod-data-field-url.html | 10 + .../mod/data/fields/url/component/url.ts | 78 ++++++++ .../mod/data/fields/url/services/handler.ts | 63 ++++++ src/addons/mod/data/fields/url/url.module.ts | 42 ++++ 52 files changed, 3348 insertions(+) create mode 100644 src/addons/mod/data/classes/field-plugin-component.ts create mode 100644 src/addons/mod/data/components/field-plugin/addon-mod-data-field-plugin.html create mode 100644 src/addons/mod/data/components/field-plugin/field-plugin.ts create mode 100644 src/addons/mod/data/fields/checkbox/checkbox.module.ts create mode 100644 src/addons/mod/data/fields/checkbox/component/addon-mod-data-field-checkbox.html create mode 100644 src/addons/mod/data/fields/checkbox/component/checkbox.ts create mode 100644 src/addons/mod/data/fields/checkbox/services/handler.ts create mode 100644 src/addons/mod/data/fields/date/component/addon-mod-data-field-date.html create mode 100644 src/addons/mod/data/fields/date/component/date.ts create mode 100644 src/addons/mod/data/fields/date/date.module.ts create mode 100644 src/addons/mod/data/fields/date/services/handler.ts create mode 100644 src/addons/mod/data/fields/field.module.ts create mode 100644 src/addons/mod/data/fields/file/component/addon-mod-data-field-file.html create mode 100644 src/addons/mod/data/fields/file/component/file.ts create mode 100644 src/addons/mod/data/fields/file/file.module.ts create mode 100644 src/addons/mod/data/fields/file/services/handler.ts create mode 100644 src/addons/mod/data/fields/latlong/component/addon-mod-data-field-latlong.html create mode 100644 src/addons/mod/data/fields/latlong/component/latlong.ts create mode 100644 src/addons/mod/data/fields/latlong/latlong.module.ts create mode 100644 src/addons/mod/data/fields/latlong/services/handler.ts create mode 100644 src/addons/mod/data/fields/menu/component/addon-mod-data-field-menu.html create mode 100644 src/addons/mod/data/fields/menu/component/menu.ts create mode 100644 src/addons/mod/data/fields/menu/menu.module.ts create mode 100644 src/addons/mod/data/fields/menu/services/handler.ts create mode 100644 src/addons/mod/data/fields/multimenu/component/addon-mod-data-field-multimenu.html create mode 100644 src/addons/mod/data/fields/multimenu/component/multimenu.ts create mode 100644 src/addons/mod/data/fields/multimenu/multimenu.module.ts create mode 100644 src/addons/mod/data/fields/multimenu/services/handler.ts create mode 100644 src/addons/mod/data/fields/number/component/addon-mod-data-field-number.html create mode 100644 src/addons/mod/data/fields/number/component/number.ts create mode 100644 src/addons/mod/data/fields/number/number.module.ts create mode 100644 src/addons/mod/data/fields/number/services/handler.ts create mode 100644 src/addons/mod/data/fields/picture/component/addon-mod-data-field-picture.html create mode 100644 src/addons/mod/data/fields/picture/component/picture.ts create mode 100644 src/addons/mod/data/fields/picture/picture.module.ts create mode 100644 src/addons/mod/data/fields/picture/services/handler.ts create mode 100644 src/addons/mod/data/fields/radiobutton/component/addon-mod-data-field-radiobutton.html create mode 100644 src/addons/mod/data/fields/radiobutton/component/radiobutton.ts create mode 100644 src/addons/mod/data/fields/radiobutton/radiobutton.module.ts create mode 100644 src/addons/mod/data/fields/radiobutton/services/handler.ts create mode 100644 src/addons/mod/data/fields/text/component/addon-mod-data-field-text.html create mode 100644 src/addons/mod/data/fields/text/component/text.ts create mode 100644 src/addons/mod/data/fields/text/services/handler.ts create mode 100644 src/addons/mod/data/fields/text/text.module.ts create mode 100644 src/addons/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html create mode 100644 src/addons/mod/data/fields/textarea/component/textarea.ts create mode 100644 src/addons/mod/data/fields/textarea/services/handler.ts create mode 100644 src/addons/mod/data/fields/textarea/textarea.module.ts create mode 100644 src/addons/mod/data/fields/url/component/addon-mod-data-field-url.html create mode 100644 src/addons/mod/data/fields/url/component/url.ts create mode 100644 src/addons/mod/data/fields/url/services/handler.ts create mode 100644 src/addons/mod/data/fields/url/url.module.ts diff --git a/src/addons/mod/data/classes/field-plugin-component.ts b/src/addons/mod/data/classes/field-plugin-component.ts new file mode 100644 index 000000000..9c0a79948 --- /dev/null +++ b/src/addons/mod/data/classes/field-plugin-component.ts @@ -0,0 +1,116 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Input, Output, OnInit, OnChanges, SimpleChange, EventEmitter, Component } from '@angular/core'; +import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { CoreFormFields } from '@singletons/form'; +import { AddonModDataData, AddonModDataEntryField, AddonModDataField, AddonModDataTemplateMode } from '../services/data'; + +/** + * Base class for component to render a field. + */ +@Component({ + template: '', +}) +export abstract class AddonModDataFieldPluginComponent implements OnInit, OnChanges { + + @Input() mode!: AddonModDataTemplateMode; // The render mode. + @Input() field!: AddonModDataField; // The field to render. + @Input() value?: Partial; // The value of the field. + @Input() database?: AddonModDataData; // Database object. + @Input() error?: string; // Error when editing. + @Input() form?: FormGroup; // Form where to add the form control. Just required for edit and search modes. + @Input() searchFields?: CoreFormFields; // The search value of all fields. + @Output() gotoEntry: EventEmitter; // Action to perform. + + constructor(protected fb: FormBuilder) { + this.gotoEntry = new EventEmitter(); + } + + /** + * Add the form control for the search mode. + * + * @param fieldName Control field name. + * @param value Initial set value. + */ + protected addControl(fieldName: string, value?: unknown): void { + if (!this.form) { + return; + } + + if (this.searchMode) { + this.form.addControl(fieldName, this.fb.control(this.searchFields?.[fieldName] || undefined)); + } + + if (this.editMode) { + this.form.addControl(fieldName, this.fb.control(value, this.field.required ? Validators.required : null)); + } + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.init(); + } + + /** + * Initialize field. + */ + protected init(): void { + return; + } + + /** + * Component being changed. + */ + ngOnChanges(changes: { [name: string]: SimpleChange }): void { + if ((this.showMode || this.listMode) && changes.value) { + this.updateValue(changes.value.currentValue); + } + } + + /** + * Update value being shown. + */ + protected updateValue(value?: Partial): void { + this.value = value; + } + + /** Magic mode getters */ + get listMode(): boolean { + return this.mode == AddonModDataTemplateMode.LIST; + } + + get showMode(): boolean { + return this.mode == AddonModDataTemplateMode.SHOW; + } + + get displayMode(): boolean { + return this.listMode || this.showMode; + } + + get editMode(): boolean { + return this.mode == AddonModDataTemplateMode.EDIT; + } + + get searchMode(): boolean { + return this.mode == AddonModDataTemplateMode.SEARCH; + } + + get inputMode(): boolean { + return this.searchMode || this.editMode; + } + +} diff --git a/src/addons/mod/data/components/field-plugin/addon-mod-data-field-plugin.html b/src/addons/mod/data/components/field-plugin/addon-mod-data-field-plugin.html new file mode 100644 index 000000000..44e4d82a5 --- /dev/null +++ b/src/addons/mod/data/components/field-plugin/addon-mod-data-field-plugin.html @@ -0,0 +1,5 @@ + + + + + diff --git a/src/addons/mod/data/components/field-plugin/field-plugin.ts b/src/addons/mod/data/components/field-plugin/field-plugin.ts new file mode 100644 index 000000000..28d20115f --- /dev/null +++ b/src/addons/mod/data/components/field-plugin/field-plugin.ts @@ -0,0 +1,103 @@ +// (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, OnChanges, ViewChild, Input, Output, SimpleChange, Type, EventEmitter } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-component'; +import { CoreFormFields } from '@singletons/form'; +import { AddonModDataData, AddonModDataField, AddonModDataTemplateMode } from '../../services/data'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; + +/** + * Component that displays a database field plugin. + */ +@Component({ + selector: 'addon-mod-data-field-plugin', + templateUrl: 'addon-mod-data-field-plugin.html', +}) +export class AddonModDataFieldPluginComponent implements OnInit, OnChanges { + + @ViewChild(CoreDynamicComponent) dynamicComponent?: CoreDynamicComponent; + + @Input() mode!: AddonModDataTemplateMode; // The render mode. + @Input() field!: AddonModDataField; // The field to render. + @Input() value?: unknown; // The value of the field. + @Input() database?: AddonModDataData; // Database object. + @Input() error?: string; // Error when editing. + @Input() form?: FormGroup; // Form where to add the form control. Just required for edit and search modes. + @Input() searchFields?: CoreFormFields; // The search value of all fields. + @Output() gotoEntry = new EventEmitter(); // Action to perform. + + fieldComponent?: Type; // Component to render the plugin. + pluginData?: AddonDataFieldPluginComponentData; // Data to pass to the component. + fieldLoaded = false; + + /** + * Component being initialized. + */ + async ngOnInit(): Promise { + if (!this.field) { + this.fieldLoaded = true; + + return; + } + + try{ + // Check if the plugin has defined its own component to render itself. + this.fieldComponent = await AddonModDataFieldsDelegate.getComponentForField(this.field); + + if (this.fieldComponent) { + // Prepare the data to pass to the component. + this.pluginData = { + mode: this.mode, + field: this.field, + value: this.value, + database: this.database, + error: this.error, + gotoEntry: this.gotoEntry, + form: this.form, + searchFields: this.searchFields, + }; + } + } finally { + this.fieldLoaded = true; + } + } + + /** + * Component being changed. + */ + ngOnChanges(changes: { [name: string]: SimpleChange }): void { + if (this.fieldLoaded && this.pluginData) { + if (this.mode == AddonModDataTemplateMode.EDIT && changes.error) { + this.pluginData.error = changes.error.currentValue; + } + if ((this.mode == AddonModDataTemplateMode.SHOW || this.mode == AddonModDataTemplateMode.LIST) && changes.value) { + this.pluginData.value = changes.value.currentValue; + } + } + } + +} + +export type AddonDataFieldPluginComponentData = { + mode: AddonModDataTemplateMode; // The render mode. + field: AddonModDataField; // The field to render. + value?: unknown; // The value of the field. + database?: AddonModDataData; // Database object. + error?: string; // Error when editing. + form?: FormGroup; // Form where to add the form control. Just required for edit and search modes. + searchFields?: CoreFormFields; // The search value of all fields. + gotoEntry: EventEmitter; +}; diff --git a/src/addons/mod/data/fields/checkbox/checkbox.module.ts b/src/addons/mod/data/fields/checkbox/checkbox.module.ts new file mode 100644 index 000000000..beb2b7786 --- /dev/null +++ b/src/addons/mod/data/fields/checkbox/checkbox.module.ts @@ -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 { APP_INITIALIZER, NgModule } from '@angular/core'; +import { AddonModDataFieldCheckboxComponent } from './component/checkbox'; +import { CoreSharedModule } from '@/core/shared.module'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldCheckboxHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldCheckboxComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldCheckboxHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldCheckboxComponent, + ], +}) +export class AddonModDataFieldCheckboxModule {} diff --git a/src/addons/mod/data/fields/checkbox/component/addon-mod-data-field-checkbox.html b/src/addons/mod/data/fields/checkbox/component/addon-mod-data-field-checkbox.html new file mode 100644 index 000000000..9bfe1c173 --- /dev/null +++ b/src/addons/mod/data/fields/checkbox/component/addon-mod-data-field-checkbox.html @@ -0,0 +1,16 @@ + + + + {{option.key}} + + + + + {{ 'addon.mod_data.selectedrequired' | translate }} + + + + + + diff --git a/src/addons/mod/data/fields/checkbox/component/checkbox.ts b/src/addons/mod/data/fields/checkbox/component/checkbox.ts new file mode 100644 index 000000000..032574c9b --- /dev/null +++ b/src/addons/mod/data/fields/checkbox/component/checkbox.ts @@ -0,0 +1,70 @@ +// (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 { AddonModDataEntryField } from '@addons/mod/data/services/data'; +import { Component } from '@angular/core'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data checkbox field. + */ +@Component({ + selector: 'addon-mod-data-field-checkbox', + templateUrl: 'addon-mod-data-field-checkbox.html', +}) +export class AddonModDataFieldCheckboxComponent extends AddonModDataFieldPluginComponent { + + options: { + key: string; + value: string; + }[] = []; + + /** + * @inheritdoc + */ + protected init(): void { + if (this.displayMode) { + this.updateValue(this.value); + + return; + } + + this.options = this.field.param1.split(/\r?\n/).map((option) => ({ key: option, value: option })); + + const values: string[] = []; + if (this.editMode && this.value && this.value.content) { + this.value.content.split('##').forEach((value) => { + const x = this.options.findIndex((option) => value == option.key); + if (x >= 0) { + values.push(value); + } + }); + } + + if (this.searchMode) { + this.addControl('f_' + this.field.id + '_allreq'); + } + + this.addControl('f_' + this.field.id, values); + } + + /** + * @inheritdoc + */ + protected updateValue(value?: Partial): void { + this.value = value || {}; + this.value.content = value?.content?.split('##').join('
'); + } + +} diff --git a/src/addons/mod/data/fields/checkbox/services/handler.ts b/src/addons/mod/data/fields/checkbox/services/handler.ts new file mode 100644 index 000000000..a2f0b4e20 --- /dev/null +++ b/src/addons/mod/data/fields/checkbox/services/handler.ts @@ -0,0 +1,131 @@ +// (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 { + AddonModDataEntryField, + AddonModDataField, + AddonModDataSearchEntriesAdvancedFieldFormatted, + AddonModDataSubfieldData, +} from '@addons/mod/data/services/data'; +import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreFormFields } from '@singletons/form'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldCheckboxComponent } from '../component/checkbox'; + +/** + * Handler for checkbox data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldCheckboxHandlerService implements AddonModDataFieldHandler { + + name = 'AddonModDataFieldCheckboxHandler'; + type = 'checkbox'; + + /** + * @inheritdoc + */ + getComponent(): Type { + return AddonModDataFieldCheckboxComponent; + } + + /** + * @inheritdoc + */ + getFieldSearchData( + field: AddonModDataField, + inputData: CoreFormFields, + ): AddonModDataSearchEntriesAdvancedFieldFormatted[] { + + const fieldName = 'f_' + field.id; + const reqName = 'f_' + field.id + '_allreq'; + + if (inputData[fieldName]) { + const values: AddonModDataSearchEntriesAdvancedFieldFormatted[] = []; + + values.push({ + name: fieldName, + value: inputData[fieldName], + }); + + if (inputData[reqName]) { + values.push({ + name: reqName, + value: true, + }); + } + + return values; + } + + return []; + } + + /** + * @inheritdoc + */ + getFieldEditData(field: AddonModDataField, inputData: CoreFormFields): AddonModDataSubfieldData[] { + const fieldName = 'f_' + field.id; + + return [{ + fieldid: field.id, + value: inputData[fieldName] || [], + }]; + } + + /** + * @inheritdoc + */ + hasFieldDataChanged( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): boolean { + const fieldName = 'f_' + field.id; + + const content = originalFieldData?.content || ''; + + return inputData[fieldName].join('##') != content; + } + + /** + * Check and get field requeriments. + * + * @param field Defines the field to be rendered. + * @param inputData Data entered in the edit form. + * @return String with the notification or false. + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + /** + * @inheritdoc + */ + overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields): AddonModDataEntryField { + originalContent.content = (offlineContent[''] && offlineContent[''].join('##')) || ''; + + return originalContent; + } + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModDataFieldCheckboxHandler = makeSingleton(AddonModDataFieldCheckboxHandlerService); diff --git a/src/addons/mod/data/fields/date/component/addon-mod-data-field-date.html b/src/addons/mod/data/fields/date/component/addon-mod-data-field-date.html new file mode 100644 index 000000000..4d1c12b81 --- /dev/null +++ b/src/addons/mod/data/fields/date/component/addon-mod-data-field-date.html @@ -0,0 +1,16 @@ + + + + + + + {{ 'addon.mod_data.usedate' | translate }} + + + + + + + {{ displayDate | coreFormatDate:'strftimedate' }} + diff --git a/src/addons/mod/data/fields/date/component/date.ts b/src/addons/mod/data/fields/date/component/date.ts new file mode 100644 index 000000000..fcf20c918 --- /dev/null +++ b/src/addons/mod/data/fields/date/component/date.ts @@ -0,0 +1,70 @@ +// (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 } from '@angular/core'; +import { CoreTimeUtils } from '@services/utils/time'; +import { Translate } from '@singletons'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data date field. + */ +@Component({ + selector: 'addon-mod-data-field-date', + templateUrl: 'addon-mod-data-field-date.html', +}) +export class AddonModDataFieldDateComponent extends AddonModDataFieldPluginComponent { + + format!: string; + displayDate?: number; + + /** + * @inheritdoc + */ + protected init(): void { + if (this.displayMode) { + return; + } + + let date: Date; + + // Calculate format to use. + this.format = CoreTimeUtils.fixFormatForDatetime(CoreTimeUtils.convertPHPToMoment( + Translate.instant('core.strftimedate'), + )); + + if (this.searchMode) { + this.addControl('f_' + this.field.id + '_z'); + + date = this.searchFields!['f_' + this.field.id + '_y'] + ? new Date(this.searchFields!['f_' + this.field.id + '_y'] + '-' + + this.searchFields!['f_' + this.field.id + '_m'] + '-' + this.searchFields!['f_' + this.field.id + '_d']) + : new Date(); + + this.searchFields!['f_' + this.field.id] = CoreTimeUtils.toDatetimeFormat(date.getTime()); + } else { + date = this.value?.content + ? new Date(parseInt(this.value.content, 10) * 1000) + : new Date(); + + this.displayDate = this.value?.content + ? parseInt(this.value.content, 10) * 1000 + : undefined; + + } + + this.addControl('f_' + this.field.id, CoreTimeUtils.toDatetimeFormat(date.getTime())); + } + +} diff --git a/src/addons/mod/data/fields/date/date.module.ts b/src/addons/mod/data/fields/date/date.module.ts new file mode 100644 index 000000000..05d33a1e0 --- /dev/null +++ b/src/addons/mod/data/fields/date/date.module.ts @@ -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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldDateComponent } from './component/date'; +import { AddonModDataFieldDateHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldDateComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldDateHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldDateComponent, + ], +}) +export class AddonModDataFieldDateModule {} diff --git a/src/addons/mod/data/fields/date/services/handler.ts b/src/addons/mod/data/fields/date/services/handler.ts new file mode 100644 index 000000000..0fdde13ee --- /dev/null +++ b/src/addons/mod/data/fields/date/services/handler.ts @@ -0,0 +1,165 @@ +// (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 { + AddonModDataEntryField, + AddonModDataField, + AddonModDataSearchEntriesAdvancedFieldFormatted, + AddonModDataSubfieldData, +} from '@addons/mod/data/services/data'; +import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreFormFields } from '@singletons/form'; +import { CoreTimeUtils } from '@services/utils/time'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldDateComponent } from '../component/date'; + +/** + * Handler for date data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldDateHandlerService implements AddonModDataFieldHandler { + + name = 'AddonModDataFieldDateHandler'; + type = 'date'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldDateComponent; + } + + /** + * @inheritdoc + */ + getFieldSearchData( + field: AddonModDataField, + inputData: CoreFormFields, + ): AddonModDataSearchEntriesAdvancedFieldFormatted[] { + const fieldName = 'f_' + field.id; + const enabledName = 'f_' + field.id + '_z'; + + if (inputData[enabledName] && typeof inputData[fieldName] == 'string') { + const date = inputData[fieldName].substr(0, 10).split('-'); + + return [ + { + name: fieldName + '_y', + value: date[0], + }, + { + name: fieldName + '_m', + value: date[1], + }, + { + name: fieldName + '_d', + value: date[2], + }, + { + name: enabledName, + value: 1, + }, + ]; + } + + return []; + } + + /** + * @inheritdoc + */ + getFieldEditData(field: AddonModDataField, inputData: CoreFormFields): AddonModDataSubfieldData[] { + const fieldName = 'f_' + field.id; + + if (typeof inputData[fieldName] != 'string') { + return []; + } + + const date = inputData[fieldName].substr(0, 10).split('-'); + + return [ + { + fieldid: field.id, + subfield: 'year', + value: date[0], + }, + { + fieldid: field.id, + subfield: 'month', + value: date[1], + }, + { + fieldid: field.id, + subfield: 'day', + value: date[2], + }, + ]; + } + + /** + * @inheritdoc + */ + hasFieldDataChanged( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): boolean { + const fieldName = 'f_' + field.id; + const input = inputData[fieldName] && inputData[fieldName].substr(0, 10) || ''; + + const content = (originalFieldData && originalFieldData?.content && + CoreTimeUtils.toDatetimeFormat(parseInt(originalFieldData.content, 10) * 1000).substr(0, 10)) || ''; + + return input != content; + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (field.required && + (!inputData || inputData.length < 2 || !inputData[0].value || !inputData[1].value || !inputData[2].value)) { + + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + /** + * @inheritdoc + */ + overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields): AddonModDataEntryField { + if (offlineContent['day']) { + let date = Date.UTC( + parseInt(offlineContent['year'], 10), + parseInt(offlineContent['month'], 10) - 1, + parseInt(offlineContent['day'], 10), + ); + date = Math.floor(date / 1000); + + originalContent.content = String(date) || ''; + } + + return originalContent; + } + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModDataFieldDateHandler = makeSingleton(AddonModDataFieldDateHandlerService); diff --git a/src/addons/mod/data/fields/field.module.ts b/src/addons/mod/data/fields/field.module.ts new file mode 100644 index 000000000..293e00baf --- /dev/null +++ b/src/addons/mod/data/fields/field.module.ts @@ -0,0 +1,45 @@ +// (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 { AddonModDataFieldCheckboxModule } from './checkbox/checkbox.module'; +import { AddonModDataFieldDateModule } from './date/date.module'; +import { AddonModDataFieldFileModule } from './file/file.module'; +import { AddonModDataFieldLatlongModule } from './latlong/latlong.module'; +import { AddonModDataFieldMenuModule } from './menu/menu.module'; +import { AddonModDataFieldMultimenuModule } from './multimenu/multimenu.module'; +import { AddonModDataFieldNumberModule } from './number/number.module'; +import { AddonModDataFieldPictureModule } from './picture/picture.module'; +import { AddonModDataFieldRadiobuttonModule } from './radiobutton/radiobutton.module'; +import { AddonModDataFieldTextModule } from './text/text.module'; +import { AddonModDataFieldTextareaModule } from './textarea/textarea.module'; +import { AddonModDataFieldUrlModule } from './url/url.module'; + +@NgModule({ + imports: [ + AddonModDataFieldCheckboxModule, + AddonModDataFieldDateModule, + AddonModDataFieldFileModule, + AddonModDataFieldLatlongModule, + AddonModDataFieldMenuModule, + AddonModDataFieldMultimenuModule, + AddonModDataFieldNumberModule, + AddonModDataFieldPictureModule, + AddonModDataFieldRadiobuttonModule, + AddonModDataFieldTextModule, + AddonModDataFieldTextareaModule, + AddonModDataFieldUrlModule, + ], +}) +export class AddonModDataFieldModule { } diff --git a/src/addons/mod/data/fields/file/component/addon-mod-data-field-file.html b/src/addons/mod/data/fields/file/component/addon-mod-data-field-file.html new file mode 100644 index 000000000..4afdd5eb6 --- /dev/null +++ b/src/addons/mod/data/fields/file/component/addon-mod-data-field-file.html @@ -0,0 +1,17 @@ + + + + + + + + + + + + +
+ +
+
diff --git a/src/addons/mod/data/fields/file/component/file.ts b/src/addons/mod/data/fields/file/component/file.ts new file mode 100644 index 000000000..2f1680027 --- /dev/null +++ b/src/addons/mod/data/fields/file/component/file.ts @@ -0,0 +1,81 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component } from '@angular/core'; +import { AddonModDataEntryField, AddonModDataProvider } from '@addons/mod/data/services/data'; +import { AddonModDataFieldPluginComponent } from '@addons/mod/data/classes/field-plugin-component'; +import { CoreFileSession } from '@services/file-session'; +import { CoreWSExternalFile } from '@services/ws'; +import { FileEntry } from '@ionic-native/file'; + +/** + * Component to render data file field. + */ +@Component({ + selector: 'addon-mod-data-field-file', + templateUrl: 'addon-mod-data-field-file.html', +}) +export class AddonModDataFieldFileComponent extends AddonModDataFieldPluginComponent { + + files: (CoreWSExternalFile | FileEntry)[] = []; + component?: string; + componentId?: number; + maxSizeBytes?: number; + + /** + * Get the files from the input value. + * + * @param value Input value. + * @return List of files. + */ + protected getFiles(value?: Partial): (CoreWSExternalFile | FileEntry)[] { + let files = value?.files || []; + + // Reduce to first element. + if (files.length > 0) { + files = [files[0]]; + } + + return files; + } + + /** + * @inheritdoc + */ + protected init(): void { + if (this.searchMode) { + this.addControl('f_' + this.field.id); + + return; + } + + this.component = AddonModDataProvider.COMPONENT; + this.componentId = this.database!.coursemodule; + + this.updateValue(this.value); + + if (this.editMode) { + this.maxSizeBytes = parseInt(this.field.param3, 10); + CoreFileSession.setFiles(this.component, this.database!.id + '_' + this.field.id, this.files); + } + } + + /** + * @inheritdoc + */ + protected updateValue(value?: Partial): void { + this.value = value; + this.files = this.getFiles(value); + } + +} diff --git a/src/addons/mod/data/fields/file/file.module.ts b/src/addons/mod/data/fields/file/file.module.ts new file mode 100644 index 000000000..e3ad12323 --- /dev/null +++ b/src/addons/mod/data/fields/file/file.module.ts @@ -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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldFileComponent } from './component/file'; +import { AddonModDataFieldFileHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldFileComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldFileHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldFileComponent, + ], +}) +export class AddonModDataFieldFileModule {} diff --git a/src/addons/mod/data/fields/file/services/handler.ts b/src/addons/mod/data/fields/file/services/handler.ts new file mode 100644 index 000000000..77157c31e --- /dev/null +++ b/src/addons/mod/data/fields/file/services/handler.ts @@ -0,0 +1,136 @@ +// (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 { + AddonModDataEntryField, + AddonModDataField, + AddonModDataProvider, + AddonModDataSearchEntriesAdvancedFieldFormatted, + AddonModDataSubfieldData, +} from '@addons/mod/data/services/data'; +import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader'; +import { FileEntry } from '@ionic-native/file'; +import { CoreFileSession } from '@services/file-session'; +import { CoreFormFields } from '@singletons/form'; +import { CoreWSExternalFile } from '@services/ws'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldFileComponent } from '../component/file'; + +/** + * Handler for file data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldFileHandlerService implements AddonModDataFieldHandler { + + name = 'AddonModDataFieldFileHandler'; + type = 'file'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldFileComponent; + } + + /** + * @inheritdoc + */ + getFieldSearchData(field: AddonModDataField, inputData: CoreFormFields): AddonModDataSearchEntriesAdvancedFieldFormatted[] { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName], + }]; + } + + return []; + } + + /** + * @inheritdoc + */ + getFieldEditData(field: AddonModDataField): AddonModDataSubfieldData[] { + const files = this.getFieldEditFiles(field); + + return [{ + fieldid: field.id, + subfield: 'file', + files: files, + }]; + } + + /** + * @inheritdoc + */ + getFieldEditFiles(field: AddonModDataField): (CoreWSExternalFile | FileEntry)[] { + return CoreFileSession.getFiles(AddonModDataProvider.COMPONENT, field.dataid + '_' + field.id); + } + + /** + * @inheritdoc + */ + hasFieldDataChanged(field: AddonModDataField, inputData: CoreFormFields, originalFieldData: AddonModDataEntryField): boolean { + const files = CoreFileSession.getFiles(AddonModDataProvider.COMPONENT, field.dataid + '_' + field.id) || []; + let originalFiles = (originalFieldData && originalFieldData.files) || []; + + if (originalFiles.length) { + originalFiles = [originalFiles[0]]; + } + + return CoreFileUploader.areFileListDifferent(files, originalFiles); + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + /** + * @inheritdoc + */ + overrideData( + originalContent: AddonModDataEntryField, + offlineContent: CoreFormFields, + offlineFiles?: FileEntry[], + ): AddonModDataEntryField { + const uploadedFilesResult: CoreFileUploaderStoreFilesResult = offlineContent?.file; + + if (uploadedFilesResult && uploadedFilesResult.offline > 0 && offlineFiles && offlineFiles?.length > 0) { + originalContent.content = offlineFiles[0].name; + originalContent.files = [offlineFiles[0]]; + } else if (uploadedFilesResult && uploadedFilesResult.online && uploadedFilesResult.online.length > 0) { + originalContent.content = uploadedFilesResult.online[0].filename || ''; + originalContent.files = [uploadedFilesResult.online[0]]; + } + + return originalContent; + } + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModDataFieldFileHandler = makeSingleton(AddonModDataFieldFileHandlerService); diff --git a/src/addons/mod/data/fields/latlong/component/addon-mod-data-field-latlong.html b/src/addons/mod/data/fields/latlong/component/addon-mod-data-field-latlong.html new file mode 100644 index 000000000..8d99619ba --- /dev/null +++ b/src/addons/mod/data/fields/latlong/component/addon-mod-data-field-latlong.html @@ -0,0 +1,27 @@ + + + + + +
+ + °N +
+
+ + °E +
+
+ + + {{ 'addon.mod_data.mylocation' | translate }} + +
+ +
+
+ + + + {{ formatLatLong(north, east) }} + diff --git a/src/addons/mod/data/fields/latlong/component/latlong.ts b/src/addons/mod/data/fields/latlong/component/latlong.ts new file mode 100644 index 000000000..2a120d0da --- /dev/null +++ b/src/addons/mod/data/fields/latlong/component/latlong.ts @@ -0,0 +1,164 @@ +// (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 { AddonModDataFieldPluginComponent } from '@addons/mod/data/classes/field-plugin-component'; +import { AddonModDataEntryField } from '@addons/mod/data/services/data'; +import { Component } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; +import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; +import { CoreAnyError } from '@classes/errors/error'; +import { CoreApp } from '@services/app'; +import { CoreGeolocation, CoreGeolocationError, CoreGeolocationErrorReason } from '@services/geolocation'; +import { CoreDomUtils } from '@services/utils/dom'; + +/** + * Component to render data latlong field. + */ +@Component({ + selector: 'addon-mod-data-field-latlong', + templateUrl: 'addon-mod-data-field-latlong.html', +}) +export class AddonModDataFieldLatlongComponent extends AddonModDataFieldPluginComponent { + + north?: number; + east?: number; + + constructor( + fb: FormBuilder, + protected sanitizer: DomSanitizer, + ) { + super(fb); + } + + /** + * Format latitude and longitude in a simple text. + * + * @param north Degrees north. + * @param east Degrees East. + * @return Readable Latitude and logitude. + */ + formatLatLong(north?: number, east?: number): string { + if (typeof north !== 'undefined' || typeof east !== 'undefined') { + north = north || 0; + east = east || 0; + const northFixed = Math.abs(north).toFixed(4); + const eastFixed = Math.abs(east).toFixed(4); + + return northFixed + (north < 0 ? '°S' : '°N') + ' ' + eastFixed + (east < 0 ? '°W' : '°E'); + } + + return ''; + } + + /** + * Get link to maps from latitude and longitude. + * + * @param north Degrees north. + * @param east Degrees East. + * @return Link to maps depending on platform. + */ + getLatLongLink(north?: number, east?: number): SafeUrl { + let url = ''; + if (typeof north !== 'undefined' || typeof east !== 'undefined') { + const northFixed = north ? north.toFixed(4) : '0.0000'; + const eastFixed = east ? east.toFixed(4) : '0.0000'; + + if (CoreApp.isIOS()) { + url = 'http://maps.apple.com/?ll=' + northFixed + ',' + eastFixed + '&near=' + northFixed + ',' + eastFixed; + } else { + url = 'geo:' + northFixed + ',' + eastFixed; + } + } + + return this.sanitizer.bypassSecurityTrustUrl(url); + } + + /** + * @inheritdoc + */ + protected init(): void { + if (this.value) { + this.updateValue(this.value); + } + + if (this.editMode) { + this.addControl('f_' + this.field.id + '_0', this.north); + this.addControl('f_' + this.field.id + '_1', this.east); + } else if (this.searchMode) { + this.addControl('f_' + this.field.id); + } + } + + /** + * @inheritdoc + */ + protected updateValue(value?: Partial): void { + this.value = value; + this.north = (value && parseFloat(value.content!)) || undefined; + this.east = (value && parseFloat(value.content1!)) || undefined; + } + + /** + * Get user location. + * + * @param $event The event. + */ + async getLocation(event: Event): Promise { + event.preventDefault(); + + const modal = await CoreDomUtils.showModalLoading('addon.mod_data.gettinglocation', true); + + try { + const coordinates = await CoreGeolocation.getCoordinates(); + + this.form?.controls['f_' + this.field.id + '_0'].setValue(coordinates.latitude); + this.form?.controls['f_' + this.field.id + '_1'].setValue(coordinates.longitude); + } catch (error) { + this.showLocationErrorModal(error); + } + + modal.dismiss(); + } + + /** + * Show the appropriate error modal for the given error getting the location. + * + * @param error Location error. + */ + protected showLocationErrorModal(error: CoreAnyError): void { + if (error instanceof CoreGeolocationError) { + CoreDomUtils.showErrorModal(this.getGeolocationErrorMessage(error), true); + + return; + } + + CoreDomUtils.showErrorModalDefault(error, 'Error getting location'); + } + + /** + * Get error message from a geolocation error. + * + * @param error Geolocation error. + */ + protected getGeolocationErrorMessage(error: CoreGeolocationError): string { + // tslint:disable-next-line: switch-default + switch (error.reason) { + case CoreGeolocationErrorReason.PermissionDenied: + return 'addon.mod_data.locationpermissiondenied'; + case CoreGeolocationErrorReason.LocationNotEnabled: + return 'addon.mod_data.locationnotenabled'; + } + } + +} diff --git a/src/addons/mod/data/fields/latlong/latlong.module.ts b/src/addons/mod/data/fields/latlong/latlong.module.ts new file mode 100644 index 000000000..1dc6aacc5 --- /dev/null +++ b/src/addons/mod/data/fields/latlong/latlong.module.ts @@ -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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldLatlongComponent } from './component/latlong'; +import { AddonModDataFieldLatlongHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldLatlongComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldLatlongHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldLatlongComponent, + ], +}) +export class AddonModDataFieldLatlongModule {} diff --git a/src/addons/mod/data/fields/latlong/services/handler.ts b/src/addons/mod/data/fields/latlong/services/handler.ts new file mode 100644 index 000000000..c55606f22 --- /dev/null +++ b/src/addons/mod/data/fields/latlong/services/handler.ts @@ -0,0 +1,138 @@ +// (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 { + AddonModDataEntryField, + AddonModDataField, + AddonModDataSearchEntriesAdvancedFieldFormatted, + AddonModDataSubfieldData, +} from '@addons/mod/data/services/data'; +import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreFormFields } from '@singletons/form'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldLatlongComponent } from '../component/latlong'; + +/** + * Handler for latlong data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldLatlongHandlerService implements AddonModDataFieldHandler { + + name = 'AddonModDataFieldLatlongHandler'; + type = 'latlong'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldLatlongComponent; + } + + /** + * @inheritdoc + */ + getFieldSearchData( + field: AddonModDataField, + inputData: CoreFormFields, + ): AddonModDataSearchEntriesAdvancedFieldFormatted[] { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName], + }]; + } + + return []; + } + + /** + * @inheritdoc + */ + getFieldEditData(field: AddonModDataField, inputData: CoreFormFields): AddonModDataSubfieldData[] { + const fieldName = 'f_' + field.id; + + return [ + { + fieldid: field.id, + subfield: '0', + value: inputData[fieldName + '_0'] || '', + }, + { + fieldid: field.id, + subfield: '1', + value: inputData[fieldName + '_1'] || '', + }, + ]; + } + + /** + * @inheritdoc + */ + hasFieldDataChanged( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): boolean { + const fieldName = 'f_' + field.id; + const lat = inputData[fieldName + '_0'] || ''; + const long = inputData[fieldName + '_1'] || ''; + const originalLat = (originalFieldData && originalFieldData.content) || ''; + const originalLong = (originalFieldData && originalFieldData.content1) || ''; + + return lat != originalLat || long != originalLong; + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + let valueCount = 0; + + // The lat long class has two values that need to be checked. + inputData.forEach((value) => { + if (typeof value.value != 'undefined' && value.value != '') { + valueCount++; + } + }); + + // If we get here then only one field has been filled in. + if (valueCount == 1) { + return Translate.instant('addon.mod_data.latlongboth'); + } else if (field.required && valueCount == 0) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + /** + * @inheritdoc + */ + overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields): AddonModDataEntryField { + originalContent.content = offlineContent[0] || ''; + originalContent.content1 = offlineContent[1] || ''; + + return originalContent; + } + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModDataFieldLatlongHandler = makeSingleton(AddonModDataFieldLatlongHandlerService); diff --git a/src/addons/mod/data/fields/menu/component/addon-mod-data-field-menu.html b/src/addons/mod/data/fields/menu/component/addon-mod-data-field-menu.html new file mode 100644 index 000000000..327531cac --- /dev/null +++ b/src/addons/mod/data/fields/menu/component/addon-mod-data-field-menu.html @@ -0,0 +1,11 @@ + + + + {{ 'addon.mod_data.menuchoose' | translate }} + {{option}} + + + + +{{ value.content }} diff --git a/src/addons/mod/data/fields/menu/component/menu.ts b/src/addons/mod/data/fields/menu/component/menu.ts new file mode 100644 index 000000000..21b56b2ec --- /dev/null +++ b/src/addons/mod/data/fields/menu/component/menu.ts @@ -0,0 +1,47 @@ +// (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 } from '@angular/core'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data menu field. + */ +@Component({ + selector: 'addon-mod-data-field-menu', + templateUrl: 'addon-mod-data-field-menu.html', +}) +export class AddonModDataFieldMenuComponent extends AddonModDataFieldPluginComponent { + + options: string[] = []; + + /** + * Initialize field. + */ + protected init(): void { + if (this.displayMode) { + return; + } + + this.options = this.field.param1.split('\n'); + + let val: string | undefined; + if (this.editMode && this.value) { + val = this.value.content; + } + + this.addControl('f_' + this.field.id, val); + } + +} diff --git a/src/addons/mod/data/fields/menu/menu.module.ts b/src/addons/mod/data/fields/menu/menu.module.ts new file mode 100644 index 000000000..12a12d4fd --- /dev/null +++ b/src/addons/mod/data/fields/menu/menu.module.ts @@ -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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldMenuComponent } from './component/menu'; +import { AddonModDataFieldMenuHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldMenuComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldMenuHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldMenuComponent, + ], +}) +export class AddonModDataFieldMenuModule {} diff --git a/src/addons/mod/data/fields/menu/services/handler.ts b/src/addons/mod/data/fields/menu/services/handler.ts new file mode 100644 index 000000000..ffd068aa2 --- /dev/null +++ b/src/addons/mod/data/fields/menu/services/handler.ts @@ -0,0 +1,116 @@ +// (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 { + AddonModDataEntryField, + AddonModDataField, + AddonModDataSearchEntriesAdvancedFieldFormatted, + AddonModDataSubfieldData, +} from '@addons/mod/data/services/data'; +import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreFormFields } from '@singletons/form'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldMenuComponent } from '../component/menu'; + +/** + * Handler for menu data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldMenuHandlerService implements AddonModDataFieldHandler { + + name = 'AddonModDataFieldMenuHandler'; + type = 'menu'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldMenuComponent; + } + + /** + * @inheritdoc + */ + getFieldSearchData( + field: AddonModDataField, + inputData: CoreFormFields, + ): AddonModDataSearchEntriesAdvancedFieldFormatted[] { + const fieldName = 'f_' + field.id; + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName], + }]; + } + + return []; + } + + /** + * @inheritdoc + */ + getFieldEditData(field: AddonModDataField, inputData: CoreFormFields): AddonModDataSubfieldData[] { + + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + fieldid: field.id, + value: inputData[fieldName], + }]; + } + + return []; + } + + hasFieldDataChanged( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): boolean { + const fieldName = 'f_' + field.id; + const input = inputData[fieldName] || ''; + const content = originalFieldData?.content || ''; + + return input != content; + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + /** + * @inheritdoc + */ + overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields): AddonModDataEntryField { + originalContent.content = offlineContent[''] || ''; + + return originalContent; + } + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModDataFieldMenuHandler = makeSingleton(AddonModDataFieldMenuHandlerService); diff --git a/src/addons/mod/data/fields/multimenu/component/addon-mod-data-field-multimenu.html b/src/addons/mod/data/fields/multimenu/component/addon-mod-data-field-multimenu.html new file mode 100644 index 000000000..56a062c1c --- /dev/null +++ b/src/addons/mod/data/fields/multimenu/component/addon-mod-data-field-multimenu.html @@ -0,0 +1,17 @@ + + + + {{option.key}} + + + + + + {{ 'addon.mod_data.selectedrequired' | translate }} + + + + + + diff --git a/src/addons/mod/data/fields/multimenu/component/multimenu.ts b/src/addons/mod/data/fields/multimenu/component/multimenu.ts new file mode 100644 index 000000000..88d2ce183 --- /dev/null +++ b/src/addons/mod/data/fields/multimenu/component/multimenu.ts @@ -0,0 +1,70 @@ +// (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 { AddonModDataEntryField } from '@addons/mod/data/services/data'; +import { Component } from '@angular/core'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data multimenu field. + */ +@Component({ + selector: 'addon-mod-data-field-multimenu', + templateUrl: 'addon-mod-data-field-multimenu.html', +}) +export class AddonModDataFieldMultimenuComponent extends AddonModDataFieldPluginComponent { + + options: { + key: string; + value: string; + }[] = []; + + /** + * @inheritdoc + */ + protected init(): void { + if (this.displayMode) { + this.updateValue(this.value); + + return; + } + + this.options = this.field.param1.split(/\r?\n/).map((option) => ({ key: option, value: option })); + + const values: string[] = []; + if (this.editMode && this.value?.content) { + this.value.content.split('##').forEach((value) => { + const x = this.options.findIndex((option) => value == option.key); + if (x >= 0) { + values.push(value); + } + }); + } + + if (this.searchMode) { + this.addControl('f_' + this.field.id + '_allreq'); + } + + this.addControl('f_' + this.field.id, values); + } + + /** + * @inheritdoc + */ + protected updateValue(value?: Partial): void { + this.value = value || {}; + this.value.content = value?.content && value.content.split('##').join('
'); + } + +} diff --git a/src/addons/mod/data/fields/multimenu/multimenu.module.ts b/src/addons/mod/data/fields/multimenu/multimenu.module.ts new file mode 100644 index 000000000..3ceb09691 --- /dev/null +++ b/src/addons/mod/data/fields/multimenu/multimenu.module.ts @@ -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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldMultimenuComponent } from './component/multimenu'; +import { AddonModDataFieldMultimenuHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldMultimenuComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldMultimenuHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldMultimenuComponent, + ], +}) +export class AddonModDataFieldMultimenuModule {} diff --git a/src/addons/mod/data/fields/multimenu/services/handler.ts b/src/addons/mod/data/fields/multimenu/services/handler.ts new file mode 100644 index 000000000..db3b290e7 --- /dev/null +++ b/src/addons/mod/data/fields/multimenu/services/handler.ts @@ -0,0 +1,127 @@ +// (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 { + AddonModDataEntryField, + AddonModDataField, + AddonModDataSearchEntriesAdvancedFieldFormatted, + AddonModDataSubfieldData, +} from '@addons/mod/data/services/data'; +import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreFormFields } from '@singletons/form'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldMultimenuComponent } from '../component/multimenu'; + +/** + * Handler for multimenu data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldMultimenuHandlerService implements AddonModDataFieldHandler { + + name = 'AddonModDataFieldMultimenuHandler'; + type = 'multimenu'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldMultimenuComponent; + } + + /** + * @inheritdoc + */ + getFieldSearchData( + field: AddonModDataField, + inputData: CoreFormFields, + ): AddonModDataSearchEntriesAdvancedFieldFormatted[] { + const fieldName = 'f_' + field.id; + const reqName = 'f_' + field.id + '_allreq'; + + if (inputData[fieldName]) { + + const values: AddonModDataSearchEntriesAdvancedFieldFormatted[] = []; + values.push({ + name: fieldName, + value: inputData[fieldName], + }); + + if (inputData[reqName]) { + values.push({ + name: reqName, + value: true, + }); + } + + return values; + } + + return []; + } + + /** + * @inheritdoc + */ + getFieldEditData(field: AddonModDataField, inputData: CoreFormFields): AddonModDataSubfieldData[] { + + const fieldName = 'f_' + field.id; + + return [{ + fieldid: field.id, + value: inputData[fieldName] || [], + }]; + } + + /** + * @inheritdoc + */ + hasFieldDataChanged( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): boolean { + const fieldName = 'f_' + field.id; + const content = originalFieldData?.content || ''; + + return inputData[fieldName].join('##') != content; + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + /** + * @inheritdoc + */ + overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields): AddonModDataEntryField { + originalContent.content = (offlineContent[''] && offlineContent[''].join('##')) || ''; + + return originalContent; + } + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModDataFieldMultimenuHandler = makeSingleton(AddonModDataFieldMultimenuHandlerService); diff --git a/src/addons/mod/data/fields/number/component/addon-mod-data-field-number.html b/src/addons/mod/data/fields/number/component/addon-mod-data-field-number.html new file mode 100644 index 000000000..2bd75978b --- /dev/null +++ b/src/addons/mod/data/fields/number/component/addon-mod-data-field-number.html @@ -0,0 +1,7 @@ + + + + + + +{{ value.content }} diff --git a/src/addons/mod/data/fields/number/component/number.ts b/src/addons/mod/data/fields/number/component/number.ts new file mode 100644 index 000000000..c6e37b6e7 --- /dev/null +++ b/src/addons/mod/data/fields/number/component/number.ts @@ -0,0 +1,44 @@ +// (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 } from '@angular/core'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data number field. + */ +@Component({ + selector: 'addon-mod-data-field-number', + templateUrl: 'addon-mod-data-field-number.html', +}) +export class AddonModDataFieldNumberComponent extends AddonModDataFieldPluginComponent{ + + /** + * @inheritdoc + */ + protected init(): void { + if (this.displayMode) { + return; + } + + let value: number | string | undefined; + if (this.editMode && this.value) { + const v = parseFloat(this.value.content || ''); + value = isNaN(v) ? '' : v; + } + + this.addControl('f_' + this.field.id, value); + } + +} diff --git a/src/addons/mod/data/fields/number/number.module.ts b/src/addons/mod/data/fields/number/number.module.ts new file mode 100644 index 000000000..5dd352b23 --- /dev/null +++ b/src/addons/mod/data/fields/number/number.module.ts @@ -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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldNumberComponent } from './component/number'; +import { AddonModDataFieldNumberHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldNumberComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldNumberHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldNumberComponent, + ], +}) +export class AddonModDataFieldNumberModule {} diff --git a/src/addons/mod/data/fields/number/services/handler.ts b/src/addons/mod/data/fields/number/services/handler.ts new file mode 100644 index 000000000..de5400b06 --- /dev/null +++ b/src/addons/mod/data/fields/number/services/handler.ts @@ -0,0 +1,63 @@ +// (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 { AddonModDataEntryField, AddonModDataField, AddonModDataSubfieldData } from '@addons/mod/data/services/data'; +import { Injectable, Type } from '@angular/core'; +import { CoreFormFields } from '@singletons/form'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldTextHandlerService } from '../../text/services/handler'; +import { AddonModDataFieldNumberComponent } from '../component/number'; + +/** + * Handler for number data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldNumberHandlerService extends AddonModDataFieldTextHandlerService { + + name = 'AddonModDataFieldNumberHandler'; + type = 'number'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldNumberComponent; + } + + /** + * @inheritdoc + */ + hasFieldDataChanged( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): boolean { + const fieldName = 'f_' + field.id; + const input = inputData[fieldName] || ''; + const content = originalFieldData?.content || ''; + + return input != content; + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (field.required && (!inputData || !inputData.length || inputData[0].value == '')) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + +} +export const AddonModDataFieldNumberHandler = makeSingleton(AddonModDataFieldNumberHandlerService); diff --git a/src/addons/mod/data/fields/picture/component/addon-mod-data-field-picture.html b/src/addons/mod/data/fields/picture/component/addon-mod-data-field-picture.html new file mode 100644 index 000000000..edfd06cfe --- /dev/null +++ b/src/addons/mod/data/fields/picture/component/addon-mod-data-field-picture.html @@ -0,0 +1,22 @@ + + + + + + + {{ 'addon.mod_data.alttext' | translate }} + + + + + + + + + + + + + diff --git a/src/addons/mod/data/fields/picture/component/picture.ts b/src/addons/mod/data/fields/picture/component/picture.ts new file mode 100644 index 000000000..11d19be88 --- /dev/null +++ b/src/addons/mod/data/fields/picture/component/picture.ts @@ -0,0 +1,142 @@ +// (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 { AddonModDataEntryField, AddonModDataProvider } from '@addons/mod/data/services/data'; +import { Component } from '@angular/core'; +import { FileEntry } from '@ionic-native/file'; +import { CoreFileSession } from '@services/file-session'; +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreWSExternalFile } from '@services/ws'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data picture field. + */ +@Component({ + selector: 'addon-mod-data-field-picture', + templateUrl: 'addon-mod-data-field-picture.html', +}) +export class AddonModDataFieldPictureComponent extends AddonModDataFieldPluginComponent { + + files: (CoreWSExternalFile | FileEntry)[] = []; + component?: string; + componentId?: number; + maxSizeBytes?: number; + + image?: CoreWSExternalFile | FileEntry; + entryId?: number; + imageUrl?: string; + title?: string; + width?: string; + height?: string; + + /** + * Get the files from the input value. + * + * @param value Input value. + * @return List of files. + */ + protected getFiles(value?: Partial): (CoreWSExternalFile | FileEntry)[] { + let files = value?.files || []; + + // Reduce to first element. + if (files.length > 0) { + files = [files[0]]; + } + + return files; + } + + /** + * Find file in a list. + * + * @param files File list where to search. + * @param filenameSeek Filename to search. + * @return File found or false. + */ + protected findFile( + files: (CoreWSExternalFile | FileEntry)[], + filenameSeek: string, + ): CoreWSExternalFile | FileEntry | undefined { + return files.find((file) => ('name' in file ? file.name : file.filename) == filenameSeek) || undefined; + } + + /** + * @inheritdoc + */ + protected init(): void { + if (this.searchMode) { + this.addControl('f_' + this.field.id); + + return; + } + + this.component = AddonModDataProvider.COMPONENT; + this.componentId = this.database!.coursemodule; + + this.updateValue(this.value); + + if (this.editMode) { + this.maxSizeBytes = parseInt(this.field.param3, 10); + CoreFileSession.setFiles(this.component, this.database!.id + '_' + this.field.id, this.files); + + const alttext = (this.value && this.value.content1) || ''; + this.addControl('f_' + this.field.id + '_alttext', alttext); + } + } + + /** + * @inheritdoc + */ + protected updateValue(value?: Partial): void { + + // Edit mode, the list shouldn't change so there is no need to watch it. + const files = value?.files || []; + + // Get image or thumb. + if (files.length > 0) { + const filenameSeek = this.listMode + ? 'thumb_' + value?.content + : value?.content; + this.image = this.findFile(files, filenameSeek || ''); + + if (!this.image && this.listMode) { + this.image = this.findFile(files, value?.content || ''); + } + + if (this.image) { + this.files = [this.image]; + } + } else { + this.image = undefined; + this.files = []; + } + + if (!this.editMode) { + this.entryId = (value && value.recordid) || undefined; + this.title = (value && value.content1) || ''; + this.imageUrl = undefined; + setTimeout(() => { + if (this.image) { + this.imageUrl = 'name' in this.image + ? this.image.toURL() // Is Offline. + : this.image.fileurl; + } + }, 1); + + this.width = CoreDomUtils.formatPixelsSize(this.field.param1); + this.height = CoreDomUtils.formatPixelsSize(this.field.param2); + } + } + +} diff --git a/src/addons/mod/data/fields/picture/picture.module.ts b/src/addons/mod/data/fields/picture/picture.module.ts new file mode 100644 index 000000000..86806e2f2 --- /dev/null +++ b/src/addons/mod/data/fields/picture/picture.module.ts @@ -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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldPictureComponent } from './component/picture'; +import { AddonModDataFieldPictureHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldPictureComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldPictureHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldPictureComponent, + ], +}) +export class AddonModDataFieldPictureModule {} diff --git a/src/addons/mod/data/fields/picture/services/handler.ts b/src/addons/mod/data/fields/picture/services/handler.ts new file mode 100644 index 000000000..cd1c26dac --- /dev/null +++ b/src/addons/mod/data/fields/picture/services/handler.ts @@ -0,0 +1,181 @@ +// (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 { + AddonModDataEntryField, + AddonModDataField, + AddonModDataProvider, + AddonModDataSearchEntriesAdvancedFieldFormatted, + AddonModDataSubfieldData, +} from '@addons/mod/data/services/data'; +import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreFileUploader, CoreFileUploaderStoreFilesResult } from '@features/fileuploader/services/fileuploader'; +import { FileEntry } from '@ionic-native/file'; +import { CoreFileSession } from '@services/file-session'; +import { CoreFormFields } from '@singletons/form'; +import { CoreWSExternalFile } from '@services/ws'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldPictureComponent } from '../component/picture'; + +/** + * Handler for picture data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldPictureHandlerService implements AddonModDataFieldHandler { + + name = 'AddonModDataFieldPictureHandler'; + type = 'picture'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldPictureComponent; + } + + /** + * @inheritdoc + */ + getFieldSearchData( + field: AddonModDataField, + inputData: CoreFormFields, + ): AddonModDataSearchEntriesAdvancedFieldFormatted[] { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName], + }]; + } + + return []; + } + + /** + * @inheritdoc + */ + getFieldEditData(field: AddonModDataField, inputData: CoreFormFields): AddonModDataSubfieldData[] { + const files = this.getFieldEditFiles(field); + const fieldName = 'f_' + field.id + '_alttext'; + + return [ + { + fieldid: field.id, + subfield: 'file', + files: files, + }, + { + fieldid: field.id, + subfield: 'alttext', + value: inputData[fieldName], + }, + ]; + } + + /** + * @inheritdoc + */ + getFieldEditFiles(field: AddonModDataField): (CoreWSExternalFile | FileEntry)[] { + return CoreFileSession.getFiles(AddonModDataProvider.COMPONENT, field.dataid + '_' + field.id); + } + + /** + * @inheritdoc + */ + hasFieldDataChanged( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): boolean { + const fieldName = 'f_' + field.id + '_alttext'; + const altText = inputData[fieldName] || ''; + const originalAltText = originalFieldData?.content1 || ''; + if (altText != originalAltText) { + return true; + } + + const files = this.getFieldEditFiles(field) || []; + let originalFiles = originalFieldData?.files || []; + + // Get image. + if (originalFiles.length > 0) { + const filenameSeek = originalFieldData?.content || ''; + const file = originalFiles.find((file) => ('name' in file ? file.name : file.filename) == filenameSeek); + if (file) { + originalFiles = [file]; + } + } + + return CoreFileUploader.areFileListDifferent(files, originalFiles); + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (!field.required) { + return; + } + + if (!inputData || !inputData.length) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + const found = inputData.some((input) => { + if (typeof input.subfield != 'undefined' && input.subfield == 'file') { + return !!input.value; + } + + return false; + }); + + if (!found) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + /** + * @inheritdoc + */ + overrideData( + originalContent: AddonModDataEntryField, + offlineContent: CoreFormFields, + offlineFiles?: FileEntry[], + ): AddonModDataEntryField { + const uploadedFilesResult: CoreFileUploaderStoreFilesResult = offlineContent?.file; + + if (uploadedFilesResult && uploadedFilesResult.offline > 0 && offlineFiles && offlineFiles?.length > 0) { + originalContent.content = offlineFiles[0].name; + originalContent.files = [offlineFiles[0]]; + } else if (uploadedFilesResult && uploadedFilesResult.online && uploadedFilesResult.online.length > 0) { + originalContent.content = uploadedFilesResult.online[0].filename || ''; + originalContent.files = [uploadedFilesResult.online[0]]; + } + + originalContent.content1 = offlineContent.alttext || ''; + + return originalContent; + } + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModDataFieldPictureHandler = makeSingleton(AddonModDataFieldPictureHandlerService); diff --git a/src/addons/mod/data/fields/radiobutton/component/addon-mod-data-field-radiobutton.html b/src/addons/mod/data/fields/radiobutton/component/addon-mod-data-field-radiobutton.html new file mode 100644 index 000000000..75e003b7c --- /dev/null +++ b/src/addons/mod/data/fields/radiobutton/component/addon-mod-data-field-radiobutton.html @@ -0,0 +1,11 @@ + + + + {{ 'addon.mod_data.menuchoose' | translate }} + {{option}} + + + + +{{ value.content }} diff --git a/src/addons/mod/data/fields/radiobutton/component/radiobutton.ts b/src/addons/mod/data/fields/radiobutton/component/radiobutton.ts new file mode 100644 index 000000000..eae23dd3c --- /dev/null +++ b/src/addons/mod/data/fields/radiobutton/component/radiobutton.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 { Component } from '@angular/core'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data radiobutton field. + */ +@Component({ + selector: 'addon-mod-data-field-radiobutton', + templateUrl: 'addon-mod-data-field-radiobutton.html', +}) +export class AddonModDataFieldRadiobuttonComponent extends AddonModDataFieldPluginComponent { + + options: string[] = []; + + /** + * Initialize field. + */ + protected init(): void { + if (this.displayMode) { + return; + } + + this.options = this.field.param1.split('\n'); + + let val: string | undefined; + if (this.editMode && this.value) { + val = this.value.content; + } + + this.addControl('f_' + this.field.id, val); + } + +} diff --git a/src/addons/mod/data/fields/radiobutton/radiobutton.module.ts b/src/addons/mod/data/fields/radiobutton/radiobutton.module.ts new file mode 100644 index 000000000..ab535000c --- /dev/null +++ b/src/addons/mod/data/fields/radiobutton/radiobutton.module.ts @@ -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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldRadiobuttonComponent } from './component/radiobutton'; +import { AddonModDataFieldRadiobuttonHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldRadiobuttonComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldRadiobuttonHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldRadiobuttonComponent, + ], +}) +export class AddonModDataFieldRadiobuttonModule {} diff --git a/src/addons/mod/data/fields/radiobutton/services/handler.ts b/src/addons/mod/data/fields/radiobutton/services/handler.ts new file mode 100644 index 000000000..98f9bc526 --- /dev/null +++ b/src/addons/mod/data/fields/radiobutton/services/handler.ts @@ -0,0 +1,114 @@ +// (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 { + AddonModDataEntryField, + AddonModDataField, + AddonModDataSearchEntriesAdvancedFieldFormatted, + AddonModDataSubfieldData, +} from '@addons/mod/data/services/data'; +import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreFormFields } from '@singletons/form'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldRadiobuttonComponent } from '../component/radiobutton'; + +/** + * Handler for checkbox data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldRadiobuttonHandlerService implements AddonModDataFieldHandler { + + name = 'AddonModDataFieldRadiobuttonHandler'; + type = 'radiobutton'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldRadiobuttonComponent; + } + + /** + * @inheritdoc + */ + getFieldSearchData( + field: AddonModDataField, + inputData: CoreFormFields, + ): AddonModDataSearchEntriesAdvancedFieldFormatted[] { + const fieldName = 'f_' + field.id; + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName], + }]; + } + + return []; + } + + /** + * @inheritdoc + */ + getFieldEditData(field: AddonModDataField, inputData: CoreFormFields): AddonModDataSubfieldData[] { + const fieldName = 'f_' + field.id; + + return [{ + fieldid: field.id, + value: inputData[fieldName] || '', + }]; + } + + /** + * @inheritdoc + */ + hasFieldDataChanged( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): boolean { + const fieldName = 'f_' + field.id; + const input = inputData[fieldName] || ''; + const content = originalFieldData?.content || ''; + + return input != content; + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + /** + * @inheritdoc + */ + overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields): AddonModDataEntryField { + originalContent.content = offlineContent[''] || ''; + + return originalContent; + } + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModDataFieldRadiobuttonHandler = makeSingleton(AddonModDataFieldRadiobuttonHandlerService); diff --git a/src/addons/mod/data/fields/text/component/addon-mod-data-field-text.html b/src/addons/mod/data/fields/text/component/addon-mod-data-field-text.html new file mode 100644 index 000000000..af610893a --- /dev/null +++ b/src/addons/mod/data/fields/text/component/addon-mod-data-field-text.html @@ -0,0 +1,7 @@ + + + + + + +{{ value.content }} diff --git a/src/addons/mod/data/fields/text/component/text.ts b/src/addons/mod/data/fields/text/component/text.ts new file mode 100644 index 000000000..116458c5b --- /dev/null +++ b/src/addons/mod/data/fields/text/component/text.ts @@ -0,0 +1,43 @@ +// (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 } from '@angular/core'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data text field. + */ +@Component({ + selector: 'addon-mod-data-field-text', + templateUrl: 'addon-mod-data-field-text.html', +}) +export class AddonModDataFieldTextComponent extends AddonModDataFieldPluginComponent { + + /** + * @inheritdoc + */ + protected init(): void { + if (this.displayMode) { + return; + } + + let value: string | undefined; + if (this.editMode && this.value) { + value = this.value.content; + } + + this.addControl('f_' + this.field.id, value); + } + +} diff --git a/src/addons/mod/data/fields/text/services/handler.ts b/src/addons/mod/data/fields/text/services/handler.ts new file mode 100644 index 000000000..8c9ba3b92 --- /dev/null +++ b/src/addons/mod/data/fields/text/services/handler.ts @@ -0,0 +1,117 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { + AddonModDataEntryField, + AddonModDataField, + AddonModDataSearchEntriesAdvancedFieldFormatted, + AddonModDataSubfieldData, +} from '@addons/mod/data/services/data'; +import { AddonModDataFieldHandler } from '@addons/mod/data/services/data-fields-delegate'; +import { Injectable, Type } from '@angular/core'; +import { CoreFormFields } from '@singletons/form'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldTextComponent } from '../component/text'; + +/** + * Handler for number data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldTextHandlerService implements AddonModDataFieldHandler { + + name = 'AddonModDataFieldTextHandler'; + type = 'text'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldTextComponent; + } + + /** + * @inheritdoc + */ + getFieldSearchData(field: AddonModDataField, inputData: CoreFormFields): AddonModDataSearchEntriesAdvancedFieldFormatted[] { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName], + }]; + } + + return []; + } + + /** + * @inheritdoc + */ + getFieldEditData( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, // eslint-disable-line @typescript-eslint/no-unused-vars + ): AddonModDataSubfieldData[] { + + const fieldName = 'f_' + field.id; + + return [{ + fieldid: field.id, + value: inputData[fieldName] || '', + }]; + } + + /** + * @inheritdoc + */ + hasFieldDataChanged( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): boolean { + const fieldName = 'f_' + field.id; + const input = inputData[fieldName] || ''; + const content = originalFieldData?.content || ''; + + return input != content; + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + /** + * @inheritdoc + */ + overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields): AddonModDataEntryField { + originalContent.content = offlineContent[''] || ''; + + return originalContent; + } + + /** + * @inheritdoc + */ + async isEnabled(): Promise { + return true; + } + +} +export const AddonModDataFieldTextHandler = makeSingleton(AddonModDataFieldTextHandlerService); diff --git a/src/addons/mod/data/fields/text/text.module.ts b/src/addons/mod/data/fields/text/text.module.ts new file mode 100644 index 000000000..890f6b9c3 --- /dev/null +++ b/src/addons/mod/data/fields/text/text.module.ts @@ -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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldTextComponent } from './component/text'; +import { AddonModDataFieldTextHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldTextComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldTextHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldTextComponent, + ], +}) +export class AddonModDataFieldTextModule {} diff --git a/src/addons/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html b/src/addons/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html new file mode 100644 index 000000000..e6d25010d --- /dev/null +++ b/src/addons/mod/data/fields/textarea/component/addon-mod-data-field-textarea.html @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/addons/mod/data/fields/textarea/component/textarea.ts b/src/addons/mod/data/fields/textarea/component/textarea.ts new file mode 100644 index 000000000..8b459b8ef --- /dev/null +++ b/src/addons/mod/data/fields/textarea/component/textarea.ts @@ -0,0 +1,65 @@ +// (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 } from '@angular/core'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; +import { AddonModDataEntryField, AddonModDataProvider } from '@addons/mod/data/services/data'; +import { CoreTextUtils } from '@services/utils/text'; +import { CoreWSExternalFile } from '@services/ws'; + +/** + * Component to render data number field. + */ +@Component({ + selector: 'addon-mod-data-field-textarea', + templateUrl: 'addon-mod-data-field-textarea.html', +}) +export class AddonModDataFieldTextareaComponent extends AddonModDataFieldPluginComponent { + + component?: string; + componentId?: number; + + /** + * Format value to be shown. Replacing plugin file Urls. + * + * @param value Value to replace. + * @return Replaced string to be rendered. + */ + format(value?: Partial): string { + const files: CoreWSExternalFile[] = (value && value.files) || []; + + return value ? CoreTextUtils.replacePluginfileUrls(value.content || '', files) : ''; + } + + /** + * Initialize field. + */ + protected init(): void { + this.component = AddonModDataProvider.COMPONENT; + this.componentId = this.database?.coursemodule; + + if (this.displayMode) { + return; + } + + let text: string | undefined; + // Check if rich text editor is enabled. + if (this.editMode) { + text = this.format(this.value); + } + + this.addControl('f_' + this.field.id, text); + } + +} diff --git a/src/addons/mod/data/fields/textarea/services/handler.ts b/src/addons/mod/data/fields/textarea/services/handler.ts new file mode 100644 index 000000000..2bfc9b85f --- /dev/null +++ b/src/addons/mod/data/fields/textarea/services/handler.ts @@ -0,0 +1,127 @@ +// (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 { AddonModDataEntryField, AddonModDataField, AddonModDataSubfieldData } from '@addons/mod/data/services/data'; +import { Injectable, Type } from '@angular/core'; +import { FileEntry } from '@ionic-native/file'; +import { CoreFormFields } from '@singletons/form'; +import { CoreTextUtils } from '@services/utils/text'; +import { CoreWSExternalFile } from '@services/ws'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonModDataFieldTextHandlerService } from '../../text/services/handler'; +import { AddonModDataFieldTextareaComponent } from '../component/textarea'; + +/** + * Handler for textarea data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldTextareaHandlerService extends AddonModDataFieldTextHandlerService { + + name = 'AddonModDataFieldTextareaHandler'; + type = 'textarea'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldTextareaComponent; + } + + /** + * @inheritdoc + */ + getFieldEditData( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): AddonModDataSubfieldData[] { + const fieldName = 'f_' + field.id; + const files = this.getFieldEditFiles(field, inputData, originalFieldData); + + let text = CoreTextUtils.restorePluginfileUrls(inputData[fieldName] || '', files); + // Add some HTML to the text if needed. + text = CoreTextUtils.formatHtmlLines(text); + + // WS does not properly check if HTML content is blank when the field is required. + if (CoreTextUtils.htmlIsBlank(text)) { + text = ''; + } + + return [ + { + fieldid: field.id, + value: text, + }, + { + fieldid: field.id, + subfield: 'content1', + value: 1, + }, + { + fieldid: field.id, + subfield: 'itemid', + files: files, + }, + ]; + } + + /** + * @inheritdoc + */ + getFieldEditFiles( + field: AddonModDataField, + inputData: CoreFormFields, + originalFieldData: AddonModDataEntryField, + ): (CoreWSExternalFile | FileEntry)[] { + return (originalFieldData && originalFieldData.files) || []; + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (!field.required) { + return; + } + + if (!inputData || !inputData.length) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + const value = inputData.find((value) => value.subfield == ''); + + if (!value || CoreTextUtils.htmlIsBlank(value.value || '')) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + } + + /** + * @inheritdoc + */ + overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields): AddonModDataEntryField { + originalContent.content = offlineContent[''] || ''; + if (originalContent.content.length > 0 && originalContent.files && originalContent.files.length > 0) { + // Take the original files since we cannot edit them on the app. + originalContent.content = CoreTextUtils.replacePluginfileUrls( + originalContent.content, + originalContent.files, + ); + } + + return originalContent; + } + +} +export const AddonModDataFieldTextareaHandler = makeSingleton(AddonModDataFieldTextareaHandlerService); diff --git a/src/addons/mod/data/fields/textarea/textarea.module.ts b/src/addons/mod/data/fields/textarea/textarea.module.ts new file mode 100644 index 000000000..7ee21e9be --- /dev/null +++ b/src/addons/mod/data/fields/textarea/textarea.module.ts @@ -0,0 +1,44 @@ +// (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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { CoreEditorComponentsModule } from '@features/editor/components/components.module'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldTextareaComponent } from './component/textarea'; +import { AddonModDataFieldTextareaHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldTextareaComponent, + ], + imports: [ + CoreSharedModule, + CoreEditorComponentsModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldTextareaHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldTextareaComponent, + ], +}) +export class AddonModDataFieldTextareaModule {} diff --git a/src/addons/mod/data/fields/url/component/addon-mod-data-field-url.html b/src/addons/mod/data/fields/url/component/addon-mod-data-field-url.html new file mode 100644 index 000000000..059d0ceab --- /dev/null +++ b/src/addons/mod/data/fields/url/component/addon-mod-data-field-url.html @@ -0,0 +1,10 @@ + + + + + + + + {{ displayValue }} + {{ displayValue }} + diff --git a/src/addons/mod/data/fields/url/component/url.ts b/src/addons/mod/data/fields/url/component/url.ts new file mode 100644 index 000000000..0cde594db --- /dev/null +++ b/src/addons/mod/data/fields/url/component/url.ts @@ -0,0 +1,78 @@ +// (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 { AddonModDataEntryField } from '@addons/mod/data/services/data'; +import { Component } from '@angular/core'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data url field. + */ +@Component({ + selector: 'addon-mod-data-field-url', + templateUrl: 'addon-mod-data-field-url.html', +}) +export class AddonModDataFieldUrlComponent extends AddonModDataFieldPluginComponent { + + autoLink = false; + displayValue = ''; + + /** + * @inheritdoc + */ + protected init(): void { + if (this.displayMode) { + return; + } + + let value: string | undefined; + if (this.editMode && this.value) { + value = this.value.content; + } + + this.addControl('f_' + this.field.id, value); + } + + /** + * Calculate data for show or list mode. + */ + protected calculateShowListData(): void { + if (!this.value || !this.value.content) { + return; + } + + const url = this.value.content; + const text = this.field.param2 || this.value.content1; // Param2 forces the text to display. + + this.autoLink = parseInt(this.field.param1, 10) === 1; + + if (this.autoLink) { + this.displayValue = text || url; + } else { + // No auto link, always display the URL. + this.displayValue = url; + } + } + + /** + * @inheritdoc + */ + protected updateValue(value?: Partial): void { + super.updateValue(value); + + if (this.displayMode) { + this.calculateShowListData(); + } + } + +} diff --git a/src/addons/mod/data/fields/url/services/handler.ts b/src/addons/mod/data/fields/url/services/handler.ts new file mode 100644 index 000000000..77ecbca8d --- /dev/null +++ b/src/addons/mod/data/fields/url/services/handler.ts @@ -0,0 +1,63 @@ +// (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 { AddonModDataField, AddonModDataSubfieldData } from '@addons/mod/data/services/data'; +import { Injectable, Type } from '@angular/core'; +import { CoreFormFields } from '@singletons/form'; +import { Translate, makeSingleton } from '@singletons'; +import { AddonModDataFieldTextHandlerService } from '../../text/services/handler'; +import { AddonModDataFieldUrlComponent } from '../component/url'; + +/** + * Handler for url data field plugin. + */ +@Injectable({ providedIn: 'root' }) +export class AddonModDataFieldUrlHandlerService extends AddonModDataFieldTextHandlerService { + + name = 'AddonModDataFieldUrlHandler'; + type = 'url'; + + /** + * @inheritdoc + */ + getComponent(): Type{ + return AddonModDataFieldUrlComponent; + } + + /** + * @inheritdoc + */ + getFieldEditData(field: AddonModDataField, inputData: CoreFormFields): AddonModDataSubfieldData[] { + const fieldName = 'f_' + field.id; + + return [ + { + fieldid: field.id, + subfield: '0', + value: (inputData[fieldName] && inputData[fieldName].trim()) || '', + }, + ]; + } + + /** + * @inheritdoc + */ + getFieldsNotifications(field: AddonModDataField, inputData: AddonModDataSubfieldData[]): string | undefined { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return Translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + +} +export const AddonModDataFieldUrlHandler = makeSingleton(AddonModDataFieldUrlHandlerService); diff --git a/src/addons/mod/data/fields/url/url.module.ts b/src/addons/mod/data/fields/url/url.module.ts new file mode 100644 index 000000000..99d8250e5 --- /dev/null +++ b/src/addons/mod/data/fields/url/url.module.ts @@ -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 { CoreSharedModule } from '@/core/shared.module'; +import { NgModule, APP_INITIALIZER } from '@angular/core'; +import { AddonModDataFieldsDelegate } from '../../services/data-fields-delegate'; +import { AddonModDataFieldUrlComponent } from './component/url'; +import { AddonModDataFieldUrlHandler } from './services/handler'; + +@NgModule({ + declarations: [ + AddonModDataFieldUrlComponent, + ], + imports: [ + CoreSharedModule, + ], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + deps: [], + useFactory: () => () => { + AddonModDataFieldsDelegate.registerHandler(AddonModDataFieldUrlHandler.instance); + }, + }, + ], + exports: [ + AddonModDataFieldUrlComponent, + ], +}) +export class AddonModDataFieldUrlModule {}