MOBILE-3640 database: Add fields components

main
Pau Ferrer Ocaña 2021-03-31 09:24:48 +02:00
parent 2991873dfe
commit 8febbe3ea7
52 changed files with 3348 additions and 0 deletions

View File

@ -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<AddonModDataEntryField>; // 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<number>; // 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<AddonModDataEntryField>): 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;
}
}

View File

@ -0,0 +1,5 @@
<core-dynamic-component [component]="fieldComponent" [data]="pluginData">
<!-- This content will be replaced by the component if found. -->
<core-loading [hideUntil]="fieldLoaded">
</core-loading>
</core-dynamic-component>

View File

@ -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<unknown>; // Component to render the plugin.
pluginData?: AddonDataFieldPluginComponentData; // Data to pass to the component.
fieldLoaded = false;
/**
* Component being initialized.
*/
async ngOnInit(): Promise<void> {
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<unknown>;
};

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}

View File

@ -0,0 +1,16 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-select [formControlName]="'f_'+field.id" multiple="true" [placeholder]="'addon.mod_data.menuchoose' | translate"
[interfaceOptions]="{header: field.name}" interface="alert">
<ion-select-option *ngFor="let option of options" [value]="option.value">{{option.key}}</ion-select-option>
</ion-select>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<ion-item *ngIf="searchMode">
<ion-label>{{ 'addon.mod_data.selectedrequired' | translate }}</ion-label>
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_allreq'" [(ngModel)]="searchFields!['f_'+field.id+'_allreq']">
</ion-checkbox>
</ion-item>
</span>
<core-format-text *ngIf="displayMode && value && value.content" [text]="value.content" [filter]="false"></core-format-text>

View File

@ -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<AddonModDataEntryField>): void {
this.value = value || {};
this.value.content = value?.content?.split('##').join('<br>');
}
}

View File

@ -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<unknown> {
return AddonModDataFieldCheckboxComponent;
}
/**
* @inheritdoc
*/
getFieldSearchData(
field: AddonModDataField,
inputData: CoreFormFields<string[]>,
): 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<string[]>): AddonModDataSubfieldData[] {
const fieldName = 'f_' + field.id;
return [{
fieldid: field.id,
value: inputData[fieldName] || [],
}];
}
/**
* @inheritdoc
*/
hasFieldDataChanged(
field: AddonModDataField,
inputData: CoreFormFields<string[]>,
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<string[]>): AddonModDataEntryField {
originalContent.content = (offlineContent[''] && offlineContent[''].join('##')) || '';
return originalContent;
}
/**
* @inheritdoc
*/
async isEnabled(): Promise<boolean> {
return true;
}
}
export const AddonModDataFieldCheckboxHandler = makeSingleton(AddonModDataFieldCheckboxHandlerService);

View File

@ -0,0 +1,16 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-datetime [formControlName]="'f_'+field.id" [placeholder]="'core.date' | translate"
[disabled]="searchMode && !searchFields!['f_'+field.id+'_z']" [displayFormat]="format"></ion-datetime>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<ion-item *ngIf="searchMode">
<ion-label>{{ 'addon.mod_data.usedate' | translate }}</ion-label>
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_z'" [(ngModel)]="searchFields!['f_'+field.id+'_z']">
</ion-checkbox>
</ion-item>
</span>
<span *ngIf="displayMode && displayDate">
{{ displayDate | coreFormatDate:'strftimedate' }}
</span>

View File

@ -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()));
}
}

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}

View File

@ -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<unknown>{
return AddonModDataFieldDateComponent;
}
/**
* @inheritdoc
*/
getFieldSearchData(
field: AddonModDataField,
inputData: CoreFormFields<string>,
): 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<string>): 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<string>,
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<string>): 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<boolean> {
return true;
}
}
export const AddonModDataFieldDateHandler = makeSingleton(AddonModDataFieldDateHandlerService);

View File

@ -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 { }

View File

@ -0,0 +1,17 @@
<span *ngIf="editMode && form">
<span [core-mark-required]="field.required" class="core-mark-required"></span>
<core-attachments [files]="files" [maxSize]="maxSizeBytes" maxSubmissions="1" [component]="component"
[componentId]="componentId" [allowOffline]="true">
</core-attachments>
<core-input-errors *ngIf="error" [errorText]="error"></core-input-errors>
</span>
<span *ngIf="searchMode && form" [formGroup]="form">
<ion-input type="text" [formControlName]="'f_'+field.id" [placeholder]="field.name"></ion-input>
</span>
<ng-container *ngIf="displayMode">
<div lines="none">
<core-files [files]="files" [component]="component" [componentId]="componentId" [alwaysDownload]="true"></core-files>
</div>
</ng-container>

View File

@ -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<AddonModDataEntryField>): (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<AddonModDataEntryField>): void {
this.value = value;
this.files = this.getFiles(value);
}
}

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}

View File

@ -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<unknown>{
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 = <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<boolean> {
return true;
}
}
export const AddonModDataFieldFileHandler = makeSingleton(AddonModDataFieldFileHandlerService);

View File

@ -0,0 +1,27 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<ion-input *ngIf="searchMode" type="text" [placeholder]="field.name" [formControlName]="'f_'+field.id"></ion-input>
<ng-container *ngIf="editMode">
<span [core-mark-required]="field.required" class="core-mark-required"></span>
<div class="addon-data-latlong">
<ion-input type="text" [formControlName]="'f_'+field.id+'_0'" maxlength="10"></ion-input>
<span class="placeholder-icon" item-right>°N</span>
</div>
<div class="addon-data-latlong">
<ion-input type="text" [formControlName]="'f_'+field.id+'_1'" maxlength="10"></ion-input>
<span class="placeholder-icon" item-right>°E</span>
</div>
<div class="addon-data-latlong">
<ion-button (click)="getLocation($event)">
<ion-icon name="fas-crosshairs" slot="start"></ion-icon>
{{ 'addon.mod_data.mylocation' | translate }}
</ion-button>
</div>
<core-input-errors *ngIf="error" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
</ng-container>
</span>
<span *ngIf="displayMode && value">
<a [href]="getLatLongLink(north, east)">{{ formatLatLong(north, east) }}</a>
</span>

View File

@ -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<AddonModDataEntryField>): 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<void> {
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';
}
}
}

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}

View File

@ -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<unknown>{
return AddonModDataFieldLatlongComponent;
}
/**
* @inheritdoc
*/
getFieldSearchData(
field: AddonModDataField,
inputData: CoreFormFields<string>,
): AddonModDataSearchEntriesAdvancedFieldFormatted[] {
const fieldName = 'f_' + field.id;
if (inputData[fieldName]) {
return [{
name: fieldName,
value: inputData[fieldName],
}];
}
return [];
}
/**
* @inheritdoc
*/
getFieldEditData(field: AddonModDataField, inputData: CoreFormFields<string>): 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<string>,
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<string>): AddonModDataEntryField {
originalContent.content = offlineContent[0] || '';
originalContent.content1 = offlineContent[1] || '';
return originalContent;
}
/**
* @inheritdoc
*/
async isEnabled(): Promise<boolean> {
return true;
}
}
export const AddonModDataFieldLatlongHandler = makeSingleton(AddonModDataFieldLatlongHandlerService);

View File

@ -0,0 +1,11 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-select [formControlName]="'f_'+field.id" [placeholder]="'addon.mod_data.menuchoose' | translate"
[interfaceOptions]="{header: field.name}" interface="action-sheet">
<ion-select-option value="">{{ 'addon.mod_data.menuchoose' | translate }}</ion-select-option>
<ion-select-option *ngFor="let option of options" [value]="option">{{option}}</ion-select-option>
</ion-select>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
</span>
<span *ngIf="displayMode && value && value.content">{{ value.content }}</span>

View File

@ -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);
}
}

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}

View File

@ -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<unknown>{
return AddonModDataFieldMenuComponent;
}
/**
* @inheritdoc
*/
getFieldSearchData(
field: AddonModDataField,
inputData: CoreFormFields<string>,
): AddonModDataSearchEntriesAdvancedFieldFormatted[] {
const fieldName = 'f_' + field.id;
if (inputData[fieldName]) {
return [{
name: fieldName,
value: inputData[fieldName],
}];
}
return [];
}
/**
* @inheritdoc
*/
getFieldEditData(field: AddonModDataField, inputData: CoreFormFields<string>): AddonModDataSubfieldData[] {
const fieldName = 'f_' + field.id;
if (inputData[fieldName]) {
return [{
fieldid: field.id,
value: inputData[fieldName],
}];
}
return [];
}
hasFieldDataChanged(
field: AddonModDataField,
inputData: CoreFormFields<string>,
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<string>): AddonModDataEntryField {
originalContent.content = offlineContent[''] || '';
return originalContent;
}
/**
* @inheritdoc
*/
async isEnabled(): Promise<boolean> {
return true;
}
}
export const AddonModDataFieldMenuHandler = makeSingleton(AddonModDataFieldMenuHandlerService);

View File

@ -0,0 +1,17 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-select [formControlName]="'f_'+field.id" multiple="true" [placeholder]="'addon.mod_data.menuchoose' | translate"
[interfaceOptions]="{header: field.name}" interface="alert">
<ion-select-option *ngFor="let option of options" [value]="option.value">{{option.key}}</ion-select-option>
</ion-select>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
<ion-item *ngIf="searchMode">
<ion-label>{{ 'addon.mod_data.selectedrequired' | translate }}</ion-label>
<ion-checkbox slot="end" [formControlName]="'f_'+field.id+'_allreq'" [(ngModel)]="searchFields!['f_'+field.id+'_allreq']">
</ion-checkbox>
</ion-item>
</span>
<core-format-text *ngIf="displayMode && value && value.content" [text]="value.content" [filter]="false"></core-format-text>

View File

@ -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<AddonModDataEntryField>): void {
this.value = value || {};
this.value.content = value?.content && value.content.split('##').join('<br>');
}
}

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}

View File

@ -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<unknown>{
return AddonModDataFieldMultimenuComponent;
}
/**
* @inheritdoc
*/
getFieldSearchData(
field: AddonModDataField,
inputData: CoreFormFields<string[]>,
): 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<string[]>): AddonModDataSubfieldData[] {
const fieldName = 'f_' + field.id;
return [{
fieldid: field.id,
value: inputData[fieldName] || [],
}];
}
/**
* @inheritdoc
*/
hasFieldDataChanged(
field: AddonModDataField,
inputData: CoreFormFields<string[]>,
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<string[]>): AddonModDataEntryField {
originalContent.content = (offlineContent[''] && offlineContent[''].join('##')) || '';
return originalContent;
}
/**
* @inheritdoc
*/
async isEnabled(): Promise<boolean> {
return true;
}
}
export const AddonModDataFieldMultimenuHandler = makeSingleton(AddonModDataFieldMultimenuHandlerService);

View File

@ -0,0 +1,7 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-input type="number" [formControlName]="'f_'+field.id" [placeholder]="field.name"></ion-input>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
</span>
<span *ngIf="displayMode && value && value.content">{{ value.content }}</span>

View File

@ -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);
}
}

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}

View File

@ -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<unknown>{
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);

View File

@ -0,0 +1,22 @@
<span *ngIf="editMode && form" [formGroup]="form">
<span [core-mark-required]="field.required" class="core-mark-required"></span>
<core-attachments [files]="files" [maxSize]="maxSizeBytes" maxSubmissions="1" [component]="component"
[componentId]="componentId" [allowOffline]="true" acceptedTypes="image">
</core-attachments>
<core-input-errors *ngIf="error" [errorText]="error"></core-input-errors>
<ion-label position="stacked">{{ 'addon.mod_data.alttext' | translate }}</ion-label>
<ion-input type="text" [formControlName]="'f_'+field.id+'_alttext'" [placeholder]=" 'addon.mod_data.alttext' | translate" >
</ion-input>
</span>
<span *ngIf="searchMode && form" [formGroup]="form">
<ion-input type="text" [formControlName]="'f_'+field.id" [placeholder]="field.name"></ion-input>
</span>
<span *ngIf="listMode && imageUrl" (click)="gotoEntry.emit(entryId)">
<img [src]="imageUrl" [alt]="title" class="core-media-adapt-width listMode_picture" core-external-content/>
</span>
<img *ngIf="showMode && imageUrl" [src]="imageUrl" [alt]="title" class="core-media-adapt-width listMode_picture"
[attr.width]="width" [attr.height]="height" core-external-content/>

View File

@ -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<AddonModDataEntryField>): (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<AddonModDataEntryField>): 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);
}
}
}

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}

View File

@ -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<unknown>{
return AddonModDataFieldPictureComponent;
}
/**
* @inheritdoc
*/
getFieldSearchData(
field: AddonModDataField,
inputData: CoreFormFields<string>,
): AddonModDataSearchEntriesAdvancedFieldFormatted[] {
const fieldName = 'f_' + field.id;
if (inputData[fieldName]) {
return [{
name: fieldName,
value: inputData[fieldName],
}];
}
return [];
}
/**
* @inheritdoc
*/
getFieldEditData(field: AddonModDataField, inputData: CoreFormFields<string>): 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<string>,
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 = <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 = <string>offlineContent.alttext || '';
return originalContent;
}
/**
* @inheritdoc
*/
async isEnabled(): Promise<boolean> {
return true;
}
}
export const AddonModDataFieldPictureHandler = makeSingleton(AddonModDataFieldPictureHandlerService);

View File

@ -0,0 +1,11 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-select [formControlName]="'f_'+field.id" [placeholder]="'addon.mod_data.menuchoose' | translate"
[interfaceOptions]="{header: field.name}" interface="alert">
<ion-select-option value="">{{ 'addon.mod_data.menuchoose' | translate }}</ion-select-option>
<ion-select-option *ngFor="let option of options" [value]="option">{{option}}</ion-select-option>
</ion-select>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
</span>
<span *ngIf="displayMode && value && value.content">{{ value.content }}</span>

View File

@ -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);
}
}

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}

View File

@ -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<unknown>{
return AddonModDataFieldRadiobuttonComponent;
}
/**
* @inheritdoc
*/
getFieldSearchData(
field: AddonModDataField,
inputData: CoreFormFields<string>,
): AddonModDataSearchEntriesAdvancedFieldFormatted[] {
const fieldName = 'f_' + field.id;
if (inputData[fieldName]) {
return [{
name: fieldName,
value: inputData[fieldName],
}];
}
return [];
}
/**
* @inheritdoc
*/
getFieldEditData(field: AddonModDataField, inputData: CoreFormFields<string>): AddonModDataSubfieldData[] {
const fieldName = 'f_' + field.id;
return [{
fieldid: field.id,
value: inputData[fieldName] || '',
}];
}
/**
* @inheritdoc
*/
hasFieldDataChanged(
field: AddonModDataField,
inputData: CoreFormFields<string>,
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<string>): AddonModDataEntryField {
originalContent.content = offlineContent[''] || '';
return originalContent;
}
/**
* @inheritdoc
*/
async isEnabled(): Promise<boolean> {
return true;
}
}
export const AddonModDataFieldRadiobuttonHandler = makeSingleton(AddonModDataFieldRadiobuttonHandlerService);

View File

@ -0,0 +1,7 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-input type="text" [formControlName]="'f_'+field.id" [placeholder]="field.name"></ion-input>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
</span>
<span *ngIf="displayMode && value && value.content">{{ value.content }}</span>

View File

@ -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);
}
}

View File

@ -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<unknown>{
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<string>,
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<string>,
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<string>): AddonModDataEntryField {
originalContent.content = offlineContent[''] || '';
return originalContent;
}
/**
* @inheritdoc
*/
async isEnabled(): Promise<boolean> {
return true;
}
}
export const AddonModDataFieldTextHandler = makeSingleton(AddonModDataFieldTextHandlerService);

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}

View File

@ -0,0 +1,14 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<ion-input *ngIf="searchMode" type="text" [placeholder]="field.name" [formControlName]="'f_'+field.id"></ion-input>
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<core-rich-text-editor *ngIf="editMode" item-content [control]="form.controls['f_'+field.id]" [placeholder]="field.name"
[formControlName]="'f_'+field.id" [component]="component" [componentId]="componentId" [autoSave]="true"
contextLevel="module" [contextInstanceId]="componentId" [elementId]="'field_'+field.id" ngDefaultControl>
</core-rich-text-editor>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
</span>
<core-format-text *ngIf="displayMode && value" [text]="format(value)" [component]="component" [componentId]="componentId"
contextLevel="module" [contextInstanceId]="componentId" [courseId]="database!.course">
</core-format-text>

View File

@ -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<AddonModDataEntryField>): string {
const files: CoreWSExternalFile[] = (value && <CoreWSExternalFile[]>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);
}
}

View File

@ -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<unknown>{
return AddonModDataFieldTextareaComponent;
}
/**
* @inheritdoc
*/
getFieldEditData(
field: AddonModDataField,
inputData: CoreFormFields<string>,
originalFieldData: AddonModDataEntryField,
): AddonModDataSubfieldData[] {
const fieldName = 'f_' + field.id;
const files = this.getFieldEditFiles(field, inputData, originalFieldData);
let text = CoreTextUtils.restorePluginfileUrls(inputData[fieldName] || '', <CoreWSExternalFile[]>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(<string>value.value || '')) {
return Translate.instant('addon.mod_data.errormustsupplyvalue');
}
}
/**
* @inheritdoc
*/
overrideData(originalContent: AddonModDataEntryField, offlineContent: CoreFormFields<string>): 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,
<CoreWSExternalFile[]>originalContent.files,
);
}
return originalContent;
}
}
export const AddonModDataFieldTextareaHandler = makeSingleton(AddonModDataFieldTextareaHandlerService);

View File

@ -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 {}

View File

@ -0,0 +1,10 @@
<span *ngIf="inputMode && form" [formGroup]="form">
<span *ngIf="editMode" [core-mark-required]="field.required" class="core-mark-required"></span>
<ion-input type="url" [formControlName]="'f_'+field.id" [placeholder]="field.name"></ion-input>
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error"></core-input-errors>
</span>
<ng-container *ngIf="displayMode && value && value.content">
<a *ngIf="autoLink" [href]="value.content" core-link capture="true">{{ displayValue }}</a>
<span *ngIf="!autoLink">{{ displayValue }}</span>
</ng-container>

View File

@ -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<AddonModDataEntryField>): void {
super.updateValue(value);
if (this.displayMode) {
this.calculateShowListData();
}
}
}

View File

@ -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<unknown>{
return AddonModDataFieldUrlComponent;
}
/**
* @inheritdoc
*/
getFieldEditData(field: AddonModDataField, inputData: CoreFormFields<string>): 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);

View File

@ -0,0 +1,42 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { 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 {}