From 03eba78d41b96af49cdfbb1dd314edd3e54824a8 Mon Sep 17 00:00:00 2001 From: Hamatoma Date: Wed, 11 Nov 2020 20:13:23 +0100 Subject: [PATCH] daily work: validation works --- data/rest/custom.configuration.yaml | 13 ++ lib/app.dart | 14 +- lib/flutter_bones.dart | 11 +- lib/src/helper/string_helper.dart | 22 +++ lib/src/model/all_db_fields_model.dart | 13 +- lib/src/model/button_model.dart | 16 +- lib/src/model/checkbox_model.dart | 15 +- lib/src/model/column_model.dart | 23 +-- lib/src/model/combo_base_model.dart | 21 +-- lib/src/model/combobox_model.dart | 12 +- lib/src/model/db_reference_model.dart | 19 +-- lib/src/model/empty_line_model.dart | 14 +- lib/src/model/field_model.dart | 25 +-- lib/src/model/model_base.dart | 24 ++- lib/src/model/module_model.dart | 15 +- lib/src/model/page_model.dart | 20 ++- lib/src/model/section_model.dart | 34 ++-- .../model/standard/configuration_model.dart | 5 + lib/src/model/standard/menu_model.dart | 6 +- lib/src/model/standard/role_model.dart | 1 + lib/src/model/standard/user_model.dart | 5 + lib/src/model/table_model.dart | 14 +- lib/src/model/text_field_model.dart | 15 +- lib/src/model/text_model.dart | 16 +- lib/src/model/widget_model.dart | 9 +- .../configuration_change_page.dart | 34 ++-- .../configuration_controller.dart | 2 +- .../configuration_create_page.dart | 25 ++- .../configuration_list_page.dart | 70 ++++---- lib/src/page/menu/menu_change_page.dart | 43 ++--- lib/src/page/menu/menu_controller.dart | 2 +- lib/src/page/menu/menu_create_page.dart | 27 +++- lib/src/page/menu/menu_list_page.dart | 54 +++---- lib/src/page/role/role_change_page.dart | 38 ++--- lib/src/page/role/role_controller.dart | 2 +- lib/src/page/role/role_create_page.dart | 26 ++- lib/src/page/role/role_list_page.dart | 54 +++---- lib/src/page/user/user_change_page.dart | 42 ++--- lib/src/page/user/user_controller.dart | 3 +- lib/src/page/user/user_create_page.dart | 29 +++- lib/src/page/user/user_list_page.dart | 54 +++---- lib/src/page/user/user_login_page.dart | 45 +++--- lib/src/page/user/user_password_page.dart | 44 ++--- lib/src/persistence/persistence_cache.dart | 38 ++++- lib/src/private/bdrawer.dart | 7 +- lib/src/widget/page_controller_bones.dart | 151 ++++++------------ lib/src/widget/view.dart | 30 +++- model_tool/lib/src/net/email.dart | 38 +++++ test/helpers/string_helper_test.dart | 27 +++- test/model/model_test.dart | 4 - test/model/standard_test.dart | 49 +++--- test/persistence_cache_test.dart | 30 +++- test/widget/widget_validators_test.dart | 2 +- 53 files changed, 732 insertions(+), 620 deletions(-) create mode 100644 data/rest/custom.configuration.yaml create mode 100644 model_tool/lib/src/net/email.dart diff --git a/data/rest/custom.configuration.yaml b/data/rest/custom.configuration.yaml new file mode 100644 index 0000000..fe2d1a5 --- /dev/null +++ b/data/rest/custom.configuration.yaml @@ -0,0 +1,13 @@ +--- +# configuration of the bones backend for configuration: +created: 2020.11.11 11:47 +author: flutter_bones.module_model.exportSqlBackend() +version: 1.0.0 +modules: + - module: configuration + sqlInfos: + - name: combobox_by_scope + type: list + sql: "SELECT configuration_id,configuration_property,configuration_value FROM configuration + WHERE configuration_scope=:scope + ORDER BY configuration_order;" diff --git a/lib/app.dart b/lib/app.dart index d79e985..452ebfe 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -2,17 +2,17 @@ import 'package:dart_bones/dart_bones.dart'; import 'package:flutter/material.dart'; import 'src/helper/settings.dart'; -import 'src/page/demo_page.dart'; import 'src/page/async_example_page.dart'; +import 'src/page/configuration/configuration_create_page.dart'; +import 'src/page/configuration/configuration_list_page.dart'; +import 'src/page/demo_page.dart'; +import 'src/page/menu/menu_create_page.dart'; +import 'src/page/menu/menu_list_page.dart'; import 'src/page/role/role_create_page.dart'; import 'src/page/role/role_list_page.dart'; import 'src/page/user/user_create_page.dart'; import 'src/page/user/user_list_page.dart'; import 'src/page/user/user_login_page.dart'; -import 'src/page/menu/menu_list_page.dart'; -import 'src/page/menu/menu_create_page.dart'; -import 'src/page/configuration/configuration_create_page.dart'; -import 'src/page/configuration/configuration_list_page.dart'; import 'src/private/bsettings.dart'; class BoneApp extends StatefulWidget { @@ -76,10 +76,10 @@ Route _getRoute(RouteSettings settings) { page = ConfigurationCreatePage(BSettings.lastInstance.pageData); break; case '/menu/list': - page = MenuCreatePage(BSettings.lastInstance.pageData); + page = MenuListPage(BSettings.lastInstance.pageData); break; case '/menu/create': - page = MenuListPage(BSettings.lastInstance.pageData); + page = MenuCreatePage(BSettings.lastInstance.pageData); break; case '/user/login': default: diff --git a/lib/flutter_bones.dart b/lib/flutter_bones.dart index f2760bf..18f3e84 100644 --- a/lib/flutter_bones.dart +++ b/lib/flutter_bones.dart @@ -2,12 +2,13 @@ /// /// Allows a model driven design of flutter apps: /// The design of the screens is done by a yaml file (or a compatible map). +export 'src/helper/helper_async.dart'; export 'src/helper/settings.dart'; export 'src/helper/string_helper.dart'; export 'src/helper/validators.dart'; -export 'src/helper/helper_async.dart'; export 'src/model/button_model.dart'; export 'src/model/checkbox_model.dart'; +export 'src/model/combo_base_model.dart'; export 'src/model/combobox_model.dart'; export 'src/model/empty_line_model.dart'; export 'src/model/field_model.dart'; @@ -17,22 +18,22 @@ export 'src/model/module_model.dart'; export 'src/model/page_model.dart'; export 'src/model/section_model.dart'; export 'src/model/standard/configuration_model.dart'; +export 'src/model/standard/menu_model.dart'; export 'src/model/standard/role_model.dart'; export 'src/model/standard/standard_modules.dart'; export 'src/model/standard/user_model.dart'; -export 'src/model/standard/menu_model.dart'; export 'src/model/text_field_model.dart'; export 'src/model/text_model.dart'; export 'src/model/widget_model.dart'; -export 'src/page/login_page.dart.01'; export 'src/page/application_data.dart'; +export 'src/page/configuration/configuration_create_page.dart'; +export 'src/page/login_page.dart.01'; export 'src/page/role/role_create_page.dart'; export 'src/page/user/user_create_page.dart'; -export 'src/page/configuration/configuration_create_page.dart'; export 'src/page/user_page.dart'; export 'src/persistence/persistence.dart'; -export 'src/persistence/rest_persistence.dart'; export 'src/persistence/persistence_cache.dart'; +export 'src/persistence/rest_persistence.dart'; export 'src/widget/raised_button_bone.dart'; export 'src/widget/simple_form.dart'; export 'src/widget/text_form_field_bone.dart'; diff --git a/lib/src/helper/string_helper.dart b/lib/src/helper/string_helper.dart index 484b308..ea9e5ee 100644 --- a/lib/src/helper/string_helper.dart +++ b/lib/src/helper/string_helper.dart @@ -128,6 +128,28 @@ class StringHelper { return rc; } + /// Replaces all placeholders defined by [syntaxPlaceholder] or [regExpPlaceholder] + /// in [source] and return this result. + /// [syntaxPlaceholder] or [regExpPlaceholder] must contain a single group + /// identifying the name of the placeholder. + /// Example: + /// replacePlaceholders('Hi ~user~', { "user": "joe"}, syntaxPlaceholder: r'~(\w+)~') + /// returns "Hi Joe". + static String replacePlaceholders( + String source, Map placeholders, + {String syntaxPlaceholder, RegExp regExpPlaceholder}) { + regExpPlaceholder ??= RegExp(syntaxPlaceholder); + final rc = + regExpPlaceholder.allMatches(source).fold(source, (string, match) { + final name = match.group(1); + final rc2 = placeholders.containsKey(name) + ? string.replaceFirst(match.group(0), placeholders[name]) + : string; + return rc2; + }); + return rc; + } + /// Splits a argument list into an [option] list and a true arguments list. /// [args]: the argument list to split. /// [options]: OUT: the options list diff --git a/lib/src/model/all_db_fields_model.dart b/lib/src/model/all_db_fields_model.dart index f1c6eb2..cc1530d 100644 --- a/lib/src/model/all_db_fields_model.dart +++ b/lib/src/model/all_db_fields_model.dart @@ -9,14 +9,12 @@ import 'widget_model.dart'; /// Describes a text widget without user interaction. class AllDbFieldsModel extends WidgetModel { static final regExprOptions = RegExp(r'^(unknown)$'); - List options; String text; bool isRichText; - final Map map; AllDbFieldsModel( - SectionModel section, PageModel page, this.map, BaseLogger logger) - : super(section, page, WidgetModelType.allDbFields, logger); + SectionModel section, PageModel page, BaseLogger logger) + : super(section, page, WidgetModelType.allDbFields, null, logger); /// Returns the name including the names of the parent @override @@ -28,12 +26,13 @@ class AllDbFieldsModel extends WidgetModel { return stringBuffer; } - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map] and stores the data in the instance. + void parse(Map map) { + super.parse(map); checkSuperfluousAttributes(map, 'options widgetType'.split(' ')); options = parseOptions('options', map); addFieldsOfTable(page.module.mainTable()); - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); } @override diff --git a/lib/src/model/button_model.dart b/lib/src/model/button_model.dart index 600f2c0..cfc10dd 100644 --- a/lib/src/model/button_model.dart +++ b/lib/src/model/button_model.dart @@ -10,14 +10,12 @@ class ButtonModel extends WidgetModel { String label; String name; String toolTip; - final Map map; - List options; ButtonModelType buttonModelType; VoidCallbackBones onPressed; /// Constructs an instance by parsing the map for some properties. - ButtonModel(SectionModel section, PageModel page, this.map, BaseLogger logger) - : super(section, page, WidgetModelType.button, logger); + ButtonModel(SectionModel section, PageModel page, BaseLogger logger) + : super(section, page, WidgetModelType.button, null, logger); /// Constructs an instance with all properties given. ButtonModel.direct( @@ -27,9 +25,8 @@ class ButtonModel extends WidgetModel { String text, ButtonModelType buttonModelType, List options, - this.map, BaseLogger logger) - : super(section, page, WidgetModelType.button, logger) { + : super(section, page, WidgetModelType.button, options, logger) { this.name = name; this.label = text; this.toolTip = toolTip; @@ -48,9 +45,10 @@ class ButtonModel extends WidgetModel { @override String fullName() => '${section?.name}.$name'; - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { name = parseString('name', map, required: true); + super.parse(map); checkSuperfluousAttributes(map, 'buttonType label name options text toolTip widgetType'.split(' ')); label = parseString('label', map, required: true); @@ -59,7 +57,7 @@ class ButtonModel extends WidgetModel { parseEnum('buttonType', map, ButtonModelType.values); buttonModelType ??= ButtonModelType.custom; options = parseOptions('options', map); - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); } @override diff --git a/lib/src/model/checkbox_model.dart b/lib/src/model/checkbox_model.dart index dd6f281..7d99b81 100644 --- a/lib/src/model/checkbox_model.dart +++ b/lib/src/model/checkbox_model.dart @@ -9,19 +9,16 @@ import 'widget_model.dart'; class CheckboxModel extends FieldModel { static final regExprOptions = RegExp(r'^(readonly|disabled|required|undef)$'); - final Map map; - CheckboxModel( - SectionModel section, PageModel page, this.map, BaseLogger logger) - : super(section, page, map, WidgetModelType.checkbox, logger); + SectionModel section, PageModel page, BaseLogger logger) + : super(section, page, WidgetModelType.checkbox, logger); - /// Parses the map and stores the data in the instance. - void parse() { - super.parse(); + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { + super.parse(map); checkSuperfluousAttributes( map, 'name label options toolTip widgetType'.split(' ')); - options = parseOptions('options', map); - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); parseFinish(); } diff --git a/lib/src/model/column_model.dart b/lib/src/model/column_model.dart index eb57c90..255ce95 100644 --- a/lib/src/model/column_model.dart +++ b/lib/src/model/column_model.dart @@ -20,8 +20,8 @@ class ColumnModel extends ComboBaseModel { String foreignKey; /// A constructor used in the parser. - ColumnModel(this.table, Map map, BaseLogger logger) - : super(null, null, map, WidgetModelType.column, logger); + ColumnModel(this.table, BaseLogger logger) + : super(null, null, WidgetModelType.column, logger); /// A constructor for columns created by the program. ColumnModel.direct( @@ -40,7 +40,6 @@ class ColumnModel extends ComboBaseModel { : super.direct( section: null, page: null, - map: null, name: name, label: label, toolTip: toolTip, @@ -64,20 +63,14 @@ class ColumnModel extends ComboBaseModel { @override String fullName() => '${table.name}.$name'; - /// Tests whether a given [option] is part of [options]. - bool hasOption(String option) { - bool rc = options?.contains(option) ?? false; - return rc; - } - - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { name = parseString('column', map, required: true); + super.parse(map); checkSuperfluousAttributes( map, 'column dataType defaultValue foreignKey label listOption listType options rows size texts tooTip validators validatorsText values widgetType' .split(' ')); - super.parse(); dataType = parseEnum('dataType', map, DataType.values, required: true); size = parseInt('size', map, required: dataType == DataType.string); @@ -88,7 +81,7 @@ class ColumnModel extends ComboBaseModel { logger.error( 'invalid foreign key in ${fullName()}: $foreignKey expected: ". " e.g. "role.role_id role_name"'); } - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); if (options.contains('primary')) { if (!options.contains('notnull')) { options.add('notnull'); @@ -112,8 +105,8 @@ class ColumnModel extends ComboBaseModel { logger.error( 'curious item (position $no) in column list of ${table.fullName()}: ${StringUtils.limitString("$item", 80)}'); } else { - final current = ColumnModel(table, item, logger); - current.parse(); + final current = ColumnModel(table, logger); + current.parse(item); table.addColumn(current); } } diff --git a/lib/src/model/combo_base_model.dart b/lib/src/model/combo_base_model.dart index 7e4880b..171474a 100644 --- a/lib/src/model/combo_base_model.dart +++ b/lib/src/model/combo_base_model.dart @@ -10,21 +10,21 @@ import 'widget_model.dart'; abstract class ComboBaseModel extends FieldModel { static final regExprListDbOption = RegExp(r'^\w+\.\w+\.\w+;\w+ \w+(?:;( ?:\w+=[^ ])*)?$'); - static final regExprListConfiguration = RegExp(r'^scope:\w+$'); + static final regExprListConfiguration = + RegExp(r'^\w+\.scope.\w+;\w+ \w+(?:;( ?:\w+=[^ ])*)?$'); List texts; List values; String listOption; ComboboxListType listType; ComboboxData data; - ComboBaseModel(SectionModel section, PageModel page, Map map, + ComboBaseModel(SectionModel section, PageModel page, WidgetModelType widgetType, BaseLogger logger) - : super(section, page, map, widgetType, logger); + : super(section, page, widgetType, logger); ComboBaseModel.direct( {SectionModel section, PageModel page, - Map map, String name, String label, String toolTip, @@ -37,7 +37,7 @@ abstract class ComboBaseModel extends FieldModel { WidgetModelType widgetType, dynamic defaultValue, BaseLogger logger}) - : super.direct(section, page, map, widgetType, name, label, toolTip, + : super.direct(section, page, widgetType, name, label, toolTip, dataType, options, defaultValue, logger); /// Transfers the list data from [texts] and [values] to [data]. @@ -52,9 +52,9 @@ abstract class ComboBaseModel extends FieldModel { } } - /// Parses the map and stores the data in the instance. - void parse() { - super.parse(); + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { + super.parse(map); texts = parseStringList('texts', map); listType = parseEnum('listType', map, ComboboxListType.values); @@ -106,12 +106,15 @@ abstract class ComboBaseModel extends FieldModel { } } +/// Stores the [texts] and [_values] of a combobox. class ComboboxData { final List texts; List _values; WaitState waitState; - ComboboxData(this.texts, this._values, [this.waitState = WaitState.undef]); + ComboboxData(this.texts, this._values, [this.waitState = WaitState.undef]){ + this._values ??= this.texts; + } /// Returns the length of [_values]. int get valuesLength => _values == null ? 0 : _values.length; diff --git a/lib/src/model/combobox_model.dart b/lib/src/model/combobox_model.dart index 23aa108..819dfe7 100644 --- a/lib/src/model/combobox_model.dart +++ b/lib/src/model/combobox_model.dart @@ -10,8 +10,8 @@ class ComboboxModel extends ComboBaseModel { static final regExprOptions = RegExp(r'^(disabled|readonly|required|undef)$'); ComboboxModel( - SectionModel section, PageModel page, Map map, BaseLogger logger) - : super(section, page, map, WidgetModelType.combobox, logger); + SectionModel section, PageModel page, BaseLogger logger) + : super(section, page, WidgetModelType.combobox, logger); /// Dumps the instance into a [StringBuffer] StringBuffer dump(StringBuffer stringBuffer) { @@ -20,14 +20,14 @@ class ComboboxModel extends ComboBaseModel { return stringBuffer; } - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { checkSuperfluousAttributes( map, 'dataType defaultValue filterType label listOption listType name options texts toolTip values validators validatorsText widgetType' .split(' ')); - super.parse(); - checkOptionsByRegExpr(options, regExprOptions); + super.parse(map); + checkOptionsByRegExpr(regExprOptions); parseFinish(); } } diff --git a/lib/src/model/db_reference_model.dart b/lib/src/model/db_reference_model.dart index 76a7efb..38b99d8 100644 --- a/lib/src/model/db_reference_model.dart +++ b/lib/src/model/db_reference_model.dart @@ -15,10 +15,10 @@ class DbReferenceModel extends ComboBaseModel { var value; ColumnModel column; - /// A constructor fetching the properties by parsing the [map]. + /// A constructor fetching the properties by parsing the map. DbReferenceModel( - SectionModel section, PageModel page, Map map, BaseLogger logger) - : super(section, page, map, WidgetModelType.dbReference, logger); + SectionModel section, PageModel page, BaseLogger logger) + : super(section, page, WidgetModelType.dbReference, logger); /// A constructor without map parsing. DbReferenceModel.fromColumn(SectionModel section, PageModel page, @@ -26,18 +26,19 @@ class DbReferenceModel extends ComboBaseModel { : super.direct( section: section, page: page, - map: null, widgetType: WidgetModelType.dbReference, name: column.name, label: column.label, toolTip: column.toolTip, dataType: column.dataType, - options: column.options ?? [], + options: [], texts: column.texts, values: column.values, listOption: column.listOption, listType: column.listType, logger: logger) { + options ??= []; + options.addAll(column.options ?? []); this.column = column; maxSize = column.size; rows = column.rows; @@ -57,13 +58,13 @@ class DbReferenceModel extends ComboBaseModel { return stringBuffer; } - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { checkSuperfluousAttributes( map, 'column filterType label maxSize listOption listType name options rows toolTip texts widgetType values' .split(' ')); - super.parse(); + super.parse(map); final columnName = parseString('column', map, required: true); column = page.module.getColumn(columnName, this); maxSize = parseInt('maxSize', map, required: false); @@ -78,7 +79,7 @@ class DbReferenceModel extends ComboBaseModel { values ??= column.values; listOption ??= column.listOption; listType ??= column.listType; - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); options += column.options; parseFinish(); } diff --git a/lib/src/model/empty_line_model.dart b/lib/src/model/empty_line_model.dart index f233c54..e133b70 100644 --- a/lib/src/model/empty_line_model.dart +++ b/lib/src/model/empty_line_model.dart @@ -7,13 +7,11 @@ import 'widget_model.dart'; /// Describes an empty line used as separator between two other widgets. class EmptyLineModel extends WidgetModel { static final regExprOptions = RegExp(r'^(unknown)$'); - List options; bool isRichText; - final Map map; EmptyLineModel( - SectionModel section, PageModel page, this.map, BaseLogger logger) - : super(section, page, WidgetModelType.emptyLine, logger); + SectionModel section, PageModel page, BaseLogger logger) + : super(section, page, WidgetModelType.emptyLine, null, logger); /// Dumps the instance into a [StringBuffer] StringBuffer dump(StringBuffer stringBuffer) { @@ -25,11 +23,11 @@ class EmptyLineModel extends WidgetModel { @override String fullName() => '${section.name}.${widgetName()}'; - /// Parses the map and stores the data in the instance. - void parse() { - options = parseOptions('options', map); + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { + super.parse(map); checkSuperfluousAttributes(map, 'options widgetType'.split(' ')); - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); } @override diff --git a/lib/src/model/field_model.dart b/lib/src/model/field_model.dart index db484f8..174c642 100644 --- a/lib/src/model/field_model.dart +++ b/lib/src/model/field_model.dart @@ -25,7 +25,6 @@ abstract class FieldModel extends WidgetModel { String label; String toolTip; DataType dataType; - List options; FilterType filterType; dynamic _value; dynamic defaultValue; @@ -45,26 +44,24 @@ abstract class FieldModel extends WidgetModel { /// key: validator name, e.g. 'regExpr' /// value: the error message final messageOfValidator = {}; - final Map map; - FieldModel(SectionModel section, PageModel page, this.map, + FieldModel(SectionModel section, PageModel page, WidgetModelType fieldModelType, BaseLogger logger) - : super(section, page, fieldModelType, logger); + : super(section, page, fieldModelType, null, logger); FieldModel.direct( SectionModel section, PageModel page, - this.map, WidgetModelType widgetModelType, this.name, this.label, this.toolTip, this.dataType, - this.options, this.defaultValue, + List options, BaseLogger logger, [this.filterType]) - : super(section, page, widgetModelType, logger); + : super(section, page, widgetModelType, options, logger); get value => _value; @@ -178,20 +175,12 @@ abstract class FieldModel extends WidgetModel { @override String fullName() => '${page.name}.$name'; - /// Tests whether a given [option] is part of [options]. - bool hasOption(String option) { - if (options == null) { - return false; - } - bool rc = options.contains(option); - return rc; - } - - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { name = parseString( widgetModelType == WidgetModelType.column ? 'column' : 'name', map, required: true); + super.parse(map); label = parseString('label', map, required: false); toolTip = parseString('toolTip', map, required: false); filterType = parseEnum('filterType', map, FilterType.values); diff --git a/lib/src/model/model_base.dart b/lib/src/model/model_base.dart index 947c039..1ada1d6 100644 --- a/lib/src/model/model_base.dart +++ b/lib/src/model/model_base.dart @@ -6,14 +6,19 @@ import 'model_types.dart'; /// Base class of all models. abstract class ModelBase { BaseLogger logger; + List options; - ModelBase(this.logger); + ModelBase(this.options, this.logger); + + void parse(Map map) { + options = parseOptions('options', map); + } /// Tests the validity of the entries in [options] comparing with an [regExpr]. /// Errors will be logged. - bool checkOptionsByRegExpr(List options, RegExp regExpr) { + bool checkOptionsByRegExpr(RegExp regExpr) { bool rc = false; - options.forEach((element) { + options?.forEach((element) { if (regExpr.firstMatch(element) == null) { logger.error('wrong option $element in ${fullName()}'); rc = true; @@ -22,6 +27,15 @@ abstract class ModelBase { return rc; } + /// Tests whether a given [option] is part of [options]. + bool hasOption(String option) { + if (options == null) { + return false; + } + bool rc = options.contains(option); + return rc; + } + /// Tests a [map] for superfluous [keys]. /// Keys of the [map] that are not listed in [keys] will be logged as errors. /// Keys that do not have the type String are logged as errors. @@ -111,7 +125,7 @@ abstract class ModelBase { /// Fetches an entry from a map addressed by a [key]. /// An error is logged if [required] is true and the map does not contain the key. List parseOptions(String key, Map map) { - List rc = []; + List rc; if (map.containsKey(key)) { final value = map[key]; if (value.runtimeType == String && value.isNotEmpty) { @@ -121,7 +135,7 @@ abstract class ModelBase { 'wrong datatype of options ${value.runtimeType} in ${fullName()}'); } } - return rc; + return rc ?? []; } /// Fetches an entry from a map addressed by a [key]. diff --git a/lib/src/model/module_model.dart b/lib/src/model/module_model.dart index a23e9a8..e283b45 100644 --- a/lib/src/model/module_model.dart +++ b/lib/src/model/module_model.dart @@ -18,12 +18,11 @@ class ModuleModel extends ModelBase { static final regExprOptions = RegExp(r'^(noPages)$'); final Map map; String name; - List options; @protected final pages = []; final tables = []; - ModuleModel(this.map, BaseLogger logger) : super(logger); + ModuleModel(this.map, BaseLogger logger) : super(null, logger); /// Adds the column [name] from [source] to [target] if that column is not member of [target]. void addColumnIfMissing( @@ -319,9 +318,6 @@ modules: return rc; } - /// Tests whether an options named 'name' is set. - bool hasOption(String name) => options != null && options.contains(name); - /// Returns the main table of the module. /// This is the first defined table. TableModel mainTable() { @@ -336,14 +332,15 @@ modules: return found; } - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map]and stores the data in the instance. + void parse([Map map]) { + map ??= this.map; name = parseString('module', map, required: true); + super.parse(map); checkSuperfluousAttributes(map, 'module options pages tables'.split(' ')); if (map.containsKey('tables')) { TableModel.parseList(this, map['tables'], logger); } - options = parseOptions('options', map); if (!hasOption('noPages')) { if (!map.containsKey('pages')) { logger.error('Module $name: missing pages'); @@ -357,7 +354,7 @@ modules: } } } - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); } /// Returns a child table given by [name], null otherwise. diff --git a/lib/src/model/page_model.dart b/lib/src/model/page_model.dart index 956bd33..20ceed6 100644 --- a/lib/src/model/page_model.dart +++ b/lib/src/model/page_model.dart @@ -13,11 +13,9 @@ typedef FilterWidget = bool Function(WidgetModel item); class PageModel extends ModelBase { static final regExprOptions = RegExp(r'^(noAutoButton)$'); final ModuleModel module; - final Map map; String name; final List sections = []; PageModelType pageModelType; - List options; final fields = []; final buttons = []; final widgets = []; @@ -25,7 +23,7 @@ class PageModel extends ModelBase { List tableTitles; List tableColumns; - PageModel(this.module, this.map, BaseLogger logger) : super(logger); + PageModel(this.module, BaseLogger logger) : super(null, logger); /// Adds a [button] to the [this.buttons]. /// If already defined and error is logged. @@ -106,8 +104,8 @@ class PageModel extends ModelBase { return rc; } - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { name = parseString('page', map, required: true); checkSuperfluousAttributes( map, @@ -150,25 +148,25 @@ class PageModel extends ModelBase { case PageModelType.list: if (buttonByName('search', required: false) == null) { addButton(ButtonModel.direct(section, this, 'search', 'Suchen', - ButtonModelType.search, [], null, logger)); + ButtonModelType.search, [], logger)); } break; case PageModelType.create: case PageModelType.change: if (buttonByName('cancel', required: false) == null) { addButton(ButtonModel.direct(section, this, 'cancel', 'Abbruch', - ButtonModelType.cancel, [], null, logger)); + ButtonModelType.cancel, [], logger)); } if (buttonByName('store', required: false) == null) { addButton(ButtonModel.direct(section, this, 'store', 'Speichern', - ButtonModelType.store, [], null, logger)); + ButtonModelType.store, [], logger)); } break; default: break; } } - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); } @override @@ -183,9 +181,9 @@ class PageModel extends ModelBase { module.logger.error( 'curious item in section list of ${module.fullName()}: ${StringUtils.limitString("$item", 80)}'); } else { - final page = PageModel(module, item, logger); + final page = PageModel(module, logger); // we need a name before adding to module - page.parse(); + page.parse(item); module.addPage(page); } } diff --git a/lib/src/model/section_model.dart b/lib/src/model/section_model.dart index cf52506..35b5430 100644 --- a/lib/src/model/section_model.dart +++ b/lib/src/model/section_model.dart @@ -19,13 +19,11 @@ class SectionModel extends WidgetModel { String name; final children = []; final buttons = []; - List options; final int no; - final Map map; - SectionModel(this.no, PageModel page, SectionModel section, this.map, + SectionModel(this.no, PageModel page, SectionModel section, BaseLogger logger) - : super(section, page, WidgetModelType.section, logger); + : super(section, page, WidgetModelType.section, null, logger); /// Dumps the internal structure into a [stringBuffer] StringBuffer dump(StringBuffer stringBuffer) { @@ -43,8 +41,8 @@ class SectionModel extends WidgetModel { String fullName() => section == null ? '${page.name}.$name' : '${section.name}.$name'; - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { sectionModelType = parseEnum( 'sectionType', map, SectionModelType.values); name = parseString('name', map) ?? @@ -53,7 +51,7 @@ class SectionModel extends WidgetModel { map, 'children fields name options sectionType widgetType'.split(' ')); options = parseOptions('options', map); - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); if (!map.containsKey('children')) { logger.error('missing children in ${fullName()}'); } else { @@ -80,28 +78,28 @@ class SectionModel extends WidgetModel { WidgetModel widget; switch (widgetType) { case WidgetModelType.allDbFields: - widget = AllDbFieldsModel(this, page, child, logger); + widget = AllDbFieldsModel(this, page, logger); break; case WidgetModelType.checkbox: - widget = CheckboxModel(this, page, child, logger); + widget = CheckboxModel(this, page, logger); break; case WidgetModelType.combobox: - widget = ComboboxModel(this, page, child, logger); + widget = ComboboxModel(this, page, logger); break; case WidgetModelType.textField: - widget = TextFieldModel(this, page, child, logger); + widget = TextFieldModel(this, page, logger); break; case WidgetModelType.button: - widget = ButtonModel(this, page, child, logger); + widget = ButtonModel(this, page, logger); break; case WidgetModelType.text: - widget = TextModel(this, page, child, logger); + widget = TextModel(this, page, logger); break; case WidgetModelType.emptyLine: - widget = EmptyLineModel(this, page, child, logger); + widget = EmptyLineModel(this, page, logger); break; case WidgetModelType.dbReference: - widget = DbReferenceModel(this, page, child, logger); + widget = DbReferenceModel(this, page, logger); break; default: //@ToDo: nested section @@ -111,7 +109,7 @@ class SectionModel extends WidgetModel { } if (widget != null) { children.add(widget); - widget.parse(); + widget.parse(child); page.widgets.add(widget); switch (widget.widgetModelType) { case WidgetModelType.button: @@ -145,8 +143,8 @@ class SectionModel extends WidgetModel { page.logger.error( 'curious item in section list of ${page.fullName()}: ${StringUtils.limitString("$item", 80)}'); } else { - final current = SectionModel(no, page, null, item, logger); - current.parse(); + final current = SectionModel(no, page, null, logger); + current.parse(item); if (section != null) { section.children.add(current); } else { diff --git a/lib/src/model/standard/configuration_model.dart b/lib/src/model/standard/configuration_model.dart index 4958f09..910a25b 100644 --- a/lib/src/model/standard/configuration_model.dart +++ b/lib/src/model/standard/configuration_model.dart @@ -25,6 +25,8 @@ class ConfigurationModel extends ModuleModel { 'label': 'Bereich', 'size': 64, 'options': 'notnull', + 'validators': r'required regExpr=i/^\w+$', + 'validatorsText': 'regExpr=Nur Buchstaben, Ziffern und Unterstrich erlaubt', }, { 'column': 'configuration_property', @@ -42,6 +44,9 @@ class ConfigurationModel extends ModuleModel { 'dataType': 'string', 'size': 16, 'label': 'Datentyp', + 'listType': 'explicite', + 'texts': ';bool;date;float;int;string', + 'validators': 'required', }, { 'column': 'configuration_value', diff --git a/lib/src/model/standard/menu_model.dart b/lib/src/model/standard/menu_model.dart index 21b321b..53eed8a 100644 --- a/lib/src/model/standard/menu_model.dart +++ b/lib/src/model/standard/menu_model.dart @@ -26,10 +26,10 @@ class MenuModel extends ModuleModel { 'column': 'menu_icon', 'dataType': 'reference', 'label': 'Bild', - 'foreignKey': - 'configuration.configuration_id configuration_property', + 'foreignKey': 'configuration.configuration_id configuration_value', 'listType': 'configuration', - 'listOption': 'scope:icons', + 'listOption': + 'std.scope.icons;configuration_value configuration_id;', }, ] }, diff --git a/lib/src/model/standard/role_model.dart b/lib/src/model/standard/role_model.dart index 87c0ad3..24e7179 100644 --- a/lib/src/model/standard/role_model.dart +++ b/lib/src/model/standard/role_model.dart @@ -26,6 +26,7 @@ class RoleModel extends ModuleModel { 'column': 'role_priority', 'dataType': 'int', 'label': 'Priorität', + 'validators': 'required minInt=1 maxInt=99', }, { 'column': 'role_active', diff --git a/lib/src/model/standard/user_model.dart b/lib/src/model/standard/user_model.dart index 3b04a94..0dc4c62 100644 --- a/lib/src/model/standard/user_model.dart +++ b/lib/src/model/standard/user_model.dart @@ -100,6 +100,11 @@ class UserModel extends ModuleModel { { "sectionType": "simpleForm", "children": [ + { + "widgetType": "text", + "text": "Ändern des Passworts von Benutzer ~user~", + "options": "placeholder" + }, { "widgetType": "textField", "name": "user_password", diff --git a/lib/src/model/table_model.dart b/lib/src/model/table_model.dart index c592f8b..83ebf83 100644 --- a/lib/src/model/table_model.dart +++ b/lib/src/model/table_model.dart @@ -9,13 +9,11 @@ import 'module_model.dart'; class TableModel extends ModelBase { static final regExprOptions = RegExp(r'^(unknown)$'); final ModuleModel module; - final Map map; String name; - List options; final columns = []; ColumnModel primary; - TableModel(this.module, this.map, BaseLogger logger) : super(logger); + TableModel(this.module, BaseLogger logger) : super(null, logger); /// Adds a [button] to the [this.buttons]. /// If already defined and error is logged. @@ -69,8 +67,8 @@ class TableModel extends ModelBase { @override String fullName() => '${module.name}.$name'; - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { name = parseString('table', map, required: true); checkSuperfluousAttributes(map, 'columns options table'.split(' ')); options = parseOptions('options', map); @@ -85,7 +83,7 @@ class TableModel extends ModelBase { ColumnModel.parseColumns(this, item, logger); } } - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); addIfMissing( '${name}_createdat', 'Erzeugt', DataType.dateTime, ['hidden', 'null']); addIfMissing( @@ -108,8 +106,8 @@ class TableModel extends ModelBase { module.logger.error( 'curious item in section list of ${module.fullName()}: ${StringUtils.limitString("$item", 80)}'); } else { - final table = TableModel(module, item, logger); - table.parse(); + final table = TableModel(module, logger); + table.parse(item); module.addTable(table); } } diff --git a/lib/src/model/text_field_model.dart b/lib/src/model/text_field_model.dart index 627bd38..8946e88 100644 --- a/lib/src/model/text_field_model.dart +++ b/lib/src/model/text_field_model.dart @@ -15,13 +15,12 @@ class TextFieldModel extends FieldModel { var value; TextFieldModel( - SectionModel section, PageModel page, Map map, BaseLogger logger) - : super(section, page, map, WidgetModelType.textField, logger); + SectionModel section, PageModel page, BaseLogger logger) + : super(section, page, WidgetModelType.textField, logger); TextFieldModel.direct( SectionModel section, PageModel page, - Map map, String name, String label, String toolTip, @@ -29,7 +28,7 @@ class TextFieldModel extends FieldModel { List options, dynamic defaultValue, BaseLogger logger) - : super.direct(section, page, null, WidgetModelType.textField, name, + : super.direct(section, page, WidgetModelType.textField, name, label, toolTip, dataType, options, defaultValue, logger); /// Dumps the internal structure into a [stringBuffer] @@ -39,9 +38,9 @@ class TextFieldModel extends FieldModel { return stringBuffer; } - /// Parses the map and stores the data in the instance. - void parse() { - super.parse(); + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { + super.parse(map); checkSuperfluousAttributes( map, 'dataType filterType label maxSize name options rows toolTip validators validatorsText value widgetType' @@ -58,7 +57,7 @@ class TextFieldModel extends FieldModel { break; } options = parseOptions('options', map); - checkOptionsByRegExpr(options, regExprOptions); + checkOptionsByRegExpr(regExprOptions); parseFinish(); } } diff --git a/lib/src/model/text_model.dart b/lib/src/model/text_model.dart index 55ec29c..59994c2 100644 --- a/lib/src/model/text_model.dart +++ b/lib/src/model/text_model.dart @@ -5,14 +5,12 @@ import 'widget_model.dart'; /// Describes a text widget without user interaction. class TextModel extends WidgetModel { - static final regExprOptions = RegExp(r'^(rich)$'); - List options; + static final regExprOptions = RegExp(r'^(richText|placeholder|h[1-5])$'); String text; bool isRichText; - final Map map; - TextModel(SectionModel section, PageModel page, this.map, BaseLogger logger) - : super(section, page, WidgetModelType.text, logger); + TextModel(SectionModel section, PageModel page, BaseLogger logger) + : super(section, page, WidgetModelType.text, null, logger); /// Returns the name including the names of the parent @override @@ -25,13 +23,13 @@ class TextModel extends WidgetModel { return stringBuffer; } - /// Parses the map and stores the data in the instance. - void parse() { + /// Parses the [map]and stores the data in the instance. + void parse(Map map) { checkSuperfluousAttributes(map, 'options text widgetType'.split(' ')); text = parseString('text', map, required: true); options = parseOptions('options', map); - checkOptionsByRegExpr(options, regExprOptions); - isRichText = options.contains('richtext'); + checkOptionsByRegExpr(regExprOptions); + isRichText = options.contains('richText'); } @override diff --git a/lib/src/model/widget_model.dart b/lib/src/model/widget_model.dart index 3db5f11..a7d4b02 100644 --- a/lib/src/model/widget_model.dart +++ b/lib/src/model/widget_model.dart @@ -13,8 +13,9 @@ abstract class WidgetModel extends ModelBase { final WidgetModelType widgetModelType; int id; - WidgetModel(this.section, this.page, this.widgetModelType, BaseLogger logger) - : super(logger) { + WidgetModel(this.section, this.page, this.widgetModelType, + List options, BaseLogger logger) + : super(options, logger) { this.id = ++lastId; } @@ -28,7 +29,9 @@ abstract class WidgetModel extends ModelBase { /// Dumps the internal structure into a [stringBuffer] StringBuffer dump(StringBuffer stringBuffer); - void parse(); + void parse(Map map){ + super.parse(map); + } String widgetName(); } diff --git a/lib/src/page/configuration/configuration_change_page.dart b/lib/src/page/configuration/configuration_change_page.dart index 865e1c2..1465cfb 100644 --- a/lib/src/page/configuration/configuration_change_page.dart +++ b/lib/src/page/configuration/configuration_change_page.dart @@ -29,7 +29,7 @@ class ConfigurationChangePage extends StatefulWidget { } } -class ConfigurationChangePageState extends State { +class ConfigurationChangePageState extends State implements RedrawPage { final ApplicationData applicationData; final int primaryId; final Map initialRow; @@ -43,24 +43,6 @@ class ConfigurationChangePageState extends State { @override Widget build(BuildContext context) { - if (controller == null) { - controller = ConfigurationController( - _formKey, this, 'change', context, applicationData, redrawCallback: - (RedrawReason reason, - {String customString, - RedrawCallbackFunctionSimple callback}) { - switch (reason) { - case RedrawReason.callback: - callback(RedrawReason.custom, customString); - setState(() {}); - break; - default: - setState(() {}); - break; - } - }); - controller.initialize(); - } // controller.buildWidgetList() is called in editForm return Scaffold( appBar: applicationData.appBarBuilder('Konfiguration ändern'), @@ -74,8 +56,22 @@ class ConfigurationChangePageState extends State { )); } + @override + void initState() { + super.initState(); + controller = ConfigurationController( + _formKey, this, 'change', context, applicationData); + controller.initialize(); + } void dispose() { controller.dispose(); super.dispose(); } + + @override + void redraw(RedrawReason reason, {String customString, RedrawCallbackFunctionSimple callback}) { + setState( () { + controller.afterSetState(reason, customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/configuration/configuration_controller.dart b/lib/src/page/configuration/configuration_controller.dart index cc579c2..5fc1f61 100644 --- a/lib/src/page/configuration/configuration_controller.dart +++ b/lib/src/page/configuration/configuration_controller.dart @@ -10,7 +10,7 @@ class ConfigurationController extends PageControllerBones { /// Controller for a page named [pageName]. ConfigurationController( GlobalKey formKey, - State parent, + RedrawPage parent, String pageName, BuildContext context, ApplicationData applicationData, diff --git a/lib/src/page/configuration/configuration_create_page.dart b/lib/src/page/configuration/configuration_create_page.dart index a6f8797..cb43aec 100644 --- a/lib/src/page/configuration/configuration_create_page.dart +++ b/lib/src/page/configuration/configuration_create_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; - import '../../helper/settings.dart'; import '../../widget/edit_form.dart'; +import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'configuration_controller.dart'; @@ -21,7 +21,7 @@ class ConfigurationCreatePage extends StatefulWidget { } } -class ConfigurationCreatePageState extends State { +class ConfigurationCreatePageState extends State implements RedrawPage { final ApplicationData applicationData; final GlobalKey _formKey = @@ -33,12 +33,7 @@ class ConfigurationCreatePageState extends State { @override Widget build(BuildContext context) { - if (controller == null) { - controller = ConfigurationController( - _formKey, this, 'create', context, applicationData); - controller.initialize(); - } - controller.buildWidgetList(); + controller.beginOfBuild(context); return Scaffold( appBar: applicationData.appBarBuilder('Neue Konfiguration'), drawer: applicationData.drawerBuilder(context), @@ -48,4 +43,18 @@ class ConfigurationCreatePageState extends State { configuration: applicationData.configuration, )); } + + @override + void initState() { + super.initState(); + controller = ConfigurationController( + _formKey, this, 'create', context, applicationData); + controller.initialize(); + } + @override + void redraw(RedrawReason reason, {String customString, RedrawCallbackFunctionSimple callback}) { + setState( () { + controller.afterSetState(reason, customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/configuration/configuration_list_page.dart b/lib/src/page/configuration/configuration_list_page.dart index dcc9c70..b71130a 100644 --- a/lib/src/page/configuration/configuration_list_page.dart +++ b/lib/src/page/configuration/configuration_list_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; - +import '../../model/page_model.dart'; import '../../widget/list_form.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; @@ -21,7 +21,8 @@ class ConfigurationListPage extends StatefulWidget { } } -class ConfigurationListPageState extends State { +class ConfigurationListPageState extends State + implements RedrawPage { final ApplicationData applicationData; final GlobalKey _formKey = @@ -31,35 +32,9 @@ class ConfigurationListPageState extends State { ConfigurationListPageState(this.applicationData); - @override - void initState() { - super.initState(); - controller = ConfigurationController( - _formKey, this, 'list', context, applicationData, redrawCallback: - (RedrawReason reason, - {String customString, RedrawCallbackFunctionSimple callback}) { - switch (reason) { - case RedrawReason.fetchList: - controller.buildRows(); - break; - case RedrawReason.callback: - callback(RedrawReason.custom, customString); - setState(() {}); - break; - default: - setState(() {}); - break; - } - }); - controller.initialize(); - controller.buildWidgetList(); - controller.buildRows(); - controller.completeAsync(); - } - @override Widget build(BuildContext context) { - controller.buildHandler(context); + controller.beginOfBuild(context); return Scaffold( appBar: applicationData.appBarBuilder('Konfigurationen'), drawer: applicationData.drawerBuilder(context), @@ -77,10 +52,7 @@ class ConfigurationListPageState extends State { RaisedButton( child: Text('Neue Konfiguration'), onPressed: () { - Navigator.pushNamed(context, '/configuration/create'); - - /// Force the redraw on return: - controller.listRows = null; + controller.goTo(pageType: PageModelType.create); }, ), ]), @@ -91,4 +63,36 @@ class ConfigurationListPageState extends State { ), ); } + + @override + void initState() { + super.initState(); + controller = ConfigurationController( + _formKey, this, 'list', context, applicationData, redrawCallback: + (RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + switch (reason) { + case RedrawReason.fetchList: + controller.buildRows(); + break; + case RedrawReason.callback: + callback(RedrawReason.custom, customString: customString); + setState(() {}); + break; + default: + setState(() {}); + break; + } + }); + controller.initialize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/menu/menu_change_page.dart b/lib/src/page/menu/menu_change_page.dart index 5127b13..b259137 100644 --- a/lib/src/page/menu/menu_change_page.dart +++ b/lib/src/page/menu/menu_change_page.dart @@ -28,7 +28,7 @@ class MenuChangePage extends StatefulWidget { } } -class MenuChangePageState extends State { +class MenuChangePageState extends State implements RedrawPage { final ApplicationData applicationData; final int primaryId; final Map initialRow; @@ -41,24 +41,7 @@ class MenuChangePageState extends State { @override Widget build(BuildContext context) { - if (controller == null) { - controller = - MenuController(_formKey, this, 'change', context, applicationData, - redrawCallback: (RedrawReason reason, - {String customString, - RedrawCallbackFunctionSimple callback}) { - switch (reason) { - case RedrawReason.callback: - callback(RedrawReason.custom, customString); - setState(() {}); - break; - default: - setState(() {}); - break; - } - }); - controller.initialize(); - } + controller.beginOfBuild(context); // controller.buildWidgetList() is called in editForm return Scaffold( appBar: applicationData.appBarBuilder('Startmenü ändern'), @@ -76,4 +59,26 @@ class MenuChangePageState extends State { controller.dispose(); super.dispose(); } + + @override + void initState() { + super.initState(); + controller = MenuController( + _formKey, + this, + 'change', + context, + applicationData, + ); + controller.initialize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/menu/menu_controller.dart b/lib/src/page/menu/menu_controller.dart index 581143a..857f0af 100644 --- a/lib/src/page/menu/menu_controller.dart +++ b/lib/src/page/menu/menu_controller.dart @@ -8,7 +8,7 @@ import 'menu_change_page.dart'; class MenuController extends PageControllerBones { /// Controller for a page named [pageName]. - MenuController(GlobalKey formKey, State parent, + MenuController(GlobalKey formKey, RedrawPage parent, String pageName, BuildContext context, ApplicationData applicationData, {Function redrawCallback}) : super(formKey, parent, MenuModel(Settings().logger), pageName, context, diff --git a/lib/src/page/menu/menu_create_page.dart b/lib/src/page/menu/menu_create_page.dart index d7acb01..f9cbcd0 100644 --- a/lib/src/page/menu/menu_create_page.dart +++ b/lib/src/page/menu/menu_create_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; import '../../widget/edit_form.dart'; +import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'menu_controller.dart'; @@ -21,7 +22,7 @@ class MenuCreatePage extends StatefulWidget { } } -class MenuCreatePageState extends State { +class MenuCreatePageState extends State implements RedrawPage { final ApplicationData applicationData; final GlobalKey _formKey = @@ -33,12 +34,7 @@ class MenuCreatePageState extends State { @override Widget build(BuildContext context) { - if (controller == null) { - controller = - MenuController(_formKey, this, 'create', context, applicationData); - controller.initialize(); - } - controller.buildWidgetList(); + controller.beginOfBuild(context); return Scaffold( appBar: applicationData.appBarBuilder('Neues Startmenü'), drawer: applicationData.drawerBuilder(context), @@ -48,4 +44,21 @@ class MenuCreatePageState extends State { configuration: applicationData.configuration, )); } + + @override + void initState() { + super.initState(); + controller = + MenuController(_formKey, this, 'create', context, applicationData); + controller.initialize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/menu/menu_list_page.dart b/lib/src/page/menu/menu_list_page.dart index 5a0b1a7..2caa054 100644 --- a/lib/src/page/menu/menu_list_page.dart +++ b/lib/src/page/menu/menu_list_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; - +import '../../model/page_model.dart'; import '../../widget/list_form.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; @@ -21,7 +21,7 @@ class MenuListPage extends StatefulWidget { } } -class MenuListPageState extends State { +class MenuListPageState extends State implements RedrawPage { final ApplicationData applicationData; final GlobalKey _formKey = @@ -31,35 +31,9 @@ class MenuListPageState extends State { MenuListPageState(this.applicationData); - @override - void initState() { - super.initState(); - controller = - MenuController(_formKey, this, 'list', context, applicationData, - redrawCallback: (RedrawReason reason, - {String customString, RedrawCallbackFunctionSimple callback}) { - switch (reason) { - case RedrawReason.fetchList: - controller.buildRows(); - break; - case RedrawReason.callback: - callback(RedrawReason.custom, customString); - setState(() {}); - break; - default: - setState(() {}); - break; - } - }); - controller.initialize(); - controller.buildWidgetList(); - controller.buildRows(); - controller.completeAsync(); - } - @override Widget build(BuildContext context) { - controller.buildHandler(context); + controller.beginOfBuild(context); return Scaffold( appBar: applicationData.appBarBuilder('Startmenüs'), drawer: applicationData.drawerBuilder(context), @@ -77,10 +51,7 @@ class MenuListPageState extends State { RaisedButton( child: Text('Neues Startmenü'), onPressed: () { - Navigator.pushNamed(context, '/menu/create'); - - /// Force the redraw on return: - controller.listRows = null; + controller.goTo(pageType: PageModelType.create); }, ), ]), @@ -91,4 +62,21 @@ class MenuListPageState extends State { ), ); } + + @override + void initState() { + super.initState(); + controller = + MenuController(_formKey, this, 'list', context, applicationData); + controller.initialize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/role/role_change_page.dart b/lib/src/page/role/role_change_page.dart index 3368f04..b05b475 100644 --- a/lib/src/page/role/role_change_page.dart +++ b/lib/src/page/role/role_change_page.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; - import '../../helper/settings.dart'; import '../../widget/edit_form.dart'; import '../../widget/page_controller_bones.dart'; @@ -28,7 +27,7 @@ class RoleChangePage extends StatefulWidget { } } -class RoleChangePageState extends State { +class RoleChangePageState extends State implements RedrawPage{ final ApplicationData applicationData; final int primaryId; final Map initialRow; @@ -41,25 +40,7 @@ class RoleChangePageState extends State { @override Widget build(BuildContext context) { - if (controller == null) { - controller = - RoleController(_formKey, this, 'change', context, applicationData, - redrawCallback: (RedrawReason reason, - {String customString, - RedrawCallbackFunctionSimple callback}) { - switch (reason) { - case RedrawReason.callback: - callback(RedrawReason.custom, customString); - setState(() {}); - break; - default: - setState(() {}); - break; - } - }); - controller.initialize(); - } - // controller.buildWidgetList() is called in editForm + controller.beginOfBuild(context); return Scaffold( appBar: applicationData.appBarBuilder('Rolle ändern'), drawer: applicationData.drawerBuilder(context), @@ -71,7 +52,22 @@ class RoleChangePageState extends State { initialRow: initialRow, )); } + @override + void initState() { + super.initState(); + controller = RoleController( + _formKey, this, 'change', context, applicationData); + controller.initialize(); + } + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } void dispose() { controller.dispose(); super.dispose(); diff --git a/lib/src/page/role/role_controller.dart b/lib/src/page/role/role_controller.dart index 13a7406..0ccf112 100644 --- a/lib/src/page/role/role_controller.dart +++ b/lib/src/page/role/role_controller.dart @@ -8,7 +8,7 @@ import 'role_change_page.dart'; class RoleController extends PageControllerBones { /// Controller for a page named [pageName]. - RoleController(GlobalKey formKey, State parent, + RoleController(GlobalKey formKey, RedrawPage parent, String pageName, BuildContext context, ApplicationData applicationData, {Function redrawCallback}) : super(formKey, parent, RoleModel(Settings().logger), pageName, context, diff --git a/lib/src/page/role/role_create_page.dart b/lib/src/page/role/role_create_page.dart index dc67076..3d0cf8d 100644 --- a/lib/src/page/role/role_create_page.dart +++ b/lib/src/page/role/role_create_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; import '../../widget/edit_form.dart'; +import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'role_controller.dart'; @@ -21,7 +22,7 @@ class RoleCreatePage extends StatefulWidget { } } -class RoleCreatePageState extends State { +class RoleCreatePageState extends State implements RedrawPage{ final ApplicationData applicationData; final GlobalKey _formKey = @@ -33,12 +34,7 @@ class RoleCreatePageState extends State { @override Widget build(BuildContext context) { - if (controller == null) { - controller = - RoleController(_formKey, this, 'create', context, applicationData); - controller.initialize(); - } - controller.buildWidgetList(); + controller.beginOfBuild(context); return Scaffold( appBar: applicationData.appBarBuilder('Neue Rolle'), drawer: applicationData.drawerBuilder(context), @@ -48,4 +44,20 @@ class RoleCreatePageState extends State { configuration: applicationData.configuration, )); } + @override + void initState() { + super.initState(); + controller = RoleController( + _formKey, this, 'create', context, applicationData); + controller.initialize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/role/role_list_page.dart b/lib/src/page/role/role_list_page.dart index b373ba4..6e53d7d 100644 --- a/lib/src/page/role/role_list_page.dart +++ b/lib/src/page/role/role_list_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; - +import '../../model/page_model.dart'; import '../../widget/list_form.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; @@ -21,7 +21,7 @@ class RoleListPage extends StatefulWidget { } } -class RoleListPageState extends State { +class RoleListPageState extends State implements RedrawPage{ final ApplicationData applicationData; final GlobalKey _formKey = @@ -31,35 +31,9 @@ class RoleListPageState extends State { RoleListPageState(this.applicationData); - @override - void initState() { - super.initState(); - controller = - RoleController(_formKey, this, 'list', context, applicationData, - redrawCallback: (RedrawReason reason, - {String customString, RedrawCallbackFunctionSimple callback}) { - switch (reason) { - case RedrawReason.fetchList: - controller.buildRows(); - break; - case RedrawReason.callback: - callback(RedrawReason.custom, customString); - setState(() {}); - break; - default: - setState(() {}); - break; - } - }); - controller.initialize(); - controller.buildWidgetList(); - controller.buildRows(); - controller.completeAsync(); - } - @override Widget build(BuildContext context) { - controller.buildHandler(context); + controller.beginOfBuild(context); return Scaffold( appBar: applicationData.appBarBuilder('Rollen'), drawer: applicationData.drawerBuilder(context), @@ -77,10 +51,7 @@ class RoleListPageState extends State { RaisedButton( child: Text('Neue Rolle'), onPressed: () { - Navigator.pushNamed(context, '/role/create'); - - /// Force the redraw on return: - controller.listRows = null; + controller.goTo(pageType: PageModelType.create); }, ), ]), @@ -91,4 +62,21 @@ class RoleListPageState extends State { ), ); } + + @override + void initState() { + super.initState(); + controller = + RoleController(_formKey, this, 'list', context, applicationData); + controller.initialize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/user/user_change_page.dart b/lib/src/page/user/user_change_page.dart index 61471aa..3409db8 100644 --- a/lib/src/page/user/user_change_page.dart +++ b/lib/src/page/user/user_change_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; + import '../../helper/settings.dart'; import '../../model/button_model.dart'; import '../../widget/edit_form.dart'; @@ -29,7 +30,7 @@ class UserChangePage extends StatefulWidget { } } -class UserChangePageState extends State { +class UserChangePageState extends State implements RedrawPage { final ApplicationData applicationData; final int primaryId; final Map initialRow; @@ -42,26 +43,7 @@ class UserChangePageState extends State { @override Widget build(BuildContext context) { - if (controller == null) { - controller = - UserController(_formKey, this, 'change', context, applicationData, - redrawCallback: (RedrawReason reason, - {String customString, - RedrawCallbackFunctionSimple callback}) { - switch (reason) { - case RedrawReason.callback: - callback(RedrawReason.custom, customString); - setState(() {}); - break; - default: - setState(() {}); - break; - } - }); - controller.initialize(); - customize(); - } - // controller.buildWidgetList() is called in editForm + controller.beginOfBuild(context); return Scaffold( appBar: applicationData.appBarBuilder('Benutzer ändern'), drawer: applicationData.drawerBuilder(context), @@ -91,4 +73,22 @@ class UserChangePageState extends State { controller.dispose(); super.dispose(); } + + @override + void initState() { + super.initState(); + controller = + UserController(_formKey, this, 'change', context, applicationData); + controller.initialize(); + customize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/user/user_controller.dart b/lib/src/page/user/user_controller.dart index 30d72db..91d75e1 100644 --- a/lib/src/page/user/user_controller.dart +++ b/lib/src/page/user/user_controller.dart @@ -8,7 +8,7 @@ import 'user_change_page.dart'; class UserController extends PageControllerBones { /// Controller for a page named [pageName]. - UserController(GlobalKey formKey, State parent, + UserController(GlobalKey formKey, RedrawPage parent, String pageName, BuildContext context, ApplicationData applicationData, {Function redrawCallback}) : super(formKey, parent, UserModel(Settings().logger), pageName, context, @@ -23,4 +23,5 @@ class UserController extends PageControllerBones { MaterialPageRoute( builder: (context) => UserChangePage(id, applicationData, row))); } + } diff --git a/lib/src/page/user/user_create_page.dart b/lib/src/page/user/user_create_page.dart index c4d34f9..cb100c9 100644 --- a/lib/src/page/user/user_create_page.dart +++ b/lib/src/page/user/user_create_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; import '../../widget/edit_form.dart'; +import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'user_controller.dart'; @@ -21,7 +22,7 @@ class UserCreatePage extends StatefulWidget { } } -class UserCreatePageState extends State { +class UserCreatePageState extends State implements RedrawPage { final ApplicationData applicationData; final GlobalKey _formKey = @@ -33,14 +34,9 @@ class UserCreatePageState extends State { @override Widget build(BuildContext context) { - if (controller == null) { - controller = - UserController(_formKey, this, 'create', context, applicationData); - controller.initialize(); - } - controller.buildWidgetList(); + controller.beginOfBuild(context); return Scaffold( - appBar: applicationData.appBarBuilder('Neue Rolle'), + appBar: applicationData.appBarBuilder('Neuer Benutzer'), drawer: applicationData.drawerBuilder(context), body: EditForm.editForm( key: _formKey, @@ -48,4 +44,21 @@ class UserCreatePageState extends State { configuration: applicationData.configuration, )); } + + @override + void initState() { + super.initState(); + controller = + UserController(_formKey, this, 'create', context, applicationData); + controller.initialize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/user/user_list_page.dart b/lib/src/page/user/user_list_page.dart index 0cdc378..a18f2cd 100644 --- a/lib/src/page/user/user_list_page.dart +++ b/lib/src/page/user/user_list_page.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bones/flutter_bones.dart'; +import '../../model/page_model.dart'; import '../../widget/list_form.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; @@ -21,7 +23,7 @@ class UserListPage extends StatefulWidget { } } -class UserListPageState extends State { +class UserListPageState extends State implements RedrawPage { final ApplicationData applicationData; final GlobalKey _formKey = @@ -31,35 +33,9 @@ class UserListPageState extends State { UserListPageState(this.applicationData); - @override - void initState() { - super.initState(); - controller = - UserController(_formKey, this, 'list', context, applicationData, - redrawCallback: (RedrawReason reason, - {String customString, RedrawCallbackFunctionSimple callback}) { - switch (reason) { - case RedrawReason.fetchList: - controller.buildRows(); - break; - case RedrawReason.callback: - callback(RedrawReason.custom, customString); - setState(() {}); - break; - default: - setState(() {}); - break; - } - }); - controller.initialize(); - controller.buildWidgetList(); - controller.buildRows(); - controller.completeAsync(); - } - @override Widget build(BuildContext context) { - controller.buildHandler(context); + controller.beginOfBuild(context); return Scaffold( appBar: applicationData.appBarBuilder('Benutzer'), drawer: applicationData.drawerBuilder(context), @@ -77,10 +53,7 @@ class UserListPageState extends State { RaisedButton( child: Text('Neuer Benutzer'), onPressed: () { - Navigator.pushNamed(context, '/user/create'); - - /// Force the redraw on return: - controller.listRows = null; + controller.goTo(pageType: PageModelType.create); }, ), ]), @@ -91,4 +64,21 @@ class UserListPageState extends State { ), ); } + + @override + void initState() { + super.initState(); + controller = + UserController(_formKey, this, 'list', context, applicationData); + controller.initialize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/user/user_login_page.dart b/lib/src/page/user/user_login_page.dart index f451d2d..0e6da0a 100644 --- a/lib/src/page/user/user_login_page.dart +++ b/lib/src/page/user/user_login_page.dart @@ -6,8 +6,8 @@ import '../../model/button_model.dart'; import '../../widget/edit_form.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; -import 'user_controller.dart'; import 'hash.dart'; +import 'user_controller.dart'; class UserLoginPage extends StatefulWidget { final ApplicationData applicationData; @@ -27,7 +27,7 @@ class UserLoginPage extends StatefulWidget { } } -class UserLoginPageState extends State { +class UserLoginPageState extends State implements RedrawPage { final ApplicationData applicationData; final GlobalKey _formKey = GlobalKey(debugLabel: 'user.login'); @@ -38,28 +38,9 @@ class UserLoginPageState extends State { @override Widget build(BuildContext context) { - if (controller == null) { - controller = - UserController(_formKey, this, 'login', context, applicationData, - redrawCallback: (RedrawReason reason, - {String customString, - RedrawCallbackFunctionSimple callback}) { - switch (reason) { - case RedrawReason.callback: - callback(RedrawReason.custom, customString); - setState(() {}); - break; - default: - setState(() {}); - break; - } - }); - controller.initialize(); - customize(); - } - // controller.buildWidgetList() is called in editForm + controller.beginOfBuild(context); return Scaffold( - appBar: applicationData.appBarBuilder('Passwort ändern'), + appBar: applicationData.appBarBuilder('Anmelden'), drawer: applicationData.drawerBuilder(context), body: EditForm.editForm( key: _formKey, @@ -109,4 +90,22 @@ class UserLoginPageState extends State { controller.dispose(); super.dispose(); } + + @override + void initState() { + super.initState(); + controller = + UserController(_formKey, this, 'login', context, applicationData); + controller.initialize(); + customize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/page/user/user_password_page.dart b/lib/src/page/user/user_password_page.dart index 4e24cb7..2b12617 100644 --- a/lib/src/page/user/user_password_page.dart +++ b/lib/src/page/user/user_password_page.dart @@ -5,8 +5,8 @@ import '../../model/button_model.dart'; import '../../widget/edit_form.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; -import 'user_controller.dart'; import 'hash.dart'; +import 'user_controller.dart'; class UserPasswordPage extends StatefulWidget { final ApplicationData applicationData; @@ -32,7 +32,7 @@ class UserPasswordPage extends StatefulWidget { } } -class UserPasswordPageState extends State { +class UserPasswordPageState extends State implements RedrawPage { final ApplicationData applicationData; final int primaryId; final String userName; @@ -45,26 +45,7 @@ class UserPasswordPageState extends State { @override Widget build(BuildContext context) { - if (controller == null) { - controller = - UserController(_formKey, this, 'password', context, applicationData, - redrawCallback: (RedrawReason reason, - {String customString, - RedrawCallbackFunctionSimple callback}) { - switch (reason) { - case RedrawReason.callback: - callback(RedrawReason.custom, customString); - setState(() {}); - break; - default: - setState(() {}); - break; - } - }); - controller.initialize(); - customize(); - } - // controller.buildWidgetList() is called in editForm + controller.beginOfBuild(context); return Scaffold( appBar: applicationData.appBarBuilder('Passwort ändern'), drawer: applicationData.drawerBuilder(context), @@ -77,6 +58,7 @@ class UserPasswordPageState extends State { } void customize() { + controller.placeholders['user'] = userName; ButtonModel button = controller.page.buttonByName('store'); button?.onPressed = () { if (_formKey.currentState.validate()) { @@ -107,4 +89,22 @@ class UserPasswordPageState extends State { controller.dispose(); super.dispose(); } + + @override + void initState() { + super.initState(); + controller = + UserController(_formKey, this, 'password', context, applicationData); + controller.initialize(); + customize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } } diff --git a/lib/src/persistence/persistence_cache.dart b/lib/src/persistence/persistence_cache.dart index 90de6be..0ea8870 100644 --- a/lib/src/persistence/persistence_cache.dart +++ b/lib/src/persistence/persistence_cache.dart @@ -22,8 +22,13 @@ enum CacheEntryType { /// A cache for objects coming from a persistence layer. /// Uses the least recently used algorithm to avoid overflow. class PersistenceCache { - static final regExpCombobox = + /// format: ..; ;... + static final regExpComboboxDbColumn = RegExp(r'^\w+\.\w+\.\w+;\w+ \w+;( ?:\w+=\S*)*$'); + + /// format: .scope.; ;... + static final regExpComboboxConfiguration = + RegExp(r'^\w+\.scope\.\w+;\w+ \w+;( ?:\w+=\S*)*$'); static final regExpRecord = RegExp(r'^\w+\.\w+\.\w+;( ?:\w+=\S*)+$'); static int nextRecordNo = 0; final leastReasentlyUsed = []; @@ -52,14 +57,33 @@ class PersistenceCache { /// The format of [key]: /// "..; ; ..." /// e.g. "x99.role.list;role_name role_id;:role_name=% :role_active=T" + /// or + /// " scope= + /// e.g. "icons scope=icons" /// If [hasUndef] is true the first combobox list entry is '-' with the value null. /// If [oneTime] is true the result is only used one time, the LRU algoritm is /// not used. - Future comboboxAsync(String key, + Future comboboxAsync(ComboboxListType listType, String key, {bool hasUndef = false, bool oneTime = false}) async { ComboboxData rc; - if (regExpCombobox.firstMatch(key) == null) { - logger.error('wrong key syntax: $key'); + String errorMessage; + switch (listType) { + case ComboboxListType.dbColumn: + if (regExpComboboxDbColumn.firstMatch(key) == null) { + errorMessage = 'wrong key syntax: $key'; + } + break; + case ComboboxListType.configuration: + if (regExpComboboxConfiguration.firstMatch(key) == null) { + errorMessage = 'wrong key syntax: $key'; + } + break; + default: + errorMessage = 'comboboxAsync(): unknown listType: $listType'; + break; + } + if (errorMessage != null) { + logger.error(errorMessage); } else if (map.containsKey(key)) { final entry = updateLRU(key); rc = entry.comboboxData; @@ -75,7 +99,11 @@ class PersistenceCache { }); final texts = hasUndef ? ['-'] : []; final values = hasUndef ? [null] : []; - + if (listType == ComboboxListType.configuration) { + params2[':scope'] = idModuleName[2]; + idModuleName[1] = 'configuration'; + idModuleName[2] = 'combobox_by_scope'; + } final rows = await persistence.list( module: idModuleName[1], sqlName: idModuleName[2], params: params2); final entry = CacheEntry(key, CacheEntryType.combobox, diff --git a/lib/src/private/bdrawer.dart b/lib/src/private/bdrawer.dart index 7b3dd6b..d01a618 100644 --- a/lib/src/private/bdrawer.dart +++ b/lib/src/private/bdrawer.dart @@ -1,11 +1,12 @@ import 'package:flutter/material.dart'; +import '../page/application_data.dart'; import '../page/configuration/configuration_list_page.dart'; import '../page/demo_page.dart'; +import '../page/menu/menu_list_page.dart'; import '../page/role/role_list_page.dart'; import '../page/user/user_list_page.dart'; import '../page/user/user_login_page.dart'; -import '../page/application_data.dart'; import 'bsettings.dart'; class MenuItem { @@ -29,10 +30,10 @@ class MenuItem { rc = RoleListPage(applicationData); break; case 'configuration.list': - rc = RoleListPage(applicationData); + rc = ConfigurationListPage(applicationData); break; case 'menu.list': - rc = RoleListPage(applicationData); + rc = MenuListPage(applicationData); break; case 'demo': rc = DemoPage(applicationData); diff --git a/lib/src/widget/page_controller_bones.dart b/lib/src/widget/page_controller_bones.dart index 3c84110..8aedd99 100644 --- a/lib/src/widget/page_controller_bones.dart +++ b/lib/src/widget/page_controller_bones.dart @@ -20,7 +20,7 @@ import 'widget_validators.dart'; typedef RedrawCallbackFunction = Function(RedrawReason reason, {String customString, RedrawCallbackFunctionSimple callback}); typedef RedrawCallbackFunctionSimple = Function( - RedrawReason reason, String customString); + RedrawReason reason, {String customString}); class PageControllerBones implements ValidatorController { final ModuleModel moduleModel; @@ -30,30 +30,51 @@ class PageControllerBones implements ValidatorController { final GlobalKey globalKey; final String pageName; PageModel page; + ThemeData themeData; Future waitForCompletion; final ApplicationData applicationData; - State parent; + RedrawPage parent; final BuildContext context; Iterable listRows; final textControllers = {}; - final comboboxDataMap = {}; final asyncCompletedModels = []; int refreshCounter = 0; + final placeholders = {}; + + /// Opens another page. + /// Use exactly one of the parameter: + /// [pageType]: in this case the route will be constructed + /// [route]: the route of the new page, e.g. '/role/change' + void goTo({ PageModelType pageType, String route}) { + applicationData.pushCaller(this); + if (pageType != null){ + route = '/${moduleModel.name}/${StringUtils.enumToString(pageType)}'; + } + Navigator.pushNamed(context, route); + } PageControllerBones(this.globalKey, this.parent, this.moduleModel, this.pageName, this.context, this.applicationData, [this.redrawCallback]); + void afterSetState(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + if (callback != null) { + callback(reason, customString: customString); + } + } + Future buildComboboxDataFromPersistence( ComboBaseModel model) async { - final rc = await applicationData.persistenceCache - .comboboxAsync(model.listOption, hasUndef: model.hasOption('undef')); + final rc = await applicationData.persistenceCache.comboboxAsync( + model.listType, model.listOption, + hasUndef: model.hasOption('undef')); return rc; } /// This method should be called in each stateful widget of a page in the /// build() method. - void buildHandler(BuildContext context) { + void beginOfBuild(BuildContext context) { if (asyncCompletedModels.isNotEmpty && ++refreshCounter == 1) { wait(millisec: 2000).then((any) => reload()); } @@ -117,69 +138,6 @@ class PageControllerBones implements ValidatorController { page.fields.forEach((element) => prepareModel(element)); } - @deprecated - ComboboxData comboboxData(String name) { - ComboboxData rc = comboboxDataMap.containsKey(name) - ? comboboxDataMap[name] - : ComboboxData([], null); - comboboxDataMap[name] = rc; - if (rc.waitState == WaitState.undef) { - ComboBaseModel model = page.fieldByName(name) as ComboBaseModel; - if (model != null) { - switch (model.listType) { - case ComboboxListType.explicite: - rc = ComboboxData(model.texts, model.values, WaitState.ready); - comboboxDataMap[name] = rc; - break; - case ComboboxListType.undef: - rc = ComboboxData([], [], WaitState.ready); - comboboxDataMap[name] = rc; - break; - case ComboboxListType.dbColumn: - comboboxDataDb(model, rc); - break; - case ComboboxListType.configuration: - break; - } - } - } - return rc; - } - - @deprecated - void comboboxDataDb(ComboBaseModel model, ComboboxData data) { - // example: xxx.role.list;role_displayname role_id;:role_name=% :excluded=0' - final keyColumnsParams = model.listOption.split(';'); - final nameModuleSql = keyColumnsParams[0].split('.'); - final cols = keyColumnsParams[1].split(' '); - final params = {}; - if (keyColumnsParams.length > 2 && keyColumnsParams[2].isNotEmpty) { - keyColumnsParams[2].split(' ').forEach((element) { - final keyValue = element.split('='); - params[keyValue[0]] = keyValue[1]; - }); - } - applicationData.persistence - .list( - module: nameModuleSql[1], sqlName: nameModuleSql[2], params: params) - .then((rows) { - rows.forEach((row) { - data.texts.add(row[cols[0]]); - var value = row[cols[1]]; - if (value is String) { - value = StringHelper.fromString(value, model.dataType); - } - data.addValue(value); - }); - if (model.hasOption('undef')) { - data.texts.insert(0, '-'); - data.insertValue(0, null); - } - data.waitState = WaitState.ready; - redraw(RedrawReason.redraw); - }); - } - /// Completes the widgets asynchronously if needed. void completeAsync() { waitForCompletion = completeModelsAsync(); @@ -188,9 +146,11 @@ class PageControllerBones implements ValidatorController { /// Completes models synchronously if possible or initializes the asynchronous /// completion. void completeModels(WidgetModel model) { - if (model is ComboBaseModel && model.listOption != null) { - model.data = - applicationData.persistenceCache.comboboxFromCache(model.listOption); + if (model is ComboBaseModel && model.listType != null) { + if (model.listOption != null) { + model.data = applicationData.persistenceCache + .comboboxFromCache(model.listOption); + } if (model.data == null) { if (model.mustCompletedAsync) { asyncCompletedModels.add(model); @@ -206,6 +166,7 @@ class PageControllerBones implements ValidatorController { Future.forEach(asyncCompletedModels, (ComboBaseModel model) async { switch (model.listType) { case ComboboxListType.dbColumn: + case ComboboxListType.configuration: final data = await buildComboboxDataFromPersistence(model); model.data = data; break; @@ -244,24 +205,6 @@ class PageControllerBones implements ValidatorController { }); } - /// Returns a [WidgetList] filled with widgets - @deprecated - WidgetList filterSet({@required String pageName}) { - final rc = WidgetList('${page.fullName()}.widgets', moduleModel.logger); - moduleModel - .pageByName(pageName) - .fields - .where((element) => element.filterType != null) - .forEach((element) { - Widget widget = View().modelToWidget(element, this); - if (element.widgetModelType == WidgetModelType.combobox) { - rc.waitCandidates.add(element); - } - rc.addWidget(element.name, widget); - }); - return rc; - } - /// Returns the container for application specific information. ApplicationData getApplicationData() { return applicationData; @@ -425,8 +368,14 @@ class PageControllerBones implements ValidatorController { /// e.g. ModuleModel.parse() is called. /// This method must be called early after the constructor. void initialize() { + themeData = Theme.of(context); page = moduleModel.pageByName(pageName); widgetList = WidgetList('${page.fullName()}.widgets', moduleModel.logger); + buildWidgetList(); + if (page?.pageModelType == PageModelType.list) { + buildRows(); + } + completeAsync(); } /// Copies the values of [initialRow] into the values of the fields. @@ -450,14 +399,13 @@ class PageControllerBones implements ValidatorController { /// [reason] and [customString] will be forwarded to the callback function. void redraw(RedrawReason reason, {String customString, RedrawCallbackFunctionSimple callback}) { - if (redrawCallback == null) { - moduleModel.logger.error( - 'not overridden: fetchListRows() in ${moduleModel.widgetName()}.$pageName'); - } else { - if (reason == RedrawReason.setError) { - applicationData.setLastErrorMessage(page.fullName(), customString); - } - redrawCallback(reason, customString: customString, callback: callback); + switch(reason){ + case RedrawReason.fetchList: + buildRows(); + break; + default: + parent.redraw(reason, customString: customString, callback: callback); + break; } } @@ -498,6 +446,13 @@ class PageControllerBones implements ValidatorController { } // This interface allows the generic handling of the edit form by a model driven module. +/// An interface for enforcing the redraw of a page. +abstract class RedrawPage { + /// Enforces a redraw (= setState()) of a page. + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}); +} + enum RedrawReason { custom, callback, diff --git a/lib/src/widget/view.dart b/lib/src/widget/view.dart index 9b56fd1..8986444 100644 --- a/lib/src/widget/view.dart +++ b/lib/src/widget/view.dart @@ -22,7 +22,7 @@ class View { static View _instance; Settings settings; BaseConfiguration widgetConfiguration; - + static final regExpPlaceholder = RegExp(r'~(\w~)~'); final BaseLogger logger; factory View([BaseLogger logger]) { @@ -85,7 +85,7 @@ class View { Widget combobox( ComboBaseModel model, PageControllerBones controller, initialValue) { Widget rc; - if (model.data != null && model.data.waitState == WaitState.ready) { + if (model != null && model.data?.waitState == WaitState.ready) { rc = comboboxRaw(model, controller, initialValue); } else { rc = FutureBuilder( @@ -94,7 +94,7 @@ class View { if (snapshot.hasData) { return comboboxRaw(model, controller, initialValue); } else if (snapshot.hasError) { - return errorMessage(snapshot.error); + return errorMessage(snapshot.error.toString()); } else { return Container( alignment: Alignment.topCenter, @@ -150,7 +150,7 @@ class View { if (comboboxData == null || comboboxData.texts.length == 0) { items.add(DropdownMenuItem( onTap: controller.getOnTap(model.name, controller), - value: null, + value: initialValue, child: Text('-'))); } else { for (var ix = 0; ix < comboboxData.texts.length; ix++) { @@ -240,7 +240,7 @@ class View { rc = emptyLine(model); break; case WidgetModelType.text: - rc = text(model); + rc = text(model, controller); break; case WidgetModelType.checkbox: rc = checkbox(model, controller, initialValue); @@ -301,8 +301,24 @@ class View { } /// Creates a text from the [model]. - Widget text(TextModel model) { - final rc = Text(model.text); + Widget text(TextModel model, PageControllerBones controller) { + var text = model.text; + if (model.hasOption('placeholders')){ + text = StringHelper.replacePlaceholders(text, controller.placeholders, regExpPlaceholder: regExpPlaceholder); + } + TextStyle style; + if (model.hasOption('h1')) { + style = controller.themeData.textTheme.headline1; + } else if (model.hasOption('h2')){ + style = controller.themeData.textTheme.headline2; + } else if (model.hasOption('h3')){ + style = controller.themeData.textTheme.headline3; + } else if (model.hasOption('h4')){ + style = controller.themeData.textTheme.headline4; + } else if (model.hasOption('h5')){ + style = controller.themeData.textTheme.headline5; + } + final rc = Text(model.text, style: style); return rc; } diff --git a/model_tool/lib/src/net/email.dart b/model_tool/lib/src/net/email.dart new file mode 100644 index 0000000..1aef55d --- /dev/null +++ b/model_tool/lib/src/net/email.dart @@ -0,0 +1,38 @@ +import 'package:http/http.dart' as http; + +class EMail { + static sendRegistrationNotification(String email) async { + Map headers = new Map(); + headers["Authorization"] = "Bearer SENDGRIDAPIKEY"; + headers["Content-Type"] = "application/json"; + + var url = 'https://api.sendgrid.com/v3/mail/send'; + var response = await http.post(url, headers: headers, body: '''{ + \"personalizations\": [ + { + \"to\": [ + { + \"email\": \"jerrod@liftaixxx.com\" + }, + { + \"email\": \"darran@gmailxxx.com\" + } + ] + } + ], + \"from\": { + \"email\": \"app@liftaixxx.com\" + }, + \"subject\": \"New user registration\", + \"content\": [ + { + \"type\": \"text\/plain\", + \"value\": \"New user register: $email\" + } + ] + } + '''); + print('Response status: ${response.statusCode}'); + print('Response body: ${response.body}'); + } +} diff --git a/test/helpers/string_helper_test.dart b/test/helpers/string_helper_test.dart index ca7db96..32f0766 100644 --- a/test/helpers/string_helper_test.dart +++ b/test/helpers/string_helper_test.dart @@ -45,6 +45,13 @@ void main() { expect(StringHelper.fromString('2020.1.2', null), startsWith('