From 661dbe22605411512d88902f22fc4f2fbff5e37d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Wed, 2 May 2018 10:44:28 +0200 Subject: [PATCH] MOBILE-2338 data: Add field plugins --- config.xml | 4 + .../components/field-plugin/field-plugin.ts | 5 +- .../mod/data/components/index/index.html | 2 +- .../mod/data/components/index/index.scss | 25 +++ src/addon/mod/data/components/index/index.ts | 26 ++- .../fields/checkbox/component/checkbox.ts | 3 +- .../data/fields/checkbox/providers/handler.ts | 1 - .../mod/data/fields/date/component/date.html | 12 ++ .../mod/data/fields/date/component/date.ts | 63 ++++++ src/addon/mod/data/fields/date/date.module.ts | 51 +++++ .../mod/data/fields/date/providers/handler.ts | 180 ++++++++++++++++ src/addon/mod/data/fields/field.module.ts | 24 ++- .../mod/data/fields/file/component/file.html | 14 ++ .../mod/data/fields/file/component/file.ts | 81 ++++++++ src/addon/mod/data/fields/file/file.module.ts | 49 +++++ .../mod/data/fields/file/providers/handler.ts | 158 ++++++++++++++ .../fields/latlong/component/latlong.html | 19 ++ .../data/fields/latlong/component/latlong.ts | 90 ++++++++ .../mod/data/fields/latlong/latlong.module.ts | 49 +++++ .../data/fields/latlong/providers/handler.ts | 159 ++++++++++++++ .../mod/data/fields/menu/component/menu.html | 9 + .../mod/data/fields/menu/component/menu.ts | 57 +++++ src/addon/mod/data/fields/menu/menu.module.ts | 49 +++++ .../mod/data/fields/menu/providers/handler.ts | 134 ++++++++++++ .../fields/multimenu/component/multimenu.html | 8 + .../fields/multimenu/component/multimenu.ts | 63 ++++++ .../data/fields/multimenu/multimenu.module.ts | 49 +++++ .../fields/multimenu/providers/handler.ts | 152 ++++++++++++++ .../data/fields/number/component/number.html | 7 + .../data/fields/number/component/number.ts | 54 +++++ .../mod/data/fields/number/number.module.ts | 49 +++++ .../data/fields/number/providers/handler.ts | 57 +++++ .../fields/picture/component/picture.html | 17 ++ .../data/fields/picture/component/picture.ts | 128 ++++++++++++ .../mod/data/fields/picture/picture.module.ts | 49 +++++ .../data/fields/picture/providers/handler.ts | 194 ++++++++++++++++++ .../radiobutton/component/radiobutton.html | 9 + .../radiobutton/component/radiobutton.ts | 57 +++++ .../fields/radiobutton/providers/handler.ts | 133 ++++++++++++ .../fields/radiobutton/radiobutton.module.ts | 49 +++++ .../mod/data/fields/text/component/text.html | 7 + .../mod/data/fields/text/component/text.ts | 54 +++++ .../mod/data/fields/text/providers/handler.ts | 134 ++++++++++++ src/addon/mod/data/fields/text/text.module.ts | 49 +++++ .../fields/textarea/component/textarea.html | 10 + .../fields/textarea/component/textarea.ts | 69 +++++++ .../data/fields/textarea/providers/handler.ts | 145 +++++++++++++ .../data/fields/textarea/textarea.module.ts | 49 +++++ .../mod/data/fields/url/component/url.html | 7 + .../mod/data/fields/url/component/url.ts | 54 +++++ .../mod/data/fields/url/providers/handler.ts | 57 +++++ src/addon/mod/data/fields/url/url.module.ts | 49 +++++ src/addon/mod/data/providers/helper.ts | 10 +- src/addon/mod/feedback/providers/helper.ts | 5 +- .../components/compile-html/compile-html.ts | 7 +- src/core/compile/providers/compile.ts | 12 +- 56 files changed, 3028 insertions(+), 29 deletions(-) create mode 100644 src/addon/mod/data/fields/date/component/date.html create mode 100644 src/addon/mod/data/fields/date/component/date.ts create mode 100644 src/addon/mod/data/fields/date/date.module.ts create mode 100644 src/addon/mod/data/fields/date/providers/handler.ts create mode 100644 src/addon/mod/data/fields/file/component/file.html create mode 100644 src/addon/mod/data/fields/file/component/file.ts create mode 100644 src/addon/mod/data/fields/file/file.module.ts create mode 100644 src/addon/mod/data/fields/file/providers/handler.ts create mode 100644 src/addon/mod/data/fields/latlong/component/latlong.html create mode 100644 src/addon/mod/data/fields/latlong/component/latlong.ts create mode 100644 src/addon/mod/data/fields/latlong/latlong.module.ts create mode 100644 src/addon/mod/data/fields/latlong/providers/handler.ts create mode 100644 src/addon/mod/data/fields/menu/component/menu.html create mode 100644 src/addon/mod/data/fields/menu/component/menu.ts create mode 100644 src/addon/mod/data/fields/menu/menu.module.ts create mode 100644 src/addon/mod/data/fields/menu/providers/handler.ts create mode 100644 src/addon/mod/data/fields/multimenu/component/multimenu.html create mode 100644 src/addon/mod/data/fields/multimenu/component/multimenu.ts create mode 100644 src/addon/mod/data/fields/multimenu/multimenu.module.ts create mode 100644 src/addon/mod/data/fields/multimenu/providers/handler.ts create mode 100644 src/addon/mod/data/fields/number/component/number.html create mode 100644 src/addon/mod/data/fields/number/component/number.ts create mode 100644 src/addon/mod/data/fields/number/number.module.ts create mode 100644 src/addon/mod/data/fields/number/providers/handler.ts create mode 100644 src/addon/mod/data/fields/picture/component/picture.html create mode 100644 src/addon/mod/data/fields/picture/component/picture.ts create mode 100644 src/addon/mod/data/fields/picture/picture.module.ts create mode 100644 src/addon/mod/data/fields/picture/providers/handler.ts create mode 100644 src/addon/mod/data/fields/radiobutton/component/radiobutton.html create mode 100644 src/addon/mod/data/fields/radiobutton/component/radiobutton.ts create mode 100644 src/addon/mod/data/fields/radiobutton/providers/handler.ts create mode 100644 src/addon/mod/data/fields/radiobutton/radiobutton.module.ts create mode 100644 src/addon/mod/data/fields/text/component/text.html create mode 100644 src/addon/mod/data/fields/text/component/text.ts create mode 100644 src/addon/mod/data/fields/text/providers/handler.ts create mode 100644 src/addon/mod/data/fields/text/text.module.ts create mode 100644 src/addon/mod/data/fields/textarea/component/textarea.html create mode 100644 src/addon/mod/data/fields/textarea/component/textarea.ts create mode 100644 src/addon/mod/data/fields/textarea/providers/handler.ts create mode 100644 src/addon/mod/data/fields/textarea/textarea.module.ts create mode 100644 src/addon/mod/data/fields/url/component/url.html create mode 100644 src/addon/mod/data/fields/url/component/url.ts create mode 100644 src/addon/mod/data/fields/url/providers/handler.ts create mode 100644 src/addon/mod/data/fields/url/url.module.ts diff --git a/config.xml b/config.xml index 5b9064b2c..8d93693b2 100644 --- a/config.xml +++ b/config.xml @@ -13,6 +13,10 @@ + + + + diff --git a/src/addon/mod/data/components/field-plugin/field-plugin.ts b/src/addon/mod/data/components/field-plugin/field-plugin.ts index 819d43e2d..cbac94fa2 100644 --- a/src/addon/mod/data/components/field-plugin/field-plugin.ts +++ b/src/addon/mod/data/components/field-plugin/field-plugin.ts @@ -38,13 +38,13 @@ export class AddonModDataFieldPluginComponent implements OnInit { fieldLoaded: boolean; constructor(protected injector: Injector, protected dataDelegate: AddonModDataFieldsDelegate, - protected dataProvider: AddonModDataProvider) { } + protected dataProvider: AddonModDataProvider) { + } /** * Component being initialized. */ ngOnInit(): void { - console.error('HERE'); if (!this.field) { this.fieldLoaded = true; @@ -65,6 +65,7 @@ export class AddonModDataFieldPluginComponent implements OnInit { error: this.error, viewAction: this.viewAction }; + } else { this.fieldLoaded = true; } diff --git a/src/addon/mod/data/components/index/index.html b/src/addon/mod/data/components/index/index.html index e53c61d7a..2fe8fe250 100644 --- a/src/addon/mod/data/components/index/index.html +++ b/src/addon/mod/data/components/index/index.html @@ -63,7 +63,7 @@ {{ cssTemplate }} - + diff --git a/src/addon/mod/data/components/index/index.scss b/src/addon/mod/data/components/index/index.scss index 1a748d124..2c2a8e5a3 100644 --- a/src/addon/mod/data/components/index/index.scss +++ b/src/addon/mod/data/components/index/index.scss @@ -1,3 +1,28 @@ addon-mod-data-index { + .core-data-contents { + overflow: visible; + white-space: normal; + word-break: break-word; + padding: $content-padding; + background-color: white; + border-top-width: 1px; + border-bottom-width: 1px; + border-right-width: 0; + border-left-width: 0; + border-style: solid; + border-color: $list-border-color; + table, tbody { + display: block; + } + + tr { + @extend .row; + padding: 0; + } + + td, th { + @extend .col; + } + } } \ No newline at end of file diff --git a/src/addon/mod/data/components/index/index.ts b/src/addon/mod/data/components/index/index.ts index 713731112..e2517fc62 100644 --- a/src/addon/mod/data/components/index/index.ts +++ b/src/addon/mod/data/components/index/index.ts @@ -23,6 +23,7 @@ import { AddonModDataProvider } from '../../providers/data'; import { AddonModDataHelperProvider } from '../../providers/helper'; import { AddonModDataOfflineProvider } from '../../providers/offline'; import { AddonModDataSyncProvider } from '../../providers/sync'; +import { AddonModDataComponentsModule } from '../components.module'; import * as moment from 'moment'; /** @@ -33,6 +34,7 @@ import * as moment from 'moment'; templateUrl: 'index.html', }) export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComponent { + component = AddonModDataProvider.COMPONENT; moduleName = 'data'; @@ -65,13 +67,16 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp offlineEntries: any; entriesRendered = ''; cssTemplate = ''; + extraImports = [AddonModDataComponentsModule]; + jsData; protected syncEventName = AddonModDataSyncProvider.AUTO_SYNCED; protected entryChangedObserver: any; protected hasComments = false; + protected fieldsArray: any; constructor(injector: Injector, private dataProvider: AddonModDataProvider, private dataHelper: AddonModDataHelperProvider, - private dataOffline: AddonModDataOfflineProvider, @Optional() private content: Content, + private dataOffline: AddonModDataOfflineProvider, @Optional() @Optional() content: Content, private dataSync: AddonModDataSyncProvider, private timeUtils: CoreTimeUtilsProvider, private groupsProvider: CoreGroupsProvider, private commentsProvider: CoreCommentsProvider, private modalCtrl: ModalController, private utils: CoreUtilsProvider) { @@ -216,8 +221,8 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp fields.forEach((field) => { this.fields[field.id] = field; }); - this.fields = this.utils.objectToArray(this.fields); - this.advancedSearch = this.dataHelper.displayAdvancedSearchFields(this.data.asearchtemplate, this.fields); + this.fieldsArray = this.utils.objectToArray(this.fields); + this.advancedSearch = this.dataHelper.displayAdvancedSearchFields(this.data.asearchtemplate, this.fieldsArray); return this.fetchEntriesData(); }); @@ -283,7 +288,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp }; if (offlineActions.length > 0) { - promises.push(this.dataHelper.applyOfflineActions(entry, offlineActions, this.fields)); + promises.push(this.dataHelper.applyOfflineActions(entry, offlineActions, this.fieldsArray)); } else { promises.push(Promise.resolve(entry)); } @@ -299,7 +304,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp entry.contents = contents; if (typeof this.offlineActions[entry.id] != 'undefined') { - promises.push(this.dataHelper.applyOfflineActions(entry, this.offlineActions[entry.id], this.fields)); + promises.push(this.dataHelper.applyOfflineActions(entry, this.offlineActions[entry.id], this.fieldsArray)); } else { promises.push(Promise.resolve(entry)); } @@ -318,12 +323,19 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp const actions = this.dataHelper.getActions(this.data, this.access, entry); - entriesHTML += this.dataHelper.displayShowFields(this.data.listtemplate, this.fields, entry, 'list', + entriesHTML += this.dataHelper.displayShowFields(this.data.listtemplate, this.fieldsArray, entry, 'list', actions); }); entriesHTML += this.data.listtemplatefooter || ''; this.entriesRendered = entriesHTML; + + // Pass the input data to the component. + this.jsData = { + fields: this.fields, + entries: this.entries, + data: this.data + }; }); } else if (!this.search.searching) { // Empty and no searching. @@ -356,7 +368,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp if (this.search.searchingAdvanced) { this.search.advanced = this.dataHelper.getSearchDataFromForm(document.forms['addon-mod_data-advanced-search-form'], - this.fields); + this.fieldsArray); this.search.searching = this.search.advanced.length > 0; } else { this.search.searching = this.search.text.length > 0; diff --git a/src/addon/mod/data/fields/checkbox/component/checkbox.ts b/src/addon/mod/data/fields/checkbox/component/checkbox.ts index 64f4db6e2..48404b1a8 100644 --- a/src/addon/mod/data/fields/checkbox/component/checkbox.ts +++ b/src/addon/mod/data/fields/checkbox/component/checkbox.ts @@ -32,7 +32,6 @@ export class AddonModDataFieldCheckboxComponent extends AddonModDataFieldPluginC constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, element: ElementRef) { - super(); } @@ -46,7 +45,7 @@ export class AddonModDataFieldCheckboxComponent extends AddonModDataFieldPluginC protected render(): void { if (this.mode == 'show') { - this.value.content.split('##').join('
'); + this.value.content = this.value.content.split('##').join('
'); return; } diff --git a/src/addon/mod/data/fields/checkbox/providers/handler.ts b/src/addon/mod/data/fields/checkbox/providers/handler.ts index d49a0a3c0..14632ab59 100644 --- a/src/addon/mod/data/fields/checkbox/providers/handler.ts +++ b/src/addon/mod/data/fields/checkbox/providers/handler.ts @@ -35,7 +35,6 @@ export class AddonModDataFieldCheckboxHandler implements AddonModDataFieldHandle * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. */ getComponent(injector: Injector, plugin: any): any | Promise { - console.error(AddonModDataFieldCheckboxComponent); return AddonModDataFieldCheckboxComponent; } diff --git a/src/addon/mod/data/fields/date/component/date.html b/src/addon/mod/data/fields/date/component/date.html new file mode 100644 index 000000000..66a9c8de0 --- /dev/null +++ b/src/addon/mod/data/fields/date/component/date.html @@ -0,0 +1,12 @@ + + + + + + {{ 'addon.mod_data.usedate' | translate }} + + + + + + diff --git a/src/addon/mod/data/fields/date/component/date.ts b/src/addon/mod/data/fields/date/component/date.ts new file mode 100644 index 000000000..034669864 --- /dev/null +++ b/src/addon/mod/data/fields/date/component/date.ts @@ -0,0 +1,63 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data date field. + */ +@Component({ + selector: 'addon-mod-data-field-date', + templateUrl: 'date.html' +}) +export class AddonModDataFieldDateComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + values = {}; + enable: boolean; + val: any; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef) { + super(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.mode = this.mode == 'list' ? 'show' : this.mode; + this.render(); + } + + protected render(): void { + if (this.mode == 'show') { + return; + } + + if (this.mode == 'edit' && this.value) { + this.enable = true; + } else { + this.value = { + content: Math.floor(Date.now() / 1000) + }; + this.enable = false; + } + + this.val = new Date(this.value.content * 1000); + } +} diff --git a/src/addon/mod/data/fields/date/date.module.ts b/src/addon/mod/data/fields/date/date.module.ts new file mode 100644 index 000000000..faf696cf5 --- /dev/null +++ b/src/addon/mod/data/fields/date/date.module.ts @@ -0,0 +1,51 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldDateHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldDateComponent } from './component/date'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldDateComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule, + CorePipesModule + ], + providers: [ + AddonModDataFieldDateHandler + ], + exports: [ + AddonModDataFieldDateComponent + ], + entryComponents: [ + AddonModDataFieldDateComponent + ] +}) +export class AddonModDataFieldDateModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldDateHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/fields/date/providers/handler.ts b/src/addon/mod/data/fields/date/providers/handler.ts new file mode 100644 index 000000000..9bb37150b --- /dev/null +++ b/src/addon/mod/data/fields/date/providers/handler.ts @@ -0,0 +1,180 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; +import { AddonModDataFieldDateComponent } from '../component/date'; + +/** + * Handler for date data field plugin. + */ +@Injectable() +export class AddonModDataFieldDateHandler implements AddonModDataFieldHandler { + name = 'AddonModDataFieldDateHandler'; + type = 'date'; + + constructor(private translate: TranslateService) { } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldDateComponent; + } + + /** + * Get field search data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the search form. + * @return {any} With name and value of the data to be sent. + */ + getFieldSearchData(field: any, inputData: any): any { + const fieldName = 'f_' + field.id, + enabledName = 'f_' + field.id + '_z'; + + if (inputData[enabledName]['1']) { + const values = [], + date = inputData[fieldName].split('-'), + year = date[0], + month = date[1], + day = date[2]; + values.push({ + name: fieldName + '_y', + value: year + }); + values.push({ + name: fieldName + '_m', + value: month + }); + values.push({ + name: fieldName + '_d', + value: day + }); + values.push({ + name: enabledName, + value: 1 + }); + + return values; + } + + return false; + } + + /** + * Get field edit data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditData(field: any, inputData: any, originalFieldData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + const values = [], + date = inputData[fieldName].split('-'), + year = date[0], + month = date[1], + day = date[2]; + values.push({ + fieldid: field.id, + subfield: 'year', + value: year + }); + values.push({ + fieldid: field.id, + subfield: 'month', + value: month + }); + values.push({ + fieldid: field.id, + subfield: 'day', + value: day + }); + + return values; + } + + return false; + } + + /** + * Get field data in changed. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @param {any} originalFieldData Original field entered data. + * @return {Promise | boolean} If the field has changes. + */ + hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { + const fieldName = 'f_' + field.id, + input = inputData[fieldName] || ''; + + originalFieldData = (originalFieldData && originalFieldData.content && + new Date(originalFieldData.content * 1000).toISOString().substr(0, 10)) || ''; + + return input != originalFieldData; + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + if (field.required && + (!inputData || inputData.length < 2 || !inputData[0].value || !inputData[1].value || !inputData[2].value)) { + + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + return false; + } + + /** + * Override field content data with offline submission. + * + * @param {any} originalContent Original data to be overriden. + * @param {any} offlineContent Array with all the offline data to override. + * @param {any} [offlineFiles] Array with all the offline files in the field. + * @return {any} Data overriden + */ + overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { + let date = Date.UTC(offlineContent['year'] || '', offlineContent['month'] ? offlineContent['month'] - 1 : null, + offlineContent['day'] || null); + date = Math.floor(date / 1000); + + originalContent.content = date || ''; + + return originalContent; + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/addon/mod/data/fields/field.module.ts b/src/addon/mod/data/fields/field.module.ts index c38ed2f56..3bd3d9e3d 100644 --- a/src/addon/mod/data/fields/field.module.ts +++ b/src/addon/mod/data/fields/field.module.ts @@ -14,11 +14,33 @@ 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({ declarations: [], imports: [ - AddonModDataFieldCheckboxModule + AddonModDataFieldCheckboxModule, + AddonModDataFieldDateModule, + AddonModDataFieldFileModule, + AddonModDataFieldLatlongModule, + AddonModDataFieldMenuModule, + AddonModDataFieldMultimenuModule, + AddonModDataFieldNumberModule, + AddonModDataFieldPictureModule, + AddonModDataFieldRadiobuttonModule, + AddonModDataFieldTextModule, + AddonModDataFieldTextareaModule, + AddonModDataFieldUrlModule ], providers: [ ], diff --git a/src/addon/mod/data/fields/file/component/file.html b/src/addon/mod/data/fields/file/component/file.html new file mode 100644 index 000000000..1ed18a0f3 --- /dev/null +++ b/src/addon/mod/data/fields/file/component/file.html @@ -0,0 +1,14 @@ + + + + + + +
+ + + + + +
+
diff --git a/src/addon/mod/data/fields/file/component/file.ts b/src/addon/mod/data/fields/file/component/file.ts new file mode 100644 index 000000000..773a3b7ed --- /dev/null +++ b/src/addon/mod/data/fields/file/component/file.ts @@ -0,0 +1,81 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; +import { CoreFileSessionProvider } from '@providers/file-session'; +import { AddonModDataProvider } from '../../../providers/data'; + +/** + * Component to render data file field. + */ +@Component({ + selector: 'addon-mod-data-field-file', + templateUrl: 'file.html' +}) +export class AddonModDataFieldFileComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + files = []; + component: string; + componentId: number; + maxSizeBytes: number; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef, private fileSessionprovider: CoreFileSessionProvider) { + super(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.mode = this.mode == 'list' ? 'show' : this.mode; + this.render(); + } + + /** + * Get the files from the input value. + * + * @param {any} value Input value. + * @return {any} List of files. + */ + protected getFiles(value: any): any { + let files = (value && value.files) || []; + + // Reduce to first element. + if (files.length > 0) { + files = [files[0]]; + } + + return files; + } + + protected render(): void { + if (this.mode == 'show' || this.mode == 'edit') { + this.component = AddonModDataProvider.COMPONENT; + this.componentId = this.database.coursemodule; + + this.files = this.getFiles(this.value); + + if (this.mode != 'show') { + // Edit mode, the list shouldn't change so there is no need to watch it. + this.maxSizeBytes = parseInt(this.field.param3, 10); + this.fileSessionprovider.setFiles(this.component, this.database.id + '_' + this.field.id, this.files); + } + } + } +} diff --git a/src/addon/mod/data/fields/file/file.module.ts b/src/addon/mod/data/fields/file/file.module.ts new file mode 100644 index 000000000..b2d240329 --- /dev/null +++ b/src/addon/mod/data/fields/file/file.module.ts @@ -0,0 +1,49 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldFileHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldFileComponent } from './component/file'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldFileComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule, + ], + providers: [ + AddonModDataFieldFileHandler + ], + exports: [ + AddonModDataFieldFileComponent + ], + entryComponents: [ + AddonModDataFieldFileComponent + ] +}) +export class AddonModDataFieldFileModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldFileHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/fields/file/providers/handler.ts b/src/addon/mod/data/fields/file/providers/handler.ts new file mode 100644 index 000000000..38a58e7de --- /dev/null +++ b/src/addon/mod/data/fields/file/providers/handler.ts @@ -0,0 +1,158 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreFileSessionProvider } from '@providers/file-session'; +import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; +import { AddonModDataProvider } from '../../../providers/data'; +import { AddonModDataFieldFileComponent } from '../component/file'; +import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; + +/** + * Handler for file data field plugin. + */ +@Injectable() +export class AddonModDataFieldFileHandler implements AddonModDataFieldHandler { + name = 'AddonModDataFieldFileHandler'; + type = 'file'; + + constructor(private translate: TranslateService, private fileSessionprovider: CoreFileSessionProvider, + private fileUploaderProvider: CoreFileUploaderProvider) { } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldFileComponent; + } + + /** + * Get field search data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the search form. + * @return {any} With name and value of the data to be sent. + */ + getFieldSearchData(field: any, inputData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName] + }]; + } + + return false; + } + + /** + * Get field edit data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditData(field: any, inputData: any, originalFieldData: any): any { + const files = this.getFieldEditFiles(field); + + if (files.length) { + return [{ + fieldid: field.id, + subfield: 'file', + files: files + }]; + } + + return false; + } + + /** + * Get field edit files in the input data. + * + * @param {any} field Defines the field.. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditFiles(field: any): any { + return this.fileSessionprovider.getFiles(AddonModDataProvider.COMPONENT, field.dataid + '_' + field.id); + } + + /** + * Get field data in changed. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @param {any} originalFieldData Original field entered data. + * @return {Promise | boolean} If the field has changes. + */ + hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { + const files = this.fileSessionprovider.getFiles(AddonModDataProvider.COMPONENT, field.dataid + '_' + field.id) || []; + let originalFiles = (originalFieldData && originalFieldData.files) || []; + + if (originalFiles.length) { + originalFiles = [originalFiles[0]]; + } + + return this.fileUploaderProvider.areFileListDifferent(files, originalFiles); + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + return false; + } + + /** + * Override field content data with offline submission. + * + * @param {any} originalContent Original data to be overriden. + * @param {any} offlineContent Array with all the offline data to override. + * @param {any} [offlineFiles] Array with all the offline files in the field. + * @return {any} Data overriden + */ + overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { + if (offlineContent && offlineContent.file && offlineContent.file.offline > 0 && offlineFiles && offlineFiles.length > 0) { + originalContent.content = offlineFiles[0].filename; + originalContent.files = [offlineFiles[0]]; + } else if (offlineContent && offlineContent.file && offlineContent.file.online && offlineContent.file.online.length > 0) { + originalContent.content = offlineContent.file.online[0].filename; + originalContent.files = [offlineContent.file.online[0]]; + } + + return originalContent; + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/addon/mod/data/fields/latlong/component/latlong.html b/src/addon/mod/data/fields/latlong/component/latlong.html new file mode 100644 index 000000000..0b2ef6c9e --- /dev/null +++ b/src/addon/mod/data/fields/latlong/component/latlong.html @@ -0,0 +1,19 @@ + + + + + + + + + °N + + + + °E + + + + + {{ formatLatLong(north, east) }} + \ No newline at end of file diff --git a/src/addon/mod/data/fields/latlong/component/latlong.ts b/src/addon/mod/data/fields/latlong/component/latlong.ts new file mode 100644 index 000000000..2d4e555a8 --- /dev/null +++ b/src/addon/mod/data/fields/latlong/component/latlong.ts @@ -0,0 +1,90 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { Platform } from 'ionic-angular'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data latlong field. + */ +@Component({ + selector: 'addon-mod-data-field-latlong', + templateUrl: 'latlong.html' +}) +export class AddonModDataFieldLatlongComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + values = {}; + north: number; + east: number; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef, private platform: Platform) { + super(); + } + + /** + * Format latitude and longitude in a simple text. + * + * @param {number} north Degrees north. + * @param {number} east Degrees East. + * @return {string} Readable Latitude and logitude. + */ + formatLatLong(north: number, east: number): string { + if (north !== null || east !== null) { + const northFixed = north ? Math.abs(north).toFixed(4) : '0.0000', + eastFixed = east ? Math.abs(east).toFixed(4) : '0.0000'; + + return northFixed + (north < 0 ? '°S' : '°N') + ' ' + eastFixed + (east < 0 ? '°W' : '°E'); + } + } + + /** + * Get link to maps from latitude and longitude. + * + * @param {number} north Degrees north. + * @param {number} east Degrees East. + * @return {string} Link to maps depending on platform. + */ + getLatLongLink(north: number, east: number): string { + if (north !== null || east !== null) { + const northFixed = north ? north.toFixed(4) : '0.0000', + eastFixed = east ? east.toFixed(4) : '0.0000'; + + if (this.platform.is('ios')) { + return 'http://maps.apple.com/?ll=' + northFixed + ',' + eastFixed + '&near=' + northFixed + ',' + eastFixed; + } + + return 'geo:' + northFixed + ',' + eastFixed; + } + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.mode = this.mode == 'list' ? 'show' : this.mode; + this.render(); + } + + protected render(): void { + if (this.value) { + this.north = (this.value && parseFloat(this.value.content)) || null; + this.east = (this.value && parseFloat(this.value.content1)) || null; + } + } +} diff --git a/src/addon/mod/data/fields/latlong/latlong.module.ts b/src/addon/mod/data/fields/latlong/latlong.module.ts new file mode 100644 index 000000000..5b9b4f11a --- /dev/null +++ b/src/addon/mod/data/fields/latlong/latlong.module.ts @@ -0,0 +1,49 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldLatlongHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldLatlongComponent } from './component/latlong'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldLatlongComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule + ], + providers: [ + AddonModDataFieldLatlongHandler + ], + exports: [ + AddonModDataFieldLatlongComponent + ], + entryComponents: [ + AddonModDataFieldLatlongComponent + ] +}) +export class AddonModDataFieldLatlongModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldLatlongHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/fields/latlong/providers/handler.ts b/src/addon/mod/data/fields/latlong/providers/handler.ts new file mode 100644 index 000000000..fb549c7c4 --- /dev/null +++ b/src/addon/mod/data/fields/latlong/providers/handler.ts @@ -0,0 +1,159 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; +import { AddonModDataFieldLatlongComponent } from '../component/latlong'; + +/** + * Handler for latlong data field plugin. + */ +@Injectable() +export class AddonModDataFieldLatlongHandler implements AddonModDataFieldHandler { + name = 'AddonModDataFieldLatlongHandler'; + type = 'latlong'; + + constructor(private translate: TranslateService) { } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldLatlongComponent; + } + + /** + * Get field search data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the search form. + * @return {any} With name and value of the data to be sent. + */ + getFieldSearchData(field: any, inputData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName] + }]; + } + + return false; + } + + /** + * Get field edit data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditData(field: any, inputData: any, originalFieldData: any): any { + const fieldName = 'f_' + field.id, + values = []; + + if (inputData[fieldName + '_0']) { + values.push({ + fieldid: field.id, + subfield: '0', + value: inputData[fieldName + '_0'] + }); + } + + if (inputData[fieldName + '_1']) { + values.push({ + fieldid: field.id, + subfield: '1', + value: inputData[fieldName + '_1'] + }); + } + + return values; + } + + /** + * Get field data in changed. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @param {any} originalFieldData Original field entered data. + * @return {Promise | boolean} If the field has changes. + */ + hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { + const fieldName = 'f_' + field.id, + lat = inputData[fieldName + '_0'] || '', + long = inputData[fieldName + '_1'] || '', + originalLat = (originalFieldData && originalFieldData.content) || '', + originalLong = (originalFieldData && originalFieldData.content1) || ''; + + return lat != originalLat || long != originalLong; + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + 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 this.translate.instant('addon.mod_data.latlongboth'); + } else if (field.required && valueCount == 0) { + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + return false; + } + + /** + * Override field content data with offline submission. + * + * @param {any} originalContent Original data to be overriden. + * @param {any} offlineContent Array with all the offline data to override. + * @param {any} [offlineFiles] Array with all the offline files in the field. + * @return {any} Data overriden + */ + overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { + originalContent.content = offlineContent[0] || ''; + originalContent.content1 = offlineContent[1] || ''; + + return originalContent; + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/addon/mod/data/fields/menu/component/menu.html b/src/addon/mod/data/fields/menu/component/menu.html new file mode 100644 index 000000000..12f41a13f --- /dev/null +++ b/src/addon/mod/data/fields/menu/component/menu.html @@ -0,0 +1,9 @@ + + + + + {{ 'addon.mod_data.menuchoose' | translate }} + {{option}} + + + \ No newline at end of file diff --git a/src/addon/mod/data/fields/menu/component/menu.ts b/src/addon/mod/data/fields/menu/component/menu.ts new file mode 100644 index 000000000..08628a556 --- /dev/null +++ b/src/addon/mod/data/fields/menu/component/menu.ts @@ -0,0 +1,57 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data menu field. + */ +@Component({ + selector: 'addon-mod-data-field-menu', + templateUrl: 'menu.html' +}) +export class AddonModDataFieldMenuComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + val: string; + options = []; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef) { + super(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.mode = this.mode == 'list' ? 'show' : this.mode; + this.render(); + } + + protected render(): void { + if (this.mode == 'show') { + return; + } + + this.options = this.field.param1.split('\n'); + + if (this.mode == 'edit' && this.value) { + this.val = this.value.content; + } + } +} diff --git a/src/addon/mod/data/fields/menu/menu.module.ts b/src/addon/mod/data/fields/menu/menu.module.ts new file mode 100644 index 000000000..2c4a16427 --- /dev/null +++ b/src/addon/mod/data/fields/menu/menu.module.ts @@ -0,0 +1,49 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldMenuHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldMenuComponent } from './component/menu'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldMenuComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule + ], + providers: [ + AddonModDataFieldMenuHandler + ], + exports: [ + AddonModDataFieldMenuComponent + ], + entryComponents: [ + AddonModDataFieldMenuComponent + ] +}) +export class AddonModDataFieldMenuModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldMenuHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/fields/menu/providers/handler.ts b/src/addon/mod/data/fields/menu/providers/handler.ts new file mode 100644 index 000000000..4e6938c28 --- /dev/null +++ b/src/addon/mod/data/fields/menu/providers/handler.ts @@ -0,0 +1,134 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; +import { AddonModDataFieldMenuComponent } from '../component/menu'; + +/** + * Handler for menu data field plugin. + */ +@Injectable() +export class AddonModDataFieldMenuHandler implements AddonModDataFieldHandler { + name = 'AddonModDataFieldMenuHandler'; + type = 'menu'; + + constructor(private translate: TranslateService) { } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldMenuComponent; + } + + /** + * Get field search data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the search form. + * @return {any} With name and value of the data to be sent. + */ + getFieldSearchData(field: any, inputData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName] + }]; + } + + return false; + } + + /** + * Get field edit data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditData(field: any, inputData: any, originalFieldData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + fieldid: field.id, + value: inputData[fieldName] + }]; + } + + return false; + } + + /** + * Get field data in changed. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @param {any} originalFieldData Original field entered data. + * @return {Promise | boolean} If the field has changes. + */ + hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { + const fieldName = 'f_' + field.id, + input = inputData[fieldName] || ''; + originalFieldData = (originalFieldData && originalFieldData.content) || ''; + + return input != originalFieldData; + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + return false; + } + + /** + * Override field content data with offline submission. + * + * @param {any} originalContent Original data to be overriden. + * @param {any} offlineContent Array with all the offline data to override. + * @param {any} [offlineFiles] Array with all the offline files in the field. + * @return {any} Data overriden + */ + overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { + originalContent.content = offlineContent[''] || ''; + + return originalContent; + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/addon/mod/data/fields/multimenu/component/multimenu.html b/src/addon/mod/data/fields/multimenu/component/multimenu.html new file mode 100644 index 000000000..fbf41a7d7 --- /dev/null +++ b/src/addon/mod/data/fields/multimenu/component/multimenu.html @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/src/addon/mod/data/fields/multimenu/component/multimenu.ts b/src/addon/mod/data/fields/multimenu/component/multimenu.ts new file mode 100644 index 000000000..c233c08c2 --- /dev/null +++ b/src/addon/mod/data/fields/multimenu/component/multimenu.ts @@ -0,0 +1,63 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data multimenu field. + */ +@Component({ + selector: 'addon-mod-data-field-multimenu', + templateUrl: 'multimenu.html' +}) +export class AddonModDataFieldMultimenuComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + options = []; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef) { + super(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.mode = this.mode == 'list' ? 'show' : this.mode; + this.render(); + } + + protected render(): void { + if (this.mode == 'show') { + return; + } + + this.options = this.field.param1.split('\n').map((option) => { + return { key: option, value: option }; + }); + + if (this.mode == 'edit' && this.value && this.value.content) { + this.value.content.split('##').forEach((value) => { + const x = this.options.findIndex((option) => value == option.key); + if (x >= 0) { + this.options[x].selected = true; + } + }); + } + } +} diff --git a/src/addon/mod/data/fields/multimenu/multimenu.module.ts b/src/addon/mod/data/fields/multimenu/multimenu.module.ts new file mode 100644 index 000000000..7b6b72d12 --- /dev/null +++ b/src/addon/mod/data/fields/multimenu/multimenu.module.ts @@ -0,0 +1,49 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldMultimenuHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldMultimenuComponent } from './component/multimenu'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldMultimenuComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule + ], + providers: [ + AddonModDataFieldMultimenuHandler + ], + exports: [ + AddonModDataFieldMultimenuComponent + ], + entryComponents: [ + AddonModDataFieldMultimenuComponent + ] +}) +export class AddonModDataFieldMultimenuModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldMultimenuHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/fields/multimenu/providers/handler.ts b/src/addon/mod/data/fields/multimenu/providers/handler.ts new file mode 100644 index 000000000..9f65ee8bf --- /dev/null +++ b/src/addon/mod/data/fields/multimenu/providers/handler.ts @@ -0,0 +1,152 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; +import { AddonModDataFieldMultimenuComponent } from '../component/multimenu'; + +/** + * Handler for multimenu data field plugin. + */ +@Injectable() +export class AddonModDataFieldMultimenuHandler implements AddonModDataFieldHandler { + name = 'AddonModDataFieldMultimenuHandler'; + type = 'multimenu'; + + constructor(private translate: TranslateService) { } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldMultimenuComponent; + } + + /** + * Get field search data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the search form. + * @return {any} With name and value of the data to be sent. + */ + getFieldSearchData(field: any, inputData: any): any { + const fieldName = 'f_' + field.id, + reqName = 'f_' + field.id + '_allreq'; + + if (inputData[fieldName].length > 0) { + const options = inputData[fieldName].split('###'), + values = []; + + if (options.length > 0) { + values.push({ + name: fieldName, + value: options + }); + + if (inputData[reqName]['1']) { + values.push({ + name: reqName, + value: true + }); + } + + return values; + } + } + + return false; + } + + /** + * Get field edit data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditData(field: any, inputData: any, originalFieldData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName] && inputData[fieldName].length > 0) { + const options = inputData[fieldName].split('###'); + if (options.length > 0) { + return [{ + fieldid: field.id, + value: options + }]; + } + } + + return false; + } + + /** + * Get field data in changed. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @param {any} originalFieldData Original field entered data. + * @return {Promise | boolean} If the field has changes. + */ + hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { + const fieldName = 'f_' + field.id, + input = inputData[fieldName] || ''; + originalFieldData = (originalFieldData && originalFieldData.content) || ''; + + return input != originalFieldData; + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + return false; + } + + /** + * Override field content data with offline submission. + * + * @param {any} originalContent Original data to be overriden. + * @param {any} offlineContent Array with all the offline data to override. + * @param {any} [offlineFiles] Array with all the offline files in the field. + * @return {any} Data overriden + */ + overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { + originalContent.content = (offlineContent[''] && offlineContent[''].join('###')) || ''; + + return originalContent; + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/addon/mod/data/fields/number/component/number.html b/src/addon/mod/data/fields/number/component/number.html new file mode 100644 index 000000000..41bbe1fb0 --- /dev/null +++ b/src/addon/mod/data/fields/number/component/number.html @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/addon/mod/data/fields/number/component/number.ts b/src/addon/mod/data/fields/number/component/number.ts new file mode 100644 index 000000000..c463ea5e4 --- /dev/null +++ b/src/addon/mod/data/fields/number/component/number.ts @@ -0,0 +1,54 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data number field. + */ +@Component({ + selector: 'addon-mod-data-field-number', + templateUrl: 'number.html' +}) +export class AddonModDataFieldNumberComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + val: number; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef) { + super(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.mode = this.mode == 'list' ? 'show' : this.mode; + this.render(); + } + + protected render(): void { + if (this.mode == 'show') { + return; + } + + if (this.mode == 'edit' && this.value) { + this.val = this.value && parseFloat(this.value.content); + } + } +} diff --git a/src/addon/mod/data/fields/number/number.module.ts b/src/addon/mod/data/fields/number/number.module.ts new file mode 100644 index 000000000..ce2acc789 --- /dev/null +++ b/src/addon/mod/data/fields/number/number.module.ts @@ -0,0 +1,49 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldNumberHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldNumberComponent } from './component/number'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldNumberComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule + ], + providers: [ + AddonModDataFieldNumberHandler + ], + exports: [ + AddonModDataFieldNumberComponent + ], + entryComponents: [ + AddonModDataFieldNumberComponent + ] +}) +export class AddonModDataFieldNumberModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldNumberHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/fields/number/providers/handler.ts b/src/addon/mod/data/fields/number/providers/handler.ts new file mode 100644 index 000000000..654681d72 --- /dev/null +++ b/src/addon/mod/data/fields/number/providers/handler.ts @@ -0,0 +1,57 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonModDataFieldTextHandler } from '../../text/providers/handler'; +import { AddonModDataFieldNumberComponent } from '../component/number'; + +/** + * Handler for number data field plugin. + */ +@Injectable() +export class AddonModDataFieldNumberHandler extends AddonModDataFieldTextHandler { + name = 'AddonModDataFieldNumberHandler'; + type = 'number'; + + constructor(protected translate: TranslateService) { + super(translate); + } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldNumberComponent; + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + if (field.required && (!inputData || !inputData.length || inputData[0].value == '')) { + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + return false; + } +} diff --git a/src/addon/mod/data/fields/picture/component/picture.html b/src/addon/mod/data/fields/picture/component/picture.html new file mode 100644 index 000000000..e435409f8 --- /dev/null +++ b/src/addon/mod/data/fields/picture/component/picture.html @@ -0,0 +1,17 @@ + + + + + + + + + + {{ 'addon.mod_data.alttext' | translate }} + + °N + + + + + \ No newline at end of file diff --git a/src/addon/mod/data/fields/picture/component/picture.ts b/src/addon/mod/data/fields/picture/component/picture.ts new file mode 100644 index 000000000..b4516a4a3 --- /dev/null +++ b/src/addon/mod/data/fields/picture/component/picture.ts @@ -0,0 +1,128 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; +import { CoreFileSessionProvider } from '@providers/file-session'; +import { AddonModDataProvider } from '../../../providers/data'; + +/** + * Component to render data picture field. + */ +@Component({ + selector: 'addon-mod-data-field-picture', + templateUrl: 'picture.html' +}) +export class AddonModDataFieldPictureComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + files = []; + component: string; + componentId: number; + maxSizeBytes: number; + + image: any; + entryId: number; + imageUrl: string; + title: string; + alttext: string; + width: string; + height: string; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef, private fileSessionprovider: CoreFileSessionProvider) { + super(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.render(); + } + + /** + * Get the files from the input value. + * + * @param {any} value Input value. + * @return {any} List of files. + */ + protected getFiles(value: any): any { + let files = (value && value.files) || []; + + // Reduce to first element. + if (files.length > 0) { + files = [files[0]]; + } + + return files; + } + + /** + * Find file in a list. + * + * @param {any[]} files File list where to search. + * @param {string} filenameSeek Filename to search. + * @return {any} File found or false. + */ + protected findFile(files: any[], filenameSeek: string): any { + return files.find((file) => file.filename == filenameSeek) || false; + } + + protected render(): void { + if (this.mode != 'search') { + this.component = AddonModDataProvider.COMPONENT; + this.componentId = this.database.coursemodule; + + // Edit mode, the list shouldn't change so there is no need to watch it. + const files = this.value && this.value.files || []; + + // Get image or thumb. + if (files.length > 0) { + const filenameSeek = this.mode == 'list' ? 'thumb_' + this.value.content : this.value.content; + this.image = this.findFile(files, filenameSeek); + + if (!this.image && this.mode == 'list') { + this.image = this.findFile(files, this.value.content); + } + + this.files = [this.image]; + } else { + this.image = false; + this.files = []; + } + + if (this.mode == 'edit') { + this.maxSizeBytes = parseInt(this.field.param3, 10); + this.fileSessionprovider.setFiles(this.component, this.database.id + '_' + this.field.id, this.files); + this.alttext = (this.value && this.value.content1) || ''; + } else { + this.entryId = (this.value && this.value.recordid) || null; + this.title = (this.value && this.value.content1) || ''; + this.imageUrl = null; + if (this.image) { + if (this.image.offline) { + this.imageUrl = (this.image && this.image.toURL()) || null; + } else { + this.imageUrl = (this.image && this.image.fileurl) || null; + } + } + this.width = this.field.param1 || ''; + this.height = this.field.param2 || ''; + } + } + } +} diff --git a/src/addon/mod/data/fields/picture/picture.module.ts b/src/addon/mod/data/fields/picture/picture.module.ts new file mode 100644 index 000000000..c666e1bad --- /dev/null +++ b/src/addon/mod/data/fields/picture/picture.module.ts @@ -0,0 +1,49 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldPictureHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldPictureComponent } from './component/picture'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldPictureComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule, + ], + providers: [ + AddonModDataFieldPictureHandler + ], + exports: [ + AddonModDataFieldPictureComponent + ], + entryComponents: [ + AddonModDataFieldPictureComponent + ] +}) +export class AddonModDataFieldPictureModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldPictureHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/fields/picture/providers/handler.ts b/src/addon/mod/data/fields/picture/providers/handler.ts new file mode 100644 index 000000000..a5eab43b0 --- /dev/null +++ b/src/addon/mod/data/fields/picture/providers/handler.ts @@ -0,0 +1,194 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreFileSessionProvider } from '@providers/file-session'; +import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; +import { AddonModDataProvider } from '../../../providers/data'; +import { AddonModDataFieldPictureComponent } from '../component/picture'; +import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; + +/** + * Handler for picture data field plugin. + */ +@Injectable() +export class AddonModDataFieldPictureHandler implements AddonModDataFieldHandler { + name = 'AddonModDataFieldPictureHandler'; + type = 'picture'; + + constructor(private translate: TranslateService, private fileSessionprovider: CoreFileSessionProvider, + private fileUploaderProvider: CoreFileUploaderProvider) { } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldPictureComponent; + } + + /** + * Get field search data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the search form. + * @return {any} With name and value of the data to be sent. + */ + getFieldSearchData(field: any, inputData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName] + }]; + } + + return false; + } + + /** + * Get field edit data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditData(field: any, inputData: any, originalFieldData: any): any { + const files = this.getFieldEditFiles(field), + values = [], + fieldName = 'f_' + field.id + '_alttext'; + + if (files.length) { + values.push({ + fieldid: field.id, + subfield: 'file', + files: files + }); + } + + if (inputData[fieldName]) { + values.push({ + fieldid: field.id, + subfield: 'alttext', + value: inputData[fieldName] + }); + } + + return values; + } + + /** + * Get field edit files in the input data. + * + * @param {any} field Defines the field.. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditFiles(field: any): any { + return this.fileSessionprovider.getFiles(AddonModDataProvider.COMPONENT, field.dataid + '_' + field.id); + } + + /** + * Get field data in changed. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @param {any} originalFieldData Original field entered data. + * @return {Promise | boolean} If the field has changes. + */ + hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { + const fieldName = 'f_' + field.id + '_alttext', + altText = inputData[fieldName] || '', + originalAltText = (originalFieldData && originalFieldData.content1) || '', + files = this.getFieldEditFiles(field) || []; + let originalFiles = (originalFieldData && originalFieldData.files) || []; + + // Get image. + if (originalFiles.length > 0) { + const filenameSeek = (originalFieldData && originalFieldData.content) || '', + file = originalFiles.find((file) => file.filename == filenameSeek); + if (file) { + originalFiles = [file]; + } + } else { + originalFiles = []; + } + + return altText != originalAltText || this.fileUploaderProvider.areFileListDifferent(files, originalFiles); + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + if (field.required) { + if (!inputData || !inputData.length) { + return this.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 this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + return false; + } + + /** + * Override field content data with offline submission. + * + * @param {any} originalContent Original data to be overriden. + * @param {any} offlineContent Array with all the offline data to override. + * @param {any} [offlineFiles] Array with all the offline files in the field. + * @return {any} Data overriden + */ + overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { + if (offlineContent && offlineContent.file && offlineContent.file.offline > 0 && offlineFiles && offlineFiles.length > 0) { + originalContent.content = offlineFiles[0].filename; + originalContent.files = [offlineFiles[0]]; + } else if (offlineContent && offlineContent.file && offlineContent.file.online && offlineContent.file.online.length > 0) { + originalContent.content = offlineContent.file.online[0].filename; + originalContent.files = [offlineContent.file.online[0]]; + } + + originalContent.content1 = offlineContent.alttext || ''; + + return originalContent; + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/addon/mod/data/fields/radiobutton/component/radiobutton.html b/src/addon/mod/data/fields/radiobutton/component/radiobutton.html new file mode 100644 index 000000000..12f41a13f --- /dev/null +++ b/src/addon/mod/data/fields/radiobutton/component/radiobutton.html @@ -0,0 +1,9 @@ + + + + + {{ 'addon.mod_data.menuchoose' | translate }} + {{option}} + + + \ No newline at end of file diff --git a/src/addon/mod/data/fields/radiobutton/component/radiobutton.ts b/src/addon/mod/data/fields/radiobutton/component/radiobutton.ts new file mode 100644 index 000000000..208d5d2cb --- /dev/null +++ b/src/addon/mod/data/fields/radiobutton/component/radiobutton.ts @@ -0,0 +1,57 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data radiobutton field. + */ +@Component({ + selector: 'addon-mod-data-field-radiobutton', + templateUrl: 'radiobutton.html' +}) +export class AddonModDataFieldRadiobuttonComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + options: number; + val: number; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef) { + super(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.mode = this.mode == 'list' ? 'show' : this.mode; + this.render(); + } + + protected render(): void { + if (this.mode == 'show') { + return; + } + + this.options = this.field.param1.split('\n'); + + if (this.mode == 'edit' && this.value) { + this.val = this.value.content; + } + } +} diff --git a/src/addon/mod/data/fields/radiobutton/providers/handler.ts b/src/addon/mod/data/fields/radiobutton/providers/handler.ts new file mode 100644 index 000000000..6efbd744b --- /dev/null +++ b/src/addon/mod/data/fields/radiobutton/providers/handler.ts @@ -0,0 +1,133 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; +import { AddonModDataFieldRadiobuttonComponent } from '../component/radiobutton'; + +/** + * Handler for checkbox data field plugin. + */ +@Injectable() +export class AddonModDataFieldRadiobuttonHandler implements AddonModDataFieldHandler { + name = 'AddonModDataFieldRadiobuttonHandler'; + type = 'radiobutton'; + + constructor(private translate: TranslateService) { } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldRadiobuttonComponent; + } + + /** + * Get field search data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the search form. + * @return {any} With name and value of the data to be sent. + */ + getFieldSearchData(field: any, inputData: any): any { + const fieldName = 'f_' + field.id; + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName] + }]; + } + + return false; + } + + /** + * Get field edit data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditData(field: any, inputData: any, originalFieldData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + fieldid: field.id, + value: inputData[fieldName] + }]; + } + + return false; + } + + /** + * Get field data in changed. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @param {any} originalFieldData Original field entered data. + * @return {Promise | boolean} If the field has changes. + */ + hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { + const fieldName = 'f_' + field.id, + input = inputData[fieldName] || ''; + originalFieldData = (originalFieldData && originalFieldData.content) || ''; + + return input != originalFieldData; + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + return false; + } + + /** + * Override field content data with offline submission. + * + * @param {any} originalContent Original data to be overriden. + * @param {any} offlineContent Array with all the offline data to override. + * @param {any} [offlineFiles] Array with all the offline files in the field. + * @return {any} Data overriden + */ + overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { + originalContent.content = offlineContent[''] || ''; + + return originalContent; + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/addon/mod/data/fields/radiobutton/radiobutton.module.ts b/src/addon/mod/data/fields/radiobutton/radiobutton.module.ts new file mode 100644 index 000000000..d361bc934 --- /dev/null +++ b/src/addon/mod/data/fields/radiobutton/radiobutton.module.ts @@ -0,0 +1,49 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldRadiobuttonHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldRadiobuttonComponent } from './component/radiobutton'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldRadiobuttonComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule + ], + providers: [ + AddonModDataFieldRadiobuttonHandler + ], + exports: [ + AddonModDataFieldRadiobuttonComponent + ], + entryComponents: [ + AddonModDataFieldRadiobuttonComponent + ] +}) +export class AddonModDataFieldRadiobuttonModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldRadiobuttonHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/fields/text/component/text.html b/src/addon/mod/data/fields/text/component/text.html new file mode 100644 index 000000000..d907c4a9c --- /dev/null +++ b/src/addon/mod/data/fields/text/component/text.html @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/addon/mod/data/fields/text/component/text.ts b/src/addon/mod/data/fields/text/component/text.ts new file mode 100644 index 000000000..174bc1cd2 --- /dev/null +++ b/src/addon/mod/data/fields/text/component/text.ts @@ -0,0 +1,54 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data text field. + */ +@Component({ + selector: 'addon-mod-data-field-text', + templateUrl: 'text.html' +}) +export class AddonModDataFieldTextComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + val: number; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef) { + super(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.mode = this.mode == 'list' ? 'show' : this.mode; + this.render(); + } + + protected render(): void { + if (this.mode == 'show') { + return; + } + + if (this.mode == 'edit' && this.value) { + this.val = this.value.content; + } + } +} diff --git a/src/addon/mod/data/fields/text/providers/handler.ts b/src/addon/mod/data/fields/text/providers/handler.ts new file mode 100644 index 000000000..19d49b713 --- /dev/null +++ b/src/addon/mod/data/fields/text/providers/handler.ts @@ -0,0 +1,134 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonModDataFieldHandler } from '../../../providers/fields-delegate'; +import { AddonModDataFieldTextComponent } from '../component/text'; + +/** + * Handler for number data field plugin. + */ +@Injectable() +export class AddonModDataFieldTextHandler implements AddonModDataFieldHandler { + name = 'AddonModDataFieldTextHandler'; + type = 'text'; + + constructor(protected translate: TranslateService) { } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldTextComponent; + } + + /** + * Get field search data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the search form. + * @return {any} With name and value of the data to be sent. + */ + getFieldSearchData(field: any, inputData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + name: fieldName, + value: inputData[fieldName] + }]; + } + + return false; + } + + /** + * Get field edit data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditData(field: any, inputData: any, originalFieldData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return [{ + fieldid: field.id, + value: inputData[fieldName] + }]; + } + + return false; + } + + /** + * Get field data in changed. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @param {any} originalFieldData Original field entered data. + * @return {Promise | boolean} If the field has changes. + */ + hasFieldDataChanged(field: any, inputData: any, originalFieldData: any): Promise | boolean { + const fieldName = 'f_' + field.id, + input = inputData[fieldName] || ''; + originalFieldData = (originalFieldData && originalFieldData.content) || ''; + + return input != originalFieldData; + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + return false; + } + + /** + * Override field content data with offline submission. + * + * @param {any} originalContent Original data to be overriden. + * @param {any} offlineContent Array with all the offline data to override. + * @param {any} [offlineFiles] Array with all the offline files in the field. + * @return {any} Data overriden + */ + overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { + originalContent.content = offlineContent[''] || ''; + + return originalContent; + } + + /** + * Whether or not the handler is enabled on a site level. + * + * @return {boolean|Promise} True or promise resolved with true if enabled. + */ + isEnabled(): boolean | Promise { + return true; + } +} diff --git a/src/addon/mod/data/fields/text/text.module.ts b/src/addon/mod/data/fields/text/text.module.ts new file mode 100644 index 000000000..a96ddf9de --- /dev/null +++ b/src/addon/mod/data/fields/text/text.module.ts @@ -0,0 +1,49 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldTextHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldTextComponent } from './component/text'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldTextComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule + ], + providers: [ + AddonModDataFieldTextHandler + ], + exports: [ + AddonModDataFieldTextComponent + ], + entryComponents: [ + AddonModDataFieldTextComponent + ] +}) +export class AddonModDataFieldTextModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldTextHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/fields/textarea/component/textarea.html b/src/addon/mod/data/fields/textarea/component/textarea.html new file mode 100644 index 000000000..5a625183b --- /dev/null +++ b/src/addon/mod/data/fields/textarea/component/textarea.html @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/addon/mod/data/fields/textarea/component/textarea.ts b/src/addon/mod/data/fields/textarea/component/textarea.ts new file mode 100644 index 000000000..83336cff7 --- /dev/null +++ b/src/addon/mod/data/fields/textarea/component/textarea.ts @@ -0,0 +1,69 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataProvider } from '../../../providers/data'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data number field. + */ +@Component({ + selector: 'addon-mod-data-field-textarea', + templateUrl: 'textarea.html' +}) +export class AddonModDataFieldTextareaComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + component: string; + componentId: number; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef) { + super(); + } + + format(value: any): string { + const files = (value && value.files) || []; + + return value ? this.textUtils.replacePluginfileUrls(value.content, files) : ''; + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.mode = this.mode == 'list' ? 'show' : this.mode; + this.render(); + } + + protected render(): void { + if (this.mode == 'show') { + this.component = AddonModDataProvider.COMPONENT; + this.componentId = this.database.coursemodule; + + return; + } + + // Check if rich text editor is enabled. + if (this.mode == 'edit') { + const files = (this.value && this.value.files) || [], + text = this.value ? this.textUtils.replacePluginfileUrls(this.value.content, files) : ''; + + this.control = this.fb.control(text); + } + } +} diff --git a/src/addon/mod/data/fields/textarea/providers/handler.ts b/src/addon/mod/data/fields/textarea/providers/handler.ts new file mode 100644 index 000000000..0a5829698 --- /dev/null +++ b/src/addon/mod/data/fields/textarea/providers/handler.ts @@ -0,0 +1,145 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonModDataFieldTextHandler } from '../../text/providers/handler'; +import { AddonModDataFieldTextareaComponent } from '../component/textarea'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; + +/** + * Handler for textarea data field plugin. + */ +@Injectable() +export class AddonModDataFieldTextareaHandler extends AddonModDataFieldTextHandler { + name = 'AddonModDataFieldTextareaHandler'; + type = 'textarea'; + + constructor(protected translate: TranslateService, private textUtils: CoreTextUtilsProvider, + private domUtils: CoreDomUtilsProvider) { + super(translate); + } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldTextareaComponent; + } + + /** + * Get field edit data in the input data. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditData(field: any, inputData: any, originalFieldData: any): any { + const fieldName = 'f_' + field.id; + + if (inputData[fieldName]) { + return this.domUtils.isRichTextEditorEnabled().then((enabled) => { + const files = this.getFieldEditFiles(field, inputData, originalFieldData); + let text = this.textUtils.restorePluginfileUrls(inputData[fieldName], files); + + if (!enabled) { + // Rich text editor not enabled, add some HTML to the text if needed. + text = this.textUtils.formatHtmlLines(text); + } + + return [{ + fieldid: field.id, + value: text + }, + { + fieldid: field.id, + subfield: 'content1', + value: 1 + }, + { + fieldid: field.id, + subfield: 'itemid', + files: files + } + ]; + }); + } + + return false; + } + + /** + * Get field edit files in the input data. + * + * @param {any} field Defines the field.. + * @param {any} inputData Data entered in the edit form. + * @param {any} originalFieldData Original field entered data. + * @return {any} With name and value of the data to be sent. + */ + getFieldEditFiles(field: any, inputData: any, originalFieldData: any): any { + return (originalFieldData && originalFieldData.files) || []; + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + if (field.required) { + if (!inputData || !inputData.length) { + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + const found = inputData.some((input) => { + if (!input.subfield) { + return !!input.value; + } + + return false; + }); + + if (!found) { + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + } + + return false; + } + + /** + * Override field content data with offline submission. + * + * @param {any} originalContent Original data to be overriden. + * @param {any} offlineContent Array with all the offline data to override. + * @param {any} [offlineFiles] Array with all the offline files in the field. + * @return {any} Data overriden + */ + overrideData(originalContent: any, offlineContent: any, offlineFiles?: any): any { + 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 = this.textUtils.replacePluginfileUrls(originalContent.content, originalContent.files); + } + + return originalContent; + } +} diff --git a/src/addon/mod/data/fields/textarea/textarea.module.ts b/src/addon/mod/data/fields/textarea/textarea.module.ts new file mode 100644 index 000000000..8c63d53f7 --- /dev/null +++ b/src/addon/mod/data/fields/textarea/textarea.module.ts @@ -0,0 +1,49 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldTextareaHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldTextareaComponent } from './component/textarea'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldTextareaComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule + ], + providers: [ + AddonModDataFieldTextareaHandler + ], + exports: [ + AddonModDataFieldTextareaComponent + ], + entryComponents: [ + AddonModDataFieldTextareaComponent + ] +}) +export class AddonModDataFieldTextareaModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldTextareaHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/fields/url/component/url.html b/src/addon/mod/data/fields/url/component/url.html new file mode 100644 index 000000000..ddd31d55f --- /dev/null +++ b/src/addon/mod/data/fields/url/component/url.html @@ -0,0 +1,7 @@ + + + + + + +{{field.name}} \ No newline at end of file diff --git a/src/addon/mod/data/fields/url/component/url.ts b/src/addon/mod/data/fields/url/component/url.ts new file mode 100644 index 000000000..15ac1f53f --- /dev/null +++ b/src/addon/mod/data/fields/url/component/url.ts @@ -0,0 +1,54 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Component, OnInit, ElementRef } from '@angular/core'; +import { FormBuilder, FormControl } from '@angular/forms'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { AddonModDataFieldPluginComponent } from '../../../classes/field-plugin-component'; + +/** + * Component to render data url field. + */ +@Component({ + selector: 'addon-mod-data-field-url', + templateUrl: 'url.html' +}) +export class AddonModDataFieldUrlComponent extends AddonModDataFieldPluginComponent implements OnInit { + + control: FormControl; + val: number; + + constructor(protected fb: FormBuilder, protected domUtils: CoreDomUtilsProvider, protected textUtils: CoreTextUtilsProvider, + element: ElementRef) { + super(); + } + + /** + * Component being initialized. + */ + ngOnInit(): void { + this.mode = this.mode == 'list' ? 'show' : this.mode; + this.render(); + } + + protected render(): void { + if (this.mode == 'show') { + return; + } + + if (this.mode == 'edit' && this.value) { + this.val = this.value.content; + } + } +} diff --git a/src/addon/mod/data/fields/url/providers/handler.ts b/src/addon/mod/data/fields/url/providers/handler.ts new file mode 100644 index 000000000..7e6ea803e --- /dev/null +++ b/src/addon/mod/data/fields/url/providers/handler.ts @@ -0,0 +1,57 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { Injector, Injectable } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { AddonModDataFieldTextHandler } from '../../text/providers/handler'; +import { AddonModDataFieldUrlComponent } from '../component/url'; + +/** + * Handler for url data field plugin. + */ +@Injectable() +export class AddonModDataFieldUrlHandler extends AddonModDataFieldTextHandler { + name = 'AddonModDataFieldUrlHandler'; + type = 'url'; + + constructor(protected translate: TranslateService) { + super(translate); + } + + /** + * Return the Component to use to display the plugin data. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param {Injector} injector Injector. + * @param {any} field The field object. + * @return {any|Promise} The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector, plugin: any): any | Promise { + return AddonModDataFieldUrlComponent; + } + + /** + * Check and get field requeriments. + * + * @param {any} field Defines the field to be rendered. + * @param {any} inputData Data entered in the edit form. + * @return {string | false} String with the notification or false. + */ + getFieldsNotifications(field: any, inputData: any): string | false { + if (field.required && (!inputData || !inputData.length || !inputData[0].value)) { + return this.translate.instant('addon.mod_data.errormustsupplyvalue'); + } + + return false; + } +} diff --git a/src/addon/mod/data/fields/url/url.module.ts b/src/addon/mod/data/fields/url/url.module.ts new file mode 100644 index 000000000..09cd8911e --- /dev/null +++ b/src/addon/mod/data/fields/url/url.module.ts @@ -0,0 +1,49 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { AddonModDataFieldUrlHandler } from './providers/handler'; +import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate'; +import { AddonModDataFieldUrlComponent } from './component/url'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; + +@NgModule({ + declarations: [ + AddonModDataFieldUrlComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule + ], + providers: [ + AddonModDataFieldUrlHandler + ], + exports: [ + AddonModDataFieldUrlComponent + ], + entryComponents: [ + AddonModDataFieldUrlComponent + ] +}) +export class AddonModDataFieldUrlModule { + constructor(fieldDelegate: AddonModDataFieldsDelegate, handler: AddonModDataFieldUrlHandler) { + fieldDelegate.registerHandler(handler); + } +} diff --git a/src/addon/mod/data/providers/helper.ts b/src/addon/mod/data/providers/helper.ts index 43ffe33fe..8f929e8b0 100644 --- a/src/addon/mod/data/providers/helper.ts +++ b/src/addon/mod/data/providers/helper.ts @@ -113,7 +113,7 @@ export class AddonModDataHelperProvider { replace = new RegExp(replace, 'gi'); // Replace field by a generic directive. - const render = ''; template = template.replace(replace, render); }); @@ -156,13 +156,13 @@ export class AddonModDataHelperProvider { replace = new RegExp(replace, 'gi'); // Replace field by a generic directive. - render = ''; template = template.replace(replace, render); }); - for (const action in actions) { + /*for (const action in actions) { replace = new RegExp('##' + action + '##', 'gi'); // Is enabled? if (actions[action]) { @@ -179,7 +179,7 @@ export class AddonModDataHelperProvider { } else { template = template.replace(replace, ''); } - } + }*/ return template; } diff --git a/src/addon/mod/feedback/providers/helper.ts b/src/addon/mod/feedback/providers/helper.ts index 7f5527623..cbd1ea4d5 100644 --- a/src/addon/mod/feedback/providers/helper.ts +++ b/src/addon/mod/feedback/providers/helper.ts @@ -47,7 +47,6 @@ export class AddonModFeedbackHelperProvider { protected getActivityHistoryBackCounter(pageName: string, instance: number, paramName: string, prefix: string, navCtrl: NavController): number { let historyInstance, params, - backTimes = 0, view = navCtrl.getActive(); while (!view.isFirst()) { @@ -60,9 +59,7 @@ export class AddonModFeedbackHelperProvider { historyInstance = params.get(paramName) ? params.get(paramName) : params.get('module').instance; // Check we are not changing to another activity. - if (historyInstance && historyInstance == instance) { - backTimes++; - } else { + if (!historyInstance || historyInstance != instance) { break; } diff --git a/src/core/compile/components/compile-html/compile-html.ts b/src/core/compile/components/compile-html/compile-html.ts index c530b08f2..8b7a7a218 100644 --- a/src/core/compile/components/compile-html/compile-html.ts +++ b/src/core/compile/components/compile-html/compile-html.ts @@ -42,6 +42,8 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { @Input() text: string; // The HTML text to display. @Input() javascript: string; // The Javascript to execute in the component. @Input() jsData: any; // Data to pass to the fake component. + @Input() extraImports: any[] = []; // Extra import modules. + @Input() extraProviders: any[] = []; // Extra providers. @Output() created: EventEmitter = new EventEmitter(); // Will emit an event when the component is instantiated. // Get the container where to put the content. @@ -61,7 +63,8 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { ngOnChanges(changes: { [name: string]: SimpleChange }): void { if ((changes.text || changes.javascript) && this.text) { // Create a new component and a new module. - this.compileProvider.createAndCompileComponent(this.text, this.getComponentClass()).then((factory) => { + this.compileProvider.createAndCompileComponent(this.text, this.getComponentClass(), this.extraImports) + .then((factory) => { // Destroy previous components. this.componentRef && this.componentRef.destroy(); @@ -95,7 +98,7 @@ export class CoreCompileHtmlComponent implements OnChanges, OnDestroy { constructor() { // If there is some javascript to run, prepare the instance. if (compileInstance.javascript) { - compileInstance.compileProvider.injectLibraries(this); + compileInstance.compileProvider.injectLibraries(this, compileInstance.extraProviders); } // Always add these elements, they could be needed on component init (componentObservable). diff --git a/src/core/compile/providers/compile.ts b/src/core/compile/providers/compile.ts index 828238d0f..411b62fee 100644 --- a/src/core/compile/providers/compile.ts +++ b/src/core/compile/providers/compile.ts @@ -112,17 +112,20 @@ export class CoreCompileProvider { * * @param {string} template The template of the component. * @param {any} componentClass The JS class of the component. + * @param {any[]} [extraImports] Extra imported modules if needed and not imported by this class. * @return {Promise>} Promise resolved with the factory to instantiate the component. */ - createAndCompileComponent(template: string, componentClass: any): Promise> { + createAndCompileComponent(template: string, componentClass: any, extraImports: any[] = []): Promise> { // Create the component using the template and the class. const component = Component({ template: template }) (componentClass); + const imports = this.IMPORTS.concat(extraImports); + // Now create the module containing the component. - const module = NgModule({imports: this.IMPORTS, declarations: [component]})(class {}); + const module = NgModule({imports: imports, declarations: [component]})(class {}); // Compile the module and the component. return this.compiler.compileModuleAndAllComponentsAsync(module).then((factories) => { @@ -166,13 +169,14 @@ export class CoreCompileProvider { * Inject all the core libraries in a certain object. * * @param {any} instance The instance where to inject the libraries. + * @param {any[]} [extraProviders] Extra imported providers if needed and not imported by this class. */ - injectLibraries(instance: any): void { + injectLibraries(instance: any, extraProviders: any[] = []): void { const providers = ( CORE_PROVIDERS).concat(CORE_CONTENTLINKS_PROVIDERS).concat(CORE_COURSE_PROVIDERS) .concat(CORE_COURSES_PROVIDERS).concat(CORE_FILEUPLOADER_PROVIDERS).concat(CORE_GRADES_PROVIDERS) .concat(CORE_LOGIN_PROVIDERS).concat(CORE_MAINMENU_PROVIDERS).concat(CORE_SHAREDFILES_PROVIDERS) .concat(CORE_SITEHOME_PROVIDERS).concat([CoreSitePluginsProvider]).concat(CORE_USER_PROVIDERS) - .concat(CORE_QUESTION_PROVIDERS).concat(IONIC_NATIVE_PROVIDERS).concat(this.OTHER_PROVIDERS); + .concat(CORE_QUESTION_PROVIDERS).concat(IONIC_NATIVE_PROVIDERS).concat(this.OTHER_PROVIDERS).concat(extraProviders); // We cannot inject anything to this constructor. Use the Injector to inject all the providers into the instance. for (const i in providers) {