From: Hamatoma Date: Mon, 23 Nov 2020 10:07:00 +0000 (+0100) Subject: daily work X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=7b729f36a1c079b0c31463a53aa85b2a0184cbf1;p=flutter_bones.git daily work --- diff --git a/Coverage.sh b/Coverage.sh index 0f927f2..016065c 100755 --- a/Coverage.sh +++ b/Coverage.sh @@ -2,7 +2,14 @@ if [ $(id -u) = 0 ]; then echo "+++ not working as root" else -flutter test --coverage -rm -Rf coverage/html/* -genhtml --show-details --output-directory=coverage/html coverage/lcov.info + file=test/coverage_helper_test.dart + PACKAGE=flutter_bones + echo "// Helper file to make coverage work for all dart files\n" > $file + echo "// ignore_for_file: unused_import" >> $file + find lib -not -name '*.g.dart' -name '*.dart' | grep -v 'generated_plugin_registrant' | cut -c4- \ + | awk -v package=$PACKAGE '{printf "import '\''package:%s%s'\'';\n", package, $1}' >> $file + echo "\nvoid main(){}" >> $file + flutter test --coverage + rm -Rf coverage/html/* + genhtml --show-details --output-directory=coverage/html coverage/lcov.info fi diff --git a/CreateModule b/CreateModule index d513e73..dd4ac59 100755 --- a/CreateModule +++ b/CreateModule @@ -1,22 +1,3 @@ #! /bin/bash -MODULE=$1 -if [ -z "$MODULE" ]; then - echo "Missing module name" - echo "example: $0 menu" -else - cd lib/src/page - if [ -d $MODULE ]; then - FN_NEW=/tmp/$MODULE.$(date "+%s") - echo "$MODULE already exists, moving to $FN_NEW" - mv $MODULE $FN_NEW - fi - mkdir $MODULE - cp -av role/* $MODULE/ - FN_REPL=/tmp/create_module.repl - MODULE3=$(echo $MODULE | tr a-z A-Z) - MODULE2=${MODULE3:0:1}${MODULE:1} - # echo -e "role\t$MODULE\nRole\t$MODULE2" >$FN_REPL - cd $MODULE - perl -pi -e"s/role/$MODULE/g;s/Role/$MODULE2/g;" *.dart - rename -v "s/role(.*)/${MODULE}\$1/;" *.dart -fi +dart run model_tool/lib/main.dart create-module $* + diff --git a/data/rest/custom.user.yaml b/data/rest/custom.user.yaml index 37243eb..0cb5223 100644 --- a/data/rest/custom.user.yaml +++ b/data/rest/custom.user.yaml @@ -16,3 +16,9 @@ modules: sql: "SELECT u.*, r.* FROM user u LEFT JOIN role r ON u.user_role=r.role_id WHERE (user_name=:user or user_email=:user);" + - name: list + type: list + options: override + sql: "SELECT t0.*,t1.role_name as user_role, role_priority FROM user t0 + LEFT JOIN role t1 on t1.role_id=t0.user_role + WHERE user_name like :user_name AND (:user_role IS NULL OR user_role=:user_role)" diff --git a/lib/app.dart b/lib/app.dart index 5e4670d..2407c42 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -16,12 +16,14 @@ import 'src/page/user/user_login_page.dart'; import 'src/page/starter/starter_list_page.dart'; import 'src/page/starter/starter_create_page.dart'; import 'src/private/bsettings.dart'; +import 'src/widget/utilities.dart'; class BoneApp extends StatefulWidget { @override BoneAppState createState() { final logger = MemoryLogger(LEVEL_FINE); BSettings.create(logger); + utilities = Utilities(logger); final mapWidgetData = { 'form.card.padding': '16.0', 'form.gap.field_button.height': '16.0', diff --git a/lib/flutter_bones.dart b/lib/flutter_bones.dart index ad28944..a5731ae 100644 --- a/lib/flutter_bones.dart +++ b/lib/flutter_bones.dart @@ -17,10 +17,11 @@ export 'src/model/model_types.dart'; 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/user_model.dart'; +export 'src/model/module/configuration_model.dart'; +export 'src/model/module/menu_model.dart'; +export 'src/model/module/role_model.dart'; +export 'src/model/module/starter_model.dart'; +export 'src/model/module/user_model.dart'; export 'src/model/text_field_model.dart'; export 'src/model/text_model.dart'; export 'src/model/widget_model.dart'; diff --git a/lib/src/helper/string_helper.dart b/lib/src/helper/string_helper.dart index 1473e04..b132b7a 100644 --- a/lib/src/helper/string_helper.dart +++ b/lib/src/helper/string_helper.dart @@ -115,7 +115,7 @@ class StringHelper { value = DateFormat('yyyy-MM-dd').format(value); break; case DataType.bool: - value = value ? 'T' : 'F'; + value = value is String ? value : (value ? 'T' : 'F'); break; default: value = value.toString(); diff --git a/lib/src/helper/url_helper.dart b/lib/src/helper/url_helper.dart index 4f5bd22..94a351e 100644 --- a/lib/src/helper/url_helper.dart +++ b/lib/src/helper/url_helper.dart @@ -1,9 +1,43 @@ -import 'package:dart_bones/dart_bones.dart'; - class UrlHelper { static final sep = '/'; static final currentDirSep = '.' + sep; + /// Returns the extension of the [path]. + /// The extension is the part behind the last '.'. + /// If the only '.' is at the top, the result is '' otherwise the the last part with '.'. + static String extensionOf(String path) { + var rc = ''; + final ix = path.lastIndexOf('.'); + final ixSlash = path.lastIndexOf(sep); + if (ix > 0 && (ixSlash < 0 || ix > ixSlash + 1)) { + rc = path.substring(ix); + } + return rc; + } + + /// Returns the extension of the [path]. + /// The extension is the part behind the last '.'. + /// If the only '.' is at the top, the result is '' otherwise the the last part with '.'. + static String filenameOf(String path) { + var rc = ''; + final ix = path.lastIndexOf('.'); + final ixSlash = path.lastIndexOf(sep); + if (ixSlash < 0) { + if (ix <= 0) { + rc = path; + } else { + rc = path.substring(0, ix); + } + } else { + if (ix <= ixSlash + 1) { + rc = path.substring(ixSlash + 1); + } else if (ix > ixSlash + 1) { + rc = path.substring(ixSlash + 1, ix); + } + } + return rc; + } + /// Joins parts to a combined path. /// [first]: first part /// [second]: second part diff --git a/lib/src/model/model_helper.dart b/lib/src/model/model_helper.dart index 85c190e..6a89a60 100644 --- a/lib/src/model/model_helper.dart +++ b/lib/src/model/model_helper.dart @@ -1,10 +1,10 @@ import 'package:dart_bones/dart_bones.dart'; import 'module_model.dart'; -import 'standard/role_model.dart'; -import 'standard/user_model.dart'; -import 'standard/menu_model.dart'; -import 'standard/starter_model.dart'; -import 'standard/configuration_model.dart'; +import 'module/role_model.dart'; +import 'module/user_model.dart'; +import 'module/menu_model.dart'; +import 'module/starter_model.dart'; +import 'module/configuration_model.dart'; class ModelHelper { /// Returns an instance of a module given by [name]. @@ -34,6 +34,11 @@ class ModelHelper { } /// Returns the names of the modules. - List moduleNames() => - ['configuration', 'menu', 'role', 'starter', 'user']; + List moduleNames() => [ + 'configuration', + 'menu', + 'role', + 'starter', + 'user', + ]; } diff --git a/lib/src/model/model_tool.dart b/lib/src/model/model_tool.dart index 17271d7..cce1602 100644 --- a/lib/src/model/model_tool.dart +++ b/lib/src/model/model_tool.dart @@ -13,6 +13,130 @@ abstract class ModelTool { final ModelHelper modelHelper; ModelTool(this.modelHelper, this.logger); + void createModule(List args, List options) { + try { + String baseDirectory; + String value; + PageModelType handledPageType; + for (var opt in options) { + value = StringUtils.stringOption('directory', 'd', opt); + if (value != null) { + baseDirectory = value; + continue; + } + value = StringUtils.stringOption('page-type', 't', opt); + if (value != null) { + handledPageType = StringUtils.stringToEnum( + value, PageModelType.values); + if (handledPageType == null) { + throw FormatException('unknown page type $value in $opt'); + } + continue; + } + logger.error('unknown option: $opt'); + } + baseDirectory ??= 'lib/src'; + if (args.isEmpty) { + args = modelHelper.moduleNames(); + } + var fnTemplate = 'lib/src/model/module/role_model.dart'; + var templateLines = readFile(fnTemplate); + if (templateLines.isEmpty) { + logger.error('missing $fnTemplate. Wrong current directory?'); + } else if (args.length < 1) { + logger.error('too few arguments'); + throw FormatException('too few arguments'); + } else { + final moduleName = args[0]; + final pageName = args.length > 1 ? args[1] : null; + if (RegExp(r'^\w+$').firstMatch(moduleName) == null) { + throw FormatException( + 'invalid character(s) in module name: $moduleName'); + } + if (pageName != null && RegExp(r'^\w+$').firstMatch(pageName) == null) { + throw FormatException( + 'invalid character(s) in page name: $moduleName'); + } + if (pageName != null && handledPageType == null) { + throw FormatException('having page name but no page type'); + } + final moduleCapital = StringHelper.capitalize(moduleName); + final converter = (String line) => line + .replaceAll('Role', moduleCapital) + .replaceAll('role', moduleName) + .replaceAll('Rollen', '!!!${moduleCapital}s') + .replaceAll('Rolle', '!!$moduleCapital'); + List output = []; + String fnOutput; + // Create the module model: + StringHelper.addRangeToList(templateLines, output, + converter: converter); + fnOutput = UrlHelper.joinPaths( + baseDirectory, 'module/standard', '${moduleName}_model.dart'); + writeFile(fnOutput, output.join('\n'), + mayExist: false, ensureDirectory: true); + // Create the controller: + fnTemplate = 'lib/src/model/module/role_controller.dart'; + templateLines = readFile(fnTemplate); + StringHelper.addRangeToList(templateLines, output, + converter: converter); + fnOutput = UrlHelper.joinPaths( + baseDirectory, 'module/standard', '${moduleName}_model.dart'); + writeFile(fnOutput, output.join('\n'), + mayExist: false, ensureDirectory: true); + // Create the pages: + List names; + List types; + if (pageName != null) { + names = [pageName]; + types = [handledPageType]; + } else { + names = ['create', 'change', 'delete', 'list']; + types = [ + PageModelType.create, + PageModelType.change, + PageModelType.delete, + PageModelType.list + ]; + } + for (var ix = 0; ix < names.length; ix++) { + createPage(baseDirectory, moduleName, names[ix], types[ix]); + } + } + } on FormatException catch (exc) { + logger.error(exc.toString()); + } + } + + /// Creates a page named [pageName] of [pageType] in the [baseDirectory]. + void createPage(String baseDirectory, String moduleName, String pageName, + PageModelType pageType) { + final typeString = StringUtils.enumToString(pageType); + final fnTemplate = 'lib/src/page/role/role_${typeString}_page.dart'; + final templateLines = readFile(fnTemplate); + if (templateLines.isEmpty) { + logger.error('missing $fnTemplate'); + } else { + final output = []; + final pageTypeCapital = StringHelper.capitalize(typeString); + final moduleCapital = StringHelper.capitalize(moduleName); + final pageNameCapital = StringHelper.capitalize(pageName); + final converter = (String line) => line + .replaceAll('Role${pageTypeCapital}Page', + '$moduleCapital${pageNameCapital}Page') + .replaceAll('RoleController', '${moduleCapital}Controller') + .replaceAll('role$pageNameCapital', '$moduleName$pageNameCapital') + .replaceAll('role$pageTypeCapital', '$moduleName$pageNameCapital') + .replaceAll('Role', moduleCapital) + .replaceAll('role', moduleName); + StringHelper.addRangeToList(templateLines, output, converter: converter); + final fnOutput = UrlHelper.joinPaths( + baseDirectory, moduleName, '${moduleName}_${pageName}_page.dart'); + writeFile(fnOutput, output.join('\n'), + ensureDirectory: true, mayExist: false); + } + } + /// Ensures that the [directory] exists. If not it will be created. void ensureDirectory(String directory); @@ -62,15 +186,12 @@ abstract class ModelTool { baseDirectory = value; continue; } - value = StringUtils.stringOption('directory', 'd', opt); + value = StringUtils.stringOption('page-type', 't', opt); if (value != null) { handledPageType = StringUtils.stringToEnum( value, PageModelType.values); if (handledPageType == null) { - logger.error('unknown page type $value in $opt'); - logger.error('aborting command'); - throw FormatException('invalid option $opt'); - break; + throw FormatException('unknown page type $value in $opt'); } continue; } @@ -91,6 +212,7 @@ abstract class ModelTool { for (var name in args) { if (name == 'role') { logger.log('module "role" ignored.'); + continue; } final sourceDir = 'lib/src/page/$name'; final files = pathOfDirectory(sourceDir); @@ -131,17 +253,16 @@ abstract class ModelTool { 'role_${StringUtils.enumToString(pageType)}_page.dart'); final templateLines = readFile(fnTemplate); if (templateLines.isEmpty) { - logger.log('ignoring ${file}: no template found'); + logger.log('ignoring $file: no template found'); continue; } final pageTypeCapital = StringHelper.capitalize(pageTypeLower); - final classFragment = 'Role$pageTypeCapital'; final converter = (String line) => line .replaceAll('Role${pageTypeCapital}Page', '$moduleCapital${pageNameCapital}Page') .replaceAll('RoleController', '${moduleCapital}Controller') - .replaceAll( - 'role$pageNameCapital', '$name$pageNameCapital'); + .replaceAll('role$pageNameCapital', '$name$pageNameCapital') + .replaceAll('role$pageTypeCapital', '$name$pageNameCapital'); modifyPage( name, pageType, @@ -164,7 +285,6 @@ abstract class ModelTool { String filename, bool overwriteConstructors, String pathSafe) { - final moduleCapital = StringHelper.capitalize(module); ensureDirectory(UrlHelper.parentOf(filename, trailingSlash: false)); final output = []; int ix; @@ -272,6 +392,9 @@ abstract class ModelTool { case 'modify-module': modifyModule(args, options); break; + case 'create-module': + createModule(args, options); + break; default: logger.error('unknown mode: $mode'); break; @@ -284,24 +407,35 @@ abstract class ModelTool { void usage(String error) { print('''usage: main [] : + create-module [] + Modifies the files of the given modules to renew the generated code. + : + -d or --directory=: + The base directory used for the created files. + --page-type= + Creates only the file with this type: 'create', 'change', 'list', 'custom' create-sql [ [...] [] Generates the DDL ("create table...") statements and the yaml files describing the insert... SQL statements. : 'role', 'user' ... : -d or --directory=: - the base directory used for the exported files. + The base directory used for the exported files. help Display this usage messages. modify-module [ [...] [] Modifies the files of the given modules to renew the generated code. - : - --page-type= - Modify only files with this type: 'create', 'change', 'list', 'custom' - --override-constructors - Parts of the constructors will be taken from the template. - Can destroy customized code! Needed if there are changed customized areas. + : + -d or --directory=: + The base directory used for the modified files. + --page-type= + Modify only files with this type: 'create', 'change', 'list', 'custom' + --override-constructors + Parts of the constructors will be taken from the template. + Can destroy customized code! Needed if there are changed customized areas. Examples: +main create-module address +main create-module address compare --page-type=custom main create-sql role user -d/tmp main create-sql --directory=/opt/sql-data main modify-pages role --customize-constructors @@ -310,5 +444,6 @@ main modify-pages --page-type=list } /// Writes a [content] to a file named [filename]. - void writeFile(String filename, String content); + void writeFile(String filename, String content, + {bool mayExist, bool ensureDirectory}); } diff --git a/lib/src/model/module/configuration_model.dart b/lib/src/model/module/configuration_model.dart new file mode 100644 index 0000000..396e908 --- /dev/null +++ b/lib/src/model/module/configuration_model.dart @@ -0,0 +1,161 @@ +import 'package:dart_bones/dart_bones.dart'; + +import '../module_model.dart'; + +/// +class ConfigurationModel extends ModuleModel { + static final yamlMap = { + // + 'module': 'configuration', + 'tables': [ + { + // configuration_id | configuration_scope | configuration_property | configuration_order | configuration_type + // | configuration_value | configuration_description + 'table': 'configuration', + 'columns': [ + { + 'column': 'configuration_id', + 'dataType': 'int', + 'label': 'Id', + 'options': 'primary', + }, + { + 'column': 'configuration_scope', + 'dataType': 'string', + 'label': 'Bereich', + 'size': 64, + 'options': 'notnull', + 'validators': r'required regExpr=i/^\w+$', + 'validatorsText': + 'regExpr=Nur Buchstaben, Ziffern und Unterstrich erlaubt', + }, + { + 'column': 'configuration_property', + 'dataType': 'string', + 'size': 64, + 'label': 'Eigenschaft', + }, + { + 'column': 'configuration_order', + 'dataType': 'int', + 'label': 'Reihe', + }, + { + 'column': 'configuration_type', + 'dataType': 'string', + 'size': 16, + 'label': 'Datentyp', + 'listType': 'explicite', + 'texts': ';bool;date;float;int;string', + 'validators': 'required', + }, + { + 'column': 'configuration_value', + 'dataType': 'string', + 'size': 255, + 'label': 'Wert', + }, + { + 'column': 'configuration_description', + 'dataType': 'string', + 'size': 255, + 'label': 'Beschreibung', + }, + ] + }, + ], + 'pages': [ + { + 'page': 'create', + 'title': 'Konfiguration hinzufügen', + 'pageType': 'create', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'change', + 'title': 'Konfiguration ändern', + 'pageType': 'change', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'delete', + 'title': 'Konfiguration löschen', + 'pageType': 'delete', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'list', + 'title': 'Konfiguration', + 'pageType': 'list', + 'tableColumns': + 'configuration_id configuration_scope configuration_property configuration_order configuration_value configuration_type', + 'tableTitles': ';Id;Bereich;Eigenschaft;Reihe;Wert;Typ', + 'sections': [ + { + 'sectionType': 'filterPanel', + 'children': [ + { + 'name': 'configuration_scope', + 'label': 'Bereich', + 'modelType': 'textField', + 'filterType': 'pattern', + //'options': 'undef', + }, + { + 'name': 'configuration_property', + 'label': 'Eigenschaft', + 'modelType': 'textField', + 'filterType': 'pattern', + }, + ], + 'buttonBar': [ + { + 'modelType': 'button', + 'buttonType': 'custom', + 'name': 'new', + 'label': 'Neue Konfiguration', + 'toolTip': 'Erzeugen einer neuen Konfiguration' + }, + ], + } + ] + }, + ], + }; + + ConfigurationModel(BaseLogger logger) : super(yamlMap, logger); + + /// Returns the name including the names of the parents. + @override + String fullName() => name; + + @override + String widgetName() => name; +} diff --git a/lib/src/model/module/menu_model.dart b/lib/src/model/module/menu_model.dart new file mode 100644 index 0000000..499c253 --- /dev/null +++ b/lib/src/model/module/menu_model.dart @@ -0,0 +1,134 @@ +import 'package:dart_bones/dart_bones.dart'; + +import '../module_model.dart'; + +class MenuModel extends ModuleModel { + static final mapMenu = { + 'module': 'menu', + 'tables': [ + { + 'table': 'menu', + 'columns': [ + { + 'column': 'menu_id', + 'dataType': 'int', + 'label': 'Id', + 'options': 'primary', + }, + { + 'column': 'menu_role', + 'dataType': 'reference', + 'label': 'Rolle', + 'foreignKey': 'role.role_id role_name', + 'listType': 'dbColumn', + 'listOption': 'all.role.list;role_name role_id;', + }, + { + 'column': 'menu_starter', + 'dataType': 'reference', + 'label': 'Programmpunkt', + 'foreignKey': 'starter.starter_id starter_name', + 'listType': 'dbColumn', + 'listOption': 'all.starter.list;starter_name starter_id;', + }, + ] + }, + ], + 'pages': [ + { + 'page': 'create', + 'title': 'Startmenü hinzufügen', + 'pageType': 'create', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'change', + 'title': 'Startmenü ändern', + 'pageType': 'change', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + }, + ], + 'buttonBar': [ + { + 'modelType': 'button', + 'buttonType': 'custom', + 'name': 'new', + 'label': 'Neues Startmenü', + 'toolTip': 'Erzeugen eines neuen Startmenüs' + }, + ], + } + ] + }, + { + 'page': 'delete', + 'title': 'Startmenü löschen', + 'pageType': 'delete', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'list', + 'title': 'Startmenü', + 'pageType': 'list', + 'tableColumns': 'menu_id menu_role menu_order menu_name', + 'tableTitles': ';Id;Rolle;Reihe;Programmpunkt', + 'sections': [ + { + 'sectionType': 'filterPanel', + 'children': [ + { + 'modelType': 'dbReference', + 'filterType': 'pattern', + 'name': 'menu_role', + 'column': 'menu_role', + 'toolTip': + 'Filter bezüglich der Rolle der anzuzeigenden Einträge.' + }, + ], + 'buttonBar': [ + { + 'modelType': 'button', + 'buttonType': 'custom', + 'name': 'new', + 'label': 'Neues Startmenü', + 'toolTip': 'Erzeugen eines Startmenüs' + }, + ], + } + ] + }, + ] + }; + + MenuModel(BaseLogger logger) : super(mapMenu, logger); + + /// Returns the name including the names of the parents. + @override + String fullName() => name; + + @override + String widgetName() => name; +} diff --git a/lib/src/model/module/role_model.dart b/lib/src/model/module/role_model.dart new file mode 100644 index 0000000..04cf3a5 --- /dev/null +++ b/lib/src/model/module/role_model.dart @@ -0,0 +1,127 @@ +import 'package:dart_bones/dart_bones.dart'; + +import '../module_model.dart'; + +class RoleModel extends ModuleModel { + RoleModel(BaseLogger logger) : super(yamlMap, logger); + + static final yamlMap = { + 'module': 'role', + 'tables': [ + { + 'table': 'role', + 'columns': [ + { + 'column': 'role_id', + 'dataType': 'int', + 'label': 'Id', + 'options': 'primary', + }, + { + 'column': 'role_name', + 'dataType': 'string', + 'label': 'Rolle', + 'size': 32, + 'options': 'unique notnull', + }, + { + 'column': 'role_priority', + 'dataType': 'int', + 'label': 'Priorität', + 'validators': 'required minInt=1 maxInt=99', + }, + { + 'column': 'role_active', + 'dataType': 'bool', + 'label': 'Aktiv', + }, + ] + }, + ], + 'pages': [ + { + 'page': 'create', + 'title': 'Rolle hinzufügen', + 'pageType': 'create', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'change', + 'title': 'Rolle ändern', + 'pageType': 'change', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'delete', + 'title': 'Rolle löschen', + 'pageType': 'delete', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'list', + 'title': 'Rollen', + 'pageType': 'list', + 'tableColumns': 'role_id role_name role_priority', + 'tableTitles': ';Id;Rolle;Priorität', + 'sections': [ + { + 'sectionType': 'filterPanel', + 'children': [ + { + 'modelType': 'textField', + 'filterType': 'pattern', + 'name': 'role_name', + 'label': 'Name', + 'toolTip': + 'Suchmuster des Rollennamens: Joker: "*" (beliebiger Text), z.B. "*min*"' + }, + ], + 'buttonBar': [ + { + 'modelType': 'button', + 'buttonType': 'custom', + 'name': 'new', + 'label': 'Neue Rolle', + 'toolTip': 'Erzeugen einer neuen Rolle' + }, + ], + } + ], + }, + ] + }; + + /// Returns the name including the names of the parents. + @override + String fullName() => name; + + @override + String widgetName() => name; +} diff --git a/lib/src/model/module/starter_model.dart b/lib/src/model/module/starter_model.dart new file mode 100644 index 0000000..bbad7ad --- /dev/null +++ b/lib/src/model/module/starter_model.dart @@ -0,0 +1,125 @@ +import 'package:dart_bones/dart_bones.dart'; + +import '../module_model.dart'; + +class StarterModel extends ModuleModel { + static final mapStarter = { + 'module': 'starter', + 'tables': [ + { + 'table': 'starter', + 'columns': [ + { + 'column': 'starter_id', + 'dataType': 'int', + 'label': 'Id', + 'options': 'primary', + }, + { + 'column': 'starter_name', + 'dataType': 'string', + 'label': 'Name', + 'size': 64, + 'options': 'unique notnull', + }, + { + 'column': 'starter_icon', + 'dataType': 'reference', + 'label': 'Bild', + 'foreignKey': 'configuration.configuration_id configuration_value', + 'listType': 'configuration', + 'listOption': + 'std.scope.icons;configuration_value configuration_id;', + }, + ] + }, + ], + 'pages': [ + { + 'page': 'create', + 'title': 'Programmpunkt hinzufügen', + 'pageType': 'create', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'change', + 'title': 'Programmpunkt hinzufügen', + 'pageType': 'change', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + }, + ] + } + ] + }, + { + 'page': 'delete', + 'title': 'Programmpunkt löschen', + 'pageType': 'delete', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'list', + 'title': 'Programmpunkte', + 'pageType': 'list', + 'tableColumns': 'starter_id starter_name starter_icon', + 'tableTitles': ';Id;Name;Bild', + 'sections': [ + { + 'sectionType': 'filterPanel', + 'children': [ + { + 'modelType': 'dbReference', + 'filterType': 'pattern', + 'name': 'starter_name', + 'column': 'starter_name', + 'toolTip': + 'Filter bezüglich des Namens der anzuzeigenden Einträge: Joker "*" (beliebiger String) ist erlaubt.' + }, + ], + 'buttonBar': [ + { + 'modelType': 'button', + 'buttonType': 'custom', + 'name': 'new', + 'label': 'Neuer Programmpunkt', + 'toolTip': 'Erzeugen eines neuen Programmpunkts' + }, + ], + } + ] + }, + ] + }; + + StarterModel(BaseLogger logger) : super(mapStarter, logger); + + /// Returns the name including the names of the parents. + @override + String fullName() => name; + + @override + String widgetName() => name; +} diff --git a/lib/src/model/module/user_model.dart b/lib/src/model/module/user_model.dart new file mode 100644 index 0000000..c6f9be1 --- /dev/null +++ b/lib/src/model/module/user_model.dart @@ -0,0 +1,231 @@ +import 'package:dart_bones/dart_bones.dart'; + +import '../module_model.dart'; + +class UserModel extends ModuleModel { + static final mapUser = { + 'module': 'user', + 'tables': [ + { + 'table': 'user', + 'columns': [ + { + 'column': 'user_id', + 'dataType': 'int', + 'label': 'Id', + 'options': 'primary', + }, + { + 'column': 'user_name', + 'dataType': 'string', + 'label': 'User', + 'size': 64, + 'options': 'unique notnull', + }, + { + 'column': 'user_displayname', + 'dataType': 'string', + 'label': 'Anzeigename', + 'size': 32, + 'options': 'unique notnull', + }, + { + 'column': 'user_email', + 'dataType': 'string', + 'label': 'EMail', + 'size': 128, + 'options': 'unique notnull', + 'validators': 'email', + }, + { + 'column': 'user_password', + 'dataType': 'string', + 'label': 'Passwort', + 'size': 128, + 'options': 'password hidden', + }, + { + 'column': 'user_role', + 'dataType': 'reference', + 'label': 'Rolle', + 'foreignKey': 'role.role_id role_name', + 'listType': 'dbColumn', + 'listOption': 'all.role.list;role_name role_id;:role_name=%', + 'options': 'undef', + 'defaultValue': '4', + }, + ] + }, + ], + 'pages': [ + { + 'page': 'create', + 'title': 'Benutzer hinzufügen', + 'pageType': 'create', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'change', + 'title': 'Benutzer ändern', + 'pageType': 'change', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + }, + ], + 'buttonBar': [ + { + 'modelType': 'button', + 'name': 'set_password', + 'label': 'Passwort ändern', + 'buttonType': 'custom', + }, + ] + } + ] + }, + { + 'page': 'password', + 'title': 'Passwort ändern', + 'pageType': 'change', + 'sql': 'update_pw', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'text', + 'text': 'Ändern des Passworts von Benutzer ~user~', + 'options': 'placeholder h3' + }, + { + 'modelType': 'textField', + 'name': 'user_password', + 'label': 'Passwort', + 'options': 'password', + 'validators': 'required', + }, + { + 'modelType': 'textField', + 'name': 'repetition', + 'label': 'Wiederholung', + 'options': 'password', + 'validators': 'required equals=user_password' + }, + ] + } + ] + }, + { + 'page': 'delete', + 'title': 'Benutzer löschen', + 'pageType': 'delete', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'allDbFields', + } + ] + } + ] + }, + { + 'page': 'list', + 'title': 'Benutzer', + 'pageType': 'list', + 'tableColumns': + 'user_id user_name user_displayname user_email user_role', + 'tableTitles': ';Id;Name;Anzeigename;EMail;Rolle', + 'sections': [ + { + 'sectionType': 'filterPanel', + 'children': [ + { + 'modelType': 'dbReference', + 'filterType': 'pattern', + 'name': 'user_name', + 'column': 'user_name', + 'toolTip': + 'Filter bezüglich des Namens der anzuzeigenden Einträge: Joker "*" (beliebiger String) ist erlaubt.' + }, + { + 'modelType': 'dbReference', + 'name': 'user_role', + 'filterType': 'equals', + 'column': 'user_role', + 'toolTip': + 'Filter bezüglich der Rolle der anzuzeigenden Einträge. "-" bedeutet keine Einschränkung', + } + ], + 'buttonBar': [ + { + 'modelType': 'button', + 'buttonType': 'custom', + 'name': 'new', + 'label': 'Neuer Benutzer', + 'toolTip': 'Erzeugen eines neuen Benutzers' + }, + ], + } + ] + }, + { + 'page': 'login', + 'title': 'Anmelden', + 'pageType': 'change', + 'options': 'noAutoButton', + 'sections': [ + { + 'sectionType': 'simpleForm', + 'children': [ + { + 'modelType': 'textField', + 'name': 'user', + 'label': 'Benutzer oder EMail', + 'validators': 'required', + }, + { + 'modelType': 'textField', + 'name': 'password', + 'label': 'Password', + 'options': 'password', + 'validators': 'required', + }, + ], + 'buttonBar': [ + { + 'modelType': 'button', + 'buttonType': 'custom', + 'name': 'login', + 'label': 'Anmelden', + }, + ] + } + ] + }, + ] + }; + + UserModel(BaseLogger logger) : super(mapUser, logger); + + /// Returns the name including the names of the parents. + @override + String fullName() => name; + + @override + String widgetName() => name; +} diff --git a/lib/src/model/page_model.dart b/lib/src/model/page_model.dart index 2c93ec1..b4991ac 100644 --- a/lib/src/model/page_model.dart +++ b/lib/src/model/page_model.dart @@ -1,4 +1,5 @@ import 'package:dart_bones/dart_bones.dart'; +import 'package:flutter_bones/src/widget/page_controller_bones.dart'; import '../helper/name_builder.dart'; import 'button_model.dart'; @@ -10,6 +11,13 @@ import 'widget_model.dart'; typedef FilterWidget = bool Function(WidgetModel item); +/// Returns whether the task specified by [task] and [parameter] can be +/// granted. +/// The type of [parameter] depends of the task: +/// Example: in list pages: TaskRight.edit/delete: parameter is the record of +/// the table row. +typedef TaskRightCallback = bool Function(TaskRight task, dynamic parameter); + /// Represents one screen of the module. class PageModel extends ModelBase { static final regExprOptions = RegExp(r'^(noAutoButton)$'); @@ -20,6 +28,7 @@ class PageModel extends ModelBase { final buttonsDeprecated = []; final widgetsDeprecated = []; final models = {}; + TaskRightCallback taskRightCallback; String sql; String title; List tableTitles; @@ -37,7 +46,7 @@ class PageModel extends ModelBase { final name = model.name; if (models.containsKey(name)) { logger.error('model ${model.fullName()} already defined: ' + - fieldByName(name).fullName()); + models[name].fullName()); } else { models[model.name] = model; } @@ -139,29 +148,47 @@ class PageModel extends ModelBase { } if (!options.contains('noAutoButton')) { - final section = sections[0]; + final section = sections.isEmpty ? null : sections[0]; switch (pageModelType) { case PageModelType.list: if (buttonByName('search', required: false) == null) { final model = ButtonModel.direct(section, this, 'search', 'Suchen', ButtonModelType.search, [], logger); addModel(model); - sections[0].buttonBar.insert(0, model); + if (sections.isEmpty) { + logger.error('missing section in ${fullName()}'); + } else { + sections[0].buttonBar.insert(0, model); + } } break; case PageModelType.create: case PageModelType.change: + case PageModelType.delete: if (buttonByName('cancel', required: false) == null) { final model = ButtonModel.direct(section, this, 'cancel', 'Abbruch', ButtonModelType.cancel, [], logger); addModel(model); - sections[0].buttonBar.insert(0, model); + if (sections.isEmpty) { + logger.error('missing sections in ${fullName()}'); + } else { + sections[0].buttonBar.insert(0, model); + } } - if (buttonByName('store', required: false) == null) { - final model = ButtonModel.direct(section, this, 'store', - 'Speichern', ButtonModelType.store, [], logger); + final buttonName = + pageModelType == PageModelType.delete ? 'delete' : 'store'; + final buttonText = + pageModelType == PageModelType.delete ? 'Löschen' : 'Speichern'; + + if (buttonByName(buttonName, required: false) == null) { + final model = ButtonModel.direct(section, this, buttonName, + buttonText, ButtonModelType.store, [], logger); addModel(model); - sections[0].buttonBar.insert(0, model); + if (sections.isEmpty) { + logger.error('missing sections in ${fullName()}'); + } else { + sections[0].buttonBar.insert(0, model); + } } break; default: @@ -192,7 +219,7 @@ class PageModel extends ModelBase { enum PageModelType { change, create, + custom, delete, list, - overview, } diff --git a/lib/src/model/standard/configuration_model.dart b/lib/src/model/standard/configuration_model.dart deleted file mode 100644 index eb20373..0000000 --- a/lib/src/model/standard/configuration_model.dart +++ /dev/null @@ -1,146 +0,0 @@ -import 'package:dart_bones/dart_bones.dart'; - -import '../module_model.dart'; - -/// -class ConfigurationModel extends ModuleModel { - static final yamlMap = { - // - 'module': 'configuration', - 'tables': [ - { - // configuration_id | configuration_scope | configuration_property | configuration_order | configuration_type - // | configuration_value | configuration_description - 'table': 'configuration', - 'columns': [ - { - 'column': 'configuration_id', - 'dataType': 'int', - 'label': 'Id', - 'options': 'primary', - }, - { - 'column': 'configuration_scope', - 'dataType': 'string', - 'label': 'Bereich', - 'size': 64, - 'options': 'notnull', - 'validators': r'required regExpr=i/^\w+$', - 'validatorsText': - 'regExpr=Nur Buchstaben, Ziffern und Unterstrich erlaubt', - }, - { - 'column': 'configuration_property', - 'dataType': 'string', - 'size': 64, - 'label': 'Eigenschaft', - }, - { - 'column': 'configuration_order', - 'dataType': 'int', - 'label': 'Reihe', - }, - { - 'column': 'configuration_type', - 'dataType': 'string', - 'size': 16, - 'label': 'Datentyp', - 'listType': 'explicite', - 'texts': ';bool;date;float;int;string', - 'validators': 'required', - }, - { - 'column': 'configuration_value', - 'dataType': 'string', - 'size': 255, - 'label': 'Wert', - }, - { - 'column': 'configuration_description', - 'dataType': 'string', - 'size': 255, - 'label': 'Beschreibung', - }, - ] - }, - ], - 'pages': [ - { - 'page': 'create', - 'title': 'Konfiguration hinzufügen', - 'pageType': 'create', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'allDbFields', - } - ] - } - ] - }, - { - 'page': 'change', - 'title': 'Konfiguration ändern', - 'pageType': 'change', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'allDbFields', - } - ] - } - ] - }, - { - 'page': 'list', - 'title': 'Konfiguration', - 'pageType': 'list', - 'tableColumns': - 'configuration_id configuration_scope configuration_property configuration_order configuration_value configuration_type', - 'tableTitles': ';Id;Bereich;Eigenschaft;Reihe;Wert;Typ', - 'sections': [ - { - 'sectionType': 'filterPanel', - 'children': [ - { - 'name': 'configuration_scope', - 'label': 'Bereich', - 'modelType': 'textField', - 'filterType': 'pattern', - //'options': 'undef', - }, - { - 'name': 'configuration_property', - 'label': 'Eigenschaft', - 'modelType': 'textField', - 'filterType': 'pattern', - }, - ], - 'buttonBar': [ - { - 'modelType': 'button', - 'buttonType': 'custom', - 'name': 'new', - 'label': 'Neue Konfiguration', - 'toolTip': 'Erzeugen einer neuen Konfiguration' - }, - ], - } - ] - }, - ], - }; - - ConfigurationModel(BaseLogger logger) : super(yamlMap, logger); - - /// Returns the name including the names of the parents. - @override - String fullName() => name; - - @override - String widgetName() => name; -} diff --git a/lib/src/model/standard/menu_model.dart b/lib/src/model/standard/menu_model.dart deleted file mode 100644 index 10c6054..0000000 --- a/lib/src/model/standard/menu_model.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:dart_bones/dart_bones.dart'; - -import '../module_model.dart'; - -class MenuModel extends ModuleModel { - static final mapMenu = { - 'module': 'menu', - 'tables': [ - { - 'table': 'menu', - 'columns': [ - { - 'column': 'menu_id', - 'dataType': 'int', - 'label': 'Id', - 'options': 'primary', - }, - { - 'column': 'menu_role', - 'dataType': 'reference', - 'label': 'Rolle', - 'foreignKey': 'role.role_id role_name', - 'listType': 'dbColumn', - 'listOption': 'all.role.list;role_name role_id;', - }, - { - 'column': 'menu_starter', - 'dataType': 'reference', - 'label': 'Programmpunkt', - 'foreignKey': 'starter.starter_id starter_name', - 'listType': 'dbColumn', - 'listOption': 'all.starter.list;starter_name starter_id;', - }, - ] - }, - ], - 'pages': [ - { - 'page': 'create', - 'title': 'Startmenü hinzufügen', - 'pageType': 'create', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'allDbFields', - } - ] - } - ] - }, - { - 'page': 'change', - 'title': 'Startmenü ändern', - 'pageType': 'change', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'allDbFields', - }, - ], - 'buttonBar': [ - { - 'modelType': 'button', - 'buttonType': 'custom', - 'name': 'new', - 'label': 'Neues Startmenü', - 'toolTip': 'Erzeugen eines neuen Startmenüs' - }, - ], - } - ] - }, - { - 'page': 'list', - 'title': 'Startmenü', - 'pageType': 'list', - 'tableColumns': 'menu_id menu_role menu_order menu_name', - 'tableTitles': ';Id;Rolle;Reihe;Programmpunkt', - 'sections': [ - { - 'sectionType': 'filterPanel', - 'children': [ - { - 'modelType': 'dbReference', - 'filterType': 'pattern', - 'name': 'menu_role', - 'column': 'menu_role', - 'toolTip': - 'Filter bezüglich der Rolle der anzuzeigenden Einträge.' - }, - ] - } - ] - }, - ] - }; - - MenuModel(BaseLogger logger) : super(mapMenu, logger); - - /// Returns the name including the names of the parents. - @override - String fullName() => name; - - @override - String widgetName() => name; -} diff --git a/lib/src/model/standard/role_model.dart b/lib/src/model/standard/role_model.dart deleted file mode 100644 index 1001014..0000000 --- a/lib/src/model/standard/role_model.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:dart_bones/dart_bones.dart'; - -import '../module_model.dart'; - -class RoleModel extends ModuleModel { - RoleModel(BaseLogger logger) : super(yamlMap, logger); - - static final yamlMap = { - 'module': 'role', - 'tables': [ - { - 'table': 'role', - 'columns': [ - { - 'column': 'role_id', - 'dataType': 'int', - 'label': 'Id', - 'options': 'primary', - }, - { - 'column': 'role_name', - 'dataType': 'string', - 'label': 'Rolle', - 'size': 32, - 'options': 'unique notnull', - }, - { - 'column': 'role_priority', - 'dataType': 'int', - 'label': 'Priorität', - 'validators': 'required minInt=1 maxInt=99', - }, - { - 'column': 'role_active', - 'dataType': 'bool', - 'label': 'Aktiv', - }, - ] - }, - ], - 'pages': [ - { - 'page': 'create', - 'title': 'Rolle hinzufügen', - 'pageType': 'create', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'allDbFields', - } - ] - } - ] - }, - { - 'page': 'change', - 'title': 'Rolle ändern', - 'pageType': 'change', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'allDbFields', - } - ] - } - ] - }, - { - 'page': 'list', - 'title': 'Rollen', - 'pageType': 'list', - 'tableColumns': 'role_id role_name role_priority', - 'tableTitles': ';Id;Rolle;Priorität', - 'sections': [ - { - 'sectionType': 'filterPanel', - 'children': [ - { - 'modelType': 'textField', - 'filterType': 'pattern', - 'name': 'role_name', - 'label': 'Name', - 'toolTip': - 'Suchmuster des Rollennamens: Joker: "*" (beliebiger Text), z.B. "*min*"' - }, - ], - 'buttonBar': [ - { - 'modelType': 'button', - 'buttonType': 'custom', - 'name': 'new', - 'label': 'Neue Rolle', - 'toolTip': 'Erzeugen einer neuen Rolle' - }, - ], - } - ], - } - ] - }; - - /// Returns the name including the names of the parents. - @override - String fullName() => name; - - @override - String widgetName() => name; -} diff --git a/lib/src/model/standard/starter_model.dart b/lib/src/model/standard/starter_model.dart deleted file mode 100644 index b8a8efd..0000000 --- a/lib/src/model/standard/starter_model.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:dart_bones/dart_bones.dart'; - -import '../module_model.dart'; - -class StarterModel extends ModuleModel { - static final mapStarter = { - 'module': 'starter', - 'tables': [ - { - 'table': 'starter', - 'columns': [ - { - 'column': 'starter_id', - 'dataType': 'int', - 'label': 'Id', - 'options': 'primary', - }, - { - 'column': 'starter_name', - 'dataType': 'string', - 'label': 'Name', - 'size': 64, - 'options': 'unique notnull', - }, - { - 'column': 'starter_icon', - 'dataType': 'reference', - 'label': 'Bild', - 'foreignKey': 'configuration.configuration_id configuration_value', - 'listType': 'configuration', - 'listOption': - 'std.scope.icons;configuration_value configuration_id;', - }, - ] - }, - ], - 'pages': [ - { - 'page': 'create', - 'title': 'Programmpunkt hinzufügen', - 'pageType': 'create', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'allDbFields', - } - ] - } - ] - }, - { - 'page': 'change', - 'title': 'Programmpunkt hinzufügen', - 'pageType': 'change', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'allDbFields', - }, - ] - } - ] - }, - { - 'page': 'list', - 'title': 'Programmpunkte', - 'pageType': 'list', - 'tableColumns': 'starter_id starter_name starter_icon', - 'tableTitles': ';Id;Name;Bild', - 'sections': [ - { - 'sectionType': 'filterPanel', - 'children': [ - { - 'modelType': 'dbReference', - 'filterType': 'pattern', - 'name': 'starter_name', - 'column': 'starter_name', - 'toolTip': - 'Filter bezüglich des Namens der anzuzeigenden Einträge: Joker "*" (beliebiger String) ist erlaubt.' - }, - ], - 'buttonBar': [ - { - 'modelType': 'button', - 'buttonType': 'custom', - 'name': 'new', - 'label': 'Neuer Programmpunkt', - 'toolTip': 'Erzeugen eines neuen Programmpunkts' - }, - ], - } - ] - }, - ] - }; - - StarterModel(BaseLogger logger) : super(mapStarter, logger); - - /// Returns the name including the names of the parents. - @override - String fullName() => name; - - @override - String widgetName() => name; -} diff --git a/lib/src/model/standard/user_model.dart b/lib/src/model/standard/user_model.dart deleted file mode 100644 index 14ffdaa..0000000 --- a/lib/src/model/standard/user_model.dart +++ /dev/null @@ -1,214 +0,0 @@ -import 'package:dart_bones/dart_bones.dart'; - -import '../module_model.dart'; - -class UserModel extends ModuleModel { - static final mapUser = { - 'module': 'user', - 'tables': [ - { - 'table': 'user', - 'columns': [ - { - 'column': 'user_id', - 'dataType': 'int', - 'label': 'Id', - 'options': 'primary', - }, - { - 'column': 'user_name', - 'dataType': 'string', - 'label': 'User', - 'size': 64, - 'options': 'unique notnull', - }, - { - 'column': 'user_displayname', - 'dataType': 'string', - 'label': 'Anzeigename', - 'size': 32, - 'options': 'unique notnull', - }, - { - 'column': 'user_email', - 'dataType': 'string', - 'label': 'EMail', - 'size': 128, - 'options': 'unique notnull', - 'validators': 'email', - }, - { - 'column': 'user_password', - 'dataType': 'string', - 'label': 'Passwort', - 'size': 128, - 'options': 'password hidden', - }, - { - 'column': 'user_role', - 'dataType': 'reference', - 'label': 'Rolle', - 'foreignKey': 'role.role_id role_name', - 'listType': 'dbColumn', - 'listOption': 'all.role.list;role_name role_id;:role_name=%', - 'options': 'undef', - 'defaultValue': '4', - }, - ] - }, - ], - 'pages': [ - { - 'page': 'create', - 'title': 'Benutzer hinzufügen', - 'pageType': 'create', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'allDbFields', - } - ] - } - ] - }, - { - 'page': 'change', - 'title': 'Benutzer ändern', - 'pageType': 'change', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'allDbFields', - }, - { - 'modelType': 'button', - 'name': 'set_password', - 'label': 'Passwort ändern', - 'buttonType': 'custom', - }, - ] - } - ] - }, - { - 'page': 'password', - 'title': 'Passwort ändern', - 'pageType': 'change', - 'sql': 'update_pw', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'text', - 'text': 'Ändern des Passworts von Benutzer ~user~', - 'options': 'placeholder h3' - }, - { - 'modelType': 'textField', - 'name': 'user_password', - 'label': 'Passwort', - 'options': 'password', - 'validators': 'required', - }, - { - 'modelType': 'textField', - 'name': 'repetition', - 'label': 'Wiederholung', - 'options': 'password', - 'validators': 'required equals=user_password' - }, - ] - } - ] - }, - { - 'page': 'list', - 'title': 'Benutzer', - 'pageType': 'list', - 'tableColumns': - 'user_id user_name user_displayname user_email user_role', - 'tableTitles': ';Id;Name;Anzeigename;EMail;Rolle', - 'sections': [ - { - 'sectionType': 'filterPanel', - 'children': [ - { - 'modelType': 'dbReference', - 'filterType': 'pattern', - 'name': 'user_name', - 'column': 'user_name', - 'toolTip': - 'Filter bezüglich des Namens der anzuzeigenden Einträge: Joker "*" (beliebiger String) ist erlaubt.' - }, - { - 'modelType': 'dbReference', - 'name': 'user_role', - 'filterType': 'equals', - 'column': 'user_role', - 'toolTip': - 'Filter bezüglich der Rolle der anzuzeigenden Einträge. "-" bedeutet keine Einschränkung', - } - ], - 'buttonBar': [ - { - 'modelType': 'button', - 'buttonType': 'custom', - 'name': 'new', - 'label': 'Neuer Benutzer', - 'toolTip': 'Erzeugen eines neuen Benutzers' - }, - ], - } - ] - }, - { - 'page': 'login', - 'title': 'Anmelden', - 'pageType': 'change', - 'options': 'noAutoButton', - 'sections': [ - { - 'sectionType': 'simpleForm', - 'children': [ - { - 'modelType': 'textField', - 'name': 'user', - 'label': 'Benutzer oder EMail', - 'validators': 'required', - }, - { - 'modelType': 'textField', - 'name': 'password', - 'label': 'Password', - 'options': 'password', - 'validators': 'required', - }, - ], - 'buttonBar': [ - { - 'modelType': 'button', - 'buttonType': 'custom', - 'name': 'login', - 'label': 'Anmelden', - }, - ] - } - ] - }, - ] - }; - - UserModel(BaseLogger logger) : super(mapUser, logger); - - /// Returns the name including the names of the parents. - @override - String fullName() => name; - - @override - String widgetName() => name; -} diff --git a/lib/src/page/configuration/configuration_change_page.dart b/lib/src/page/configuration/configuration_change_page.dart index d8848cf..18afa3d 100644 --- a/lib/src/page/configuration/configuration_change_page.dart +++ b/lib/src/page/configuration/configuration_change_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -30,8 +30,8 @@ class ConfigurationChangePage extends StatefulWidget { @override ConfigurationChangePageState createState() { //! === BeginOfCall === - final rc = - ConfigurationChangePageStateCustomized(primaryId, applicationData, initialRow); + final rc = ConfigurationChangePageStateCustomized( + primaryId, applicationData, initialRow); //! === EndOfCall === /// for unittests: @@ -40,20 +40,22 @@ class ConfigurationChangePage extends StatefulWidget { } } -abstract class ConfigurationChangePageState extends State - implements RedrawPage { +abstract class ConfigurationChangePageState + extends State implements RedrawPage { final ApplicationData applicationData; final int primaryId; final Map initialRow; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$configurationChangePageModule-$configurationChangePageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: + '$configurationChangePageModule-$configurationChangePageName'); ConfigurationController controller; //! === BeginOfCustomizedVars2 === //! === EndOfCustomizedVars2 === //! === BeginOfConstructor2 === - ConfigurationChangePageState(this.primaryId, this.applicationData, this.initialRow); + ConfigurationChangePageState( + this.primaryId, this.applicationData, this.initialRow); //! === EndOfConstructor2 === @@ -65,7 +67,7 @@ abstract class ConfigurationChangePageState extends State ConfigurationChangePage(id, applicationData, row))); } + + @override + void startDelete(int id, Map row) { + applicationData.pushCaller(this); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + ConfigurationDeletePage(id, applicationData, row))); + } } diff --git a/lib/src/page/configuration/configuration_create_page.dart b/lib/src/page/configuration/configuration_create_page.dart index 939a529..d020288 100644 --- a/lib/src/page/configuration/configuration_create_page.dart +++ b/lib/src/page/configuration/configuration_create_page.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -34,12 +34,13 @@ class ConfigurationCreatePage extends StatefulWidget { } } -abstract class ConfigurationCreatePageState extends State - implements RedrawPage { +abstract class ConfigurationCreatePageState + extends State implements RedrawPage { final ApplicationData applicationData; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$configurationCreatePageModule-$configurationCreatePageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: + '$configurationCreatePageModule-$configurationCreatePageName'); ConfigurationController controller; @@ -58,7 +59,7 @@ abstract class ConfigurationCreatePageState extends State implements RedrawPage { + final ApplicationData applicationData; + final int primaryId; + final Map initialRow; + final GlobalKey _formKey = GlobalKey( + debugLabel: + '$configurationDeletePageModule-$configurationDeletePageName'); + + ConfigurationController controller; + + //! === BeginOfCustomizedVars2 === + //! === EndOfCustomizedVars2 === + //! === BeginOfConstructor2 === + ConfigurationDeletePageState( + this.primaryId, this.applicationData, this.initialRow); + + //! === EndOfConstructor2 === + + @override + Widget build(BuildContext context) { + controller.beginOfBuild(context); + //! === BeginOfScaffold === + return Scaffold( + appBar: applicationData.appBarBuilder(controller.page.title), + drawer: applicationData.drawerBuilder(context), + bottomNavigationBar: applicationData.footerBuilder().widget(controller), + body: formDialog( + key: _formKey, + pageController: controller, + configuration: applicationData.configuration, + primaryId: primaryId, + initialRow: initialRow, + )); + //! === EndOfScaffold === + } + + /// Customize the page, e.g. modify the default widget list given by the model + void customize(); + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + controller = ConfigurationController( + _formKey, this, configurationDeletePageName, context, applicationData); + controller.initialize(); + customize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } +} + +//! === EndOfGeneratedCode: Below you can change manually: + +class ConfigurationDeletePageStateCustomized + extends ConfigurationDeletePageState { + ConfigurationDeletePageStateCustomized( + int primaryId, ApplicationData applicationData, Map initialRow) + : super(primaryId, applicationData, initialRow); + + @override + void customize() { + // ToDo: write code if needed + } +} diff --git a/lib/src/page/configuration/configuration_list_page.dart b/lib/src/page/configuration/configuration_list_page.dart index b2ab898..b3141cb 100644 --- a/lib/src/page/configuration/configuration_list_page.dart +++ b/lib/src/page/configuration/configuration_list_page.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bones/src/widget/view.dart'; import '../../model/page_model.dart'; -import '../../widget/list_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'configuration_controller.dart'; @@ -38,8 +38,8 @@ abstract class ConfigurationListPageState extends State implements RedrawPage { final ApplicationData applicationData; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$configurationListPageModule-$configurationListPageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$configurationListPageModule-$configurationListPageName'); Iterable rowsDeprecated; ConfigurationController controller; @@ -65,7 +65,16 @@ abstract class ConfigurationListPageState extends State titles: ListForm.stringsToTitles(controller.page.tableTitles), columnNames: controller.page.tableColumns ?? [], rows: controller.listRows ?? [], - showEditIcon: true, + showDeleteIcon: controller.rightOf( + configurationListPageModule, + configurationListPageName, + configurationListPageType, + TaskRight.listEdit), + showEditIcon: controller.rightOf( + configurationListPageModule, + configurationListPageName, + configurationListPageType, + TaskRight.listDelete), pageController: controller, buttons: [ ButtonBar( @@ -88,8 +97,8 @@ abstract class ConfigurationListPageState extends State @override void initState() { super.initState(); - controller = - ConfigurationController(_formKey, this, configurationListPageName, context, applicationData); + controller = ConfigurationController( + _formKey, this, configurationListPageName, context, applicationData); controller.initialize(); customize(); } diff --git a/lib/src/page/menu/menu_change_page.dart b/lib/src/page/menu/menu_change_page.dart index 6eeebab..e115ed8 100644 --- a/lib/src/page/menu/menu_change_page.dart +++ b/lib/src/page/menu/menu_change_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -45,8 +45,8 @@ abstract class MenuChangePageState extends State final ApplicationData applicationData; final int primaryId; final Map initialRow; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$menuChangePageModule-$menuChangePageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$menuChangePageModule-$menuChangePageName'); MenuController controller; @@ -65,7 +65,7 @@ abstract class MenuChangePageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, @@ -87,8 +87,8 @@ abstract class MenuChangePageState extends State @override void initState() { super.initState(); - controller = - MenuController(_formKey, this, 'change', context, applicationData); + controller = MenuController( + _formKey, this, menuChangePageName, context, applicationData); controller.initialize(); customize(); } diff --git a/lib/src/page/menu/menu_controller.dart b/lib/src/page/menu/menu_controller.dart index 857f0af..328b54a 100644 --- a/lib/src/page/menu/menu_controller.dart +++ b/lib/src/page/menu/menu_controller.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../model/standard/menu_model.dart'; +import '../../model/module/menu_model.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'menu_change_page.dart'; +import 'menu_delete_page.dart'; class MenuController extends PageControllerBones { /// Controller for a page named [pageName]. @@ -23,4 +24,13 @@ class MenuController extends PageControllerBones { MaterialPageRoute( builder: (context) => MenuChangePage(id, applicationData, row))); } + + @override + void startDelete(int id, Map row) { + applicationData.pushCaller(this); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => MenuDeletePage(id, applicationData, row))); + } } diff --git a/lib/src/page/menu/menu_create_page.dart b/lib/src/page/menu/menu_create_page.dart index 9f8fcad..b4dca06 100644 --- a/lib/src/page/menu/menu_create_page.dart +++ b/lib/src/page/menu/menu_create_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -39,8 +39,8 @@ abstract class MenuCreatePageState extends State implements RedrawPage { final ApplicationData applicationData; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$menuCreatePageModule-$menuCreatePageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$menuCreatePageModule-$menuCreatePageName'); MenuController controller; @@ -59,7 +59,7 @@ abstract class MenuCreatePageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, @@ -79,8 +79,8 @@ abstract class MenuCreatePageState extends State @override void initState() { super.initState(); - controller = - MenuController(_formKey, this, menuCreatePageName, context, applicationData); + controller = MenuController( + _formKey, this, menuCreatePageName, context, applicationData); controller.initialize(); customize(); } diff --git a/lib/src/page/menu/menu_delete_page.dart b/lib/src/page/menu/menu_delete_page.dart new file mode 100644 index 0000000..bcb71ef --- /dev/null +++ b/lib/src/page/menu/menu_delete_page.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; + +import '../../helper/settings.dart'; +import '../../widget/dialog_bones.dart'; +import '../../widget/page_controller_bones.dart'; +import '../application_data.dart'; +import '../../model/page_model.dart'; +import 'menu_controller.dart'; + +const menuDeletePageModule = 'menu'; +const menuDeletePageName = 'delete'; +const menuDeletePageType = PageModelType.delete; +//! === BeginOfGeneratedCode: Delete only in areas marked as customized + +class MenuDeletePage extends StatefulWidget { + final ApplicationData applicationData; + final Map initialRow; + final logger = Settings().logger; + final int primaryId; + + //! === BeginOfCustomizedVars1 === + //! === EndOfCustomizedVars1 === + //! === BeginOfConstructor1 === + MenuDeletePage(this.primaryId, this.applicationData, this.initialRow, + {Key key}) + : super(key: key); + + //! === EndOfConstructor1 === + + @override + MenuDeletePageState createState() { + //! === BeginOfCall === + final rc = + MenuDeletePageStateCustomized(primaryId, applicationData, initialRow); + //! === EndOfCall === + + /// for unittests: + applicationData.lastModuleState = rc; + return rc; + } +} + +abstract class MenuDeletePageState extends State + implements RedrawPage { + final ApplicationData applicationData; + final int primaryId; + final Map initialRow; + final GlobalKey _formKey = GlobalKey( + debugLabel: '$menuDeletePageModule-$menuDeletePageName'); + + MenuController controller; + + //! === BeginOfCustomizedVars2 === + //! === EndOfCustomizedVars2 === + //! === BeginOfConstructor2 === + MenuDeletePageState(this.primaryId, this.applicationData, this.initialRow); + + //! === EndOfConstructor2 === + + @override + Widget build(BuildContext context) { + controller.beginOfBuild(context); + //! === BeginOfScaffold === + return Scaffold( + appBar: applicationData.appBarBuilder(controller.page.title), + drawer: applicationData.drawerBuilder(context), + bottomNavigationBar: applicationData.footerBuilder().widget(controller), + body: formDialog( + key: _formKey, + pageController: controller, + configuration: applicationData.configuration, + primaryId: primaryId, + initialRow: initialRow, + )); + //! === EndOfScaffold === + } + + /// Customize the page, e.g. modify the default widget list given by the model + void customize(); + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + controller = MenuController( + _formKey, this, menuDeletePageName, context, applicationData); + controller.initialize(); + customize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } +} + +//! === EndOfGeneratedCode: Below you can change manually: + +class MenuDeletePageStateCustomized extends MenuDeletePageState { + MenuDeletePageStateCustomized( + int primaryId, ApplicationData applicationData, Map initialRow) + : super(primaryId, applicationData, initialRow); + + @override + void customize() { + // ToDo: write code if needed + } +} diff --git a/lib/src/page/menu/menu_list_page.dart b/lib/src/page/menu/menu_list_page.dart index 6bf709a..8b67425 100644 --- a/lib/src/page/menu/menu_list_page.dart +++ b/lib/src/page/menu/menu_list_page.dart @@ -2,7 +2,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/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'menu_controller.dart'; @@ -65,7 +65,10 @@ abstract class MenuListPageState extends State titles: ListForm.stringsToTitles(controller.page.tableTitles), columnNames: controller.page.tableColumns ?? [], rows: controller.listRows ?? [], - showEditIcon: true, + showDeleteIcon: controller.rightOf(menuListPageModule, menuListPageName, + menuListPageType, TaskRight.listEdit), + showEditIcon: controller.rightOf(menuListPageModule, menuListPageName, + menuListPageType, TaskRight.listDelete), pageController: controller, buttons: [ ButtonBar( @@ -88,8 +91,8 @@ abstract class MenuListPageState extends State @override void initState() { super.initState(); - controller = - MenuController(_formKey, this, menuListPageName, context, applicationData); + controller = MenuController( + _formKey, this, menuListPageName, context, applicationData); controller.initialize(); customize(); } diff --git a/lib/src/page/role/role_change_page.dart b/lib/src/page/role/role_change_page.dart index cb75dd9..62a5bcc 100644 --- a/lib/src/page/role/role_change_page.dart +++ b/lib/src/page/role/role_change_page.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../widget/edit_form.dart'; +import '../../model/page_model.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; -import '../../model/page_model.dart'; import 'role_controller.dart'; const roleChangePageModule = 'role'; -const roleChangePageName = 'create'; -const roleChangePageType = PageModelType.create; +const roleChangePageName = 'change'; +const roleChangePageType = PageModelType.change; //! === BeginOfGeneratedCode: Change only in areas marked as customized class RoleChangePage extends StatefulWidget { @@ -45,8 +45,8 @@ abstract class RoleChangePageState extends State final ApplicationData applicationData; final int primaryId; final Map initialRow; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$roleChangePageModule-$roleChangePageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$roleChangePageModule-$roleChangePageName'); RoleController controller; @@ -65,7 +65,7 @@ abstract class RoleChangePageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, @@ -87,8 +87,8 @@ abstract class RoleChangePageState extends State @override void initState() { super.initState(); - controller = - RoleController(_formKey, this, 'change', context, applicationData); + controller = RoleController( + _formKey, this, roleChangePageName, context, applicationData); controller.initialize(); customize(); } @@ -105,7 +105,8 @@ abstract class RoleChangePageState extends State //! === EndOfGeneratedCode: Below you can change manually: -class RoleChangePageStateCustomized extends RoleChangePageState { +class RoleChangePageStateCustomized extends RoleChangePageState + implements SingleRecordPage { RoleChangePageStateCustomized( int primaryId, ApplicationData applicationData, Map initialRow) : super(primaryId, applicationData, initialRow); @@ -114,4 +115,9 @@ class RoleChangePageStateCustomized extends RoleChangePageState { void customize() { // ToDo: write code if needed } + + @override + int getPrimaryKey() { + return primaryId; + } } diff --git a/lib/src/page/role/role_controller.dart b/lib/src/page/role/role_controller.dart index 0ccf112..af7594a 100644 --- a/lib/src/page/role/role_controller.dart +++ b/lib/src/page/role/role_controller.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../model/standard/role_model.dart'; +import '../../model/module/role_model.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'role_change_page.dart'; +import 'role_delete_page.dart'; class RoleController extends PageControllerBones { /// Controller for a page named [pageName]. @@ -23,4 +24,13 @@ class RoleController extends PageControllerBones { MaterialPageRoute( builder: (context) => RoleChangePage(id, applicationData, row))); } + + @override + void startDelete(int id, Map row) { + applicationData.pushCaller(this); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => RoleDeletePage(id, applicationData, row))); + } } diff --git a/lib/src/page/role/role_create_page.dart b/lib/src/page/role/role_create_page.dart index 13fe1f2..86dc814 100644 --- a/lib/src/page/role/role_create_page.dart +++ b/lib/src/page/role/role_create_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -39,8 +39,8 @@ abstract class RoleCreatePageState extends State implements RedrawPage { final ApplicationData applicationData; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$roleCreatePageModule-$roleCreatePageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$roleCreatePageModule-$roleCreatePageName'); RoleController controller; @@ -59,7 +59,7 @@ abstract class RoleCreatePageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, @@ -79,8 +79,8 @@ abstract class RoleCreatePageState extends State @override void initState() { super.initState(); - controller = - RoleController(_formKey, this, roleCreatePageName, context, applicationData); + controller = RoleController( + _formKey, this, roleCreatePageName, context, applicationData); controller.initialize(); customize(); } diff --git a/lib/src/page/role/role_custom_page.dart b/lib/src/page/role/role_custom_page.dart new file mode 100644 index 0000000..c83d0c2 --- /dev/null +++ b/lib/src/page/role/role_custom_page.dart @@ -0,0 +1,110 @@ +import 'package:flutter/material.dart'; + +import '../../helper/settings.dart'; +import '../../model/page_model.dart'; +import '../../widget/page_controller_bones.dart'; +import '../application_data.dart'; +import 'role_controller.dart'; + +const roleCustomPageModule = 'role'; +const roleCustomPageName = 'custom'; +const roleCustomPageType = PageModelType.custom; +//! === BeginOfGeneratedCode: Change only in areas marked as customized + +class RoleCustomPage extends StatefulWidget { + final ApplicationData applicationData; + final logger = Settings().logger; + + //! === BeginOfCustomizedVars1 === + //! === EndOfCustomizedVars1 === + //! === BeginOfConstructor1 === + RoleCustomPage(this.applicationData, {Key key}) : super(key: key); + + //! === EndOfConstructor1 === + + @override + RoleCustomPageState createState() { + //! === BeginOfCall === + final rc = RoleCustomPageStateCustomized(applicationData); + //! === EndOfCall === + + /// for unittests: + applicationData.lastModuleState = rc; + return rc; + } +} + +abstract class RoleCustomPageState extends State + implements RedrawPage { + final ApplicationData applicationData; + + final GlobalKey _formKey = GlobalKey( + debugLabel: '$roleCustomPageModule-$roleCustomPageName'); + + RoleController controller; + + //! === BeginOfCustomizedVars2 === + //! === EndOfCustomizedVars2 === + //! === BeginOfConstructor2 === + RoleCustomPageState(this.applicationData); + + //! === EndOfConstructor2 === + + @override + Widget build(BuildContext context) { + controller?.beginOfBuild(context); + return buildScaffold(context); + } + + // Builds the widget tree of the page. + Widget buildScaffold(BuildContext context); + + /// Customize the page, e.g. modify the default widget list given by the model + void customize(); + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + controller = RoleController( + _formKey, this, roleCustomPageName, context, applicationData); + controller.initialize(); + customize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } +} + +//! === EndOfGeneratedCode: Below you can change manually: + +class RoleCustomPageStateCustomized extends RoleCustomPageState { + RoleCustomPageStateCustomized(ApplicationData applicationData) + : super(applicationData); + + @override + Widget buildScaffold(BuildContext context) { + // ToDo: write code + return Scaffold( + appBar: applicationData.appBarBuilder(controller.page.title), + drawer: applicationData.drawerBuilder(context), + bottomNavigationBar: applicationData.footerBuilder().widget(controller), + body: Text('Hello world')); + } + + @override + void customize() { + // ToDo: write code if needed + } +} diff --git a/lib/src/page/role/role_delete_page.dart b/lib/src/page/role/role_delete_page.dart new file mode 100644 index 0000000..4b06a6e --- /dev/null +++ b/lib/src/page/role/role_delete_page.dart @@ -0,0 +1,122 @@ +import 'package:flutter/material.dart'; + +import '../../helper/settings.dart'; +import '../../model/page_model.dart'; +import '../../widget/dialog_bones.dart'; +import '../../widget/page_controller_bones.dart'; +import '../application_data.dart'; +import 'role_controller.dart'; + +const roleDeletePageModule = 'role'; +const roleDeletePageName = 'delete'; +const roleDeletePageType = PageModelType.delete; +//! === BeginOfGeneratedCode: Delete only in areas marked as customized + +class RoleDeletePage extends StatefulWidget { + final ApplicationData applicationData; + final Map initialRow; + final logger = Settings().logger; + final int primaryId; + + //! === BeginOfCustomizedVars1 === + //! === EndOfCustomizedVars1 === + //! === BeginOfConstructor1 === + RoleDeletePage(this.primaryId, this.applicationData, this.initialRow, + {Key key}) + : super(key: key); + + //! === EndOfConstructor1 === + + @override + RoleDeletePageState createState() { + //! === BeginOfCall === + final rc = + RoleDeletePageStateCustomized(primaryId, applicationData, initialRow); + //! === EndOfCall === + + /// for unittests: + applicationData.lastModuleState = rc; + return rc; + } +} + +abstract class RoleDeletePageState extends State + implements RedrawPage { + final ApplicationData applicationData; + final int primaryId; + final Map initialRow; + final GlobalKey _formKey = GlobalKey( + debugLabel: '$roleDeletePageModule-$roleDeletePageName'); + + RoleController controller; + + //! === BeginOfCustomizedVars2 === + //! === EndOfCustomizedVars2 === + //! === BeginOfConstructor2 === + RoleDeletePageState(this.primaryId, this.applicationData, this.initialRow); + + //! === EndOfConstructor2 === + + @override + Widget build(BuildContext context) { + controller.beginOfBuild(context); + //! === BeginOfScaffold === + return Scaffold( + appBar: applicationData.appBarBuilder(controller.page.title), + drawer: applicationData.drawerBuilder(context), + bottomNavigationBar: applicationData.footerBuilder().widget(controller), + body: formDialog( + key: _formKey, + pageController: controller, + configuration: applicationData.configuration, + primaryId: primaryId, + initialRow: initialRow, + )); + //! === EndOfScaffold === + } + + /// Customize the page, e.g. modify the default widget list given by the model + void customize(); + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + controller = RoleController( + _formKey, this, roleDeletePageName, context, applicationData); + controller.initialize(); + customize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } +} + +//! === EndOfGeneratedCode: Below you can change manually: + +class RoleDeletePageStateCustomized extends RoleDeletePageState + implements SingleRecordPage { + RoleDeletePageStateCustomized( + int primaryId, ApplicationData applicationData, Map initialRow) + : super(primaryId, applicationData, initialRow); + + @override + void customize() { + // ToDo: write code if needed + } + @override + int getPrimaryKey() { + return primaryId; + } +} diff --git a/lib/src/page/role/role_list_page.dart b/lib/src/page/role/role_list_page.dart index 313f207..15d9a33 100644 --- a/lib/src/page/role/role_list_page.dart +++ b/lib/src/page/role/role_list_page.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bones/src/widget/utilities.dart'; import '../../model/page_model.dart'; -import '../../widget/list_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../widget/view.dart'; import '../application_data.dart'; @@ -65,7 +66,10 @@ abstract class RoleListPageState extends State titles: ListForm.stringsToTitles(controller.page.tableTitles), columnNames: controller.page.tableColumns ?? [], rows: controller.listRows ?? [], - showEditIcon: true, + showDeleteIcon: controller.rightOf(roleListPageModule, roleListPageName, + roleListPageType, TaskRight.listEdit), + showEditIcon: controller.rightOf(roleListPageModule, roleListPageName, + roleListPageType, TaskRight.listDelete), pageController: controller, buttons: [ ButtonBar( @@ -88,8 +92,8 @@ abstract class RoleListPageState extends State @override void initState() { super.initState(); - controller = - RoleController(_formKey, this, roleListPageName, context, applicationData); + controller = RoleController( + _formKey, this, roleListPageName, context, applicationData); controller.initialize(); customize(); } @@ -116,5 +120,7 @@ class RoleListPageStateCustomized extends RoleListPageState { button.onPressed = () { controller.goTo(pageType: PageModelType.create); }; + controller.page.taskRightCallback = + utilities.rightsOfRoleListPage(controller); } } diff --git a/lib/src/page/starter/starter_change_page.dart b/lib/src/page/starter/starter_change_page.dart index ea8001f..9a76623 100644 --- a/lib/src/page/starter/starter_change_page.dart +++ b/lib/src/page/starter/starter_change_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -30,8 +30,8 @@ class StarterChangePage extends StatefulWidget { @override StarterChangePageState createState() { //! === BeginOfCall === - final rc = - StarterChangePageStateCustomized(primaryId, applicationData, initialRow); + final rc = StarterChangePageStateCustomized( + primaryId, applicationData, initialRow); //! === EndOfCall === /// for unittests: @@ -45,8 +45,8 @@ abstract class StarterChangePageState extends State final ApplicationData applicationData; final int primaryId; final Map initialRow; - final GlobalKey _formKey = - GlobalKey(debugLabel: 'starter_change'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$starterChangePageModule-$starterChangePageName'); StarterController controller; @@ -65,7 +65,7 @@ abstract class StarterChangePageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, @@ -78,6 +78,7 @@ abstract class StarterChangePageState extends State /// Customize the page, e.g. modify the default widget list given by the model void customize(); + @override void dispose() { controller.dispose(); super.dispose(); @@ -86,8 +87,8 @@ abstract class StarterChangePageState extends State @override void initState() { super.initState(); - controller = - StarterController(_formKey, this, 'change', context, applicationData); + controller = StarterController( + _formKey, this, starterChangePageName, context, applicationData); controller.initialize(); customize(); } diff --git a/lib/src/page/starter/starter_controller.dart b/lib/src/page/starter/starter_controller.dart index 44d5426..af5b455 100644 --- a/lib/src/page/starter/starter_controller.dart +++ b/lib/src/page/starter/starter_controller.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../model/standard/starter_model.dart'; +import '../../model/module/starter_model.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'starter_change_page.dart'; +import 'starter_delete_page.dart'; class StarterController extends PageControllerBones { /// Controller for a page named [pageName]. @@ -23,4 +24,13 @@ class StarterController extends PageControllerBones { MaterialPageRoute( builder: (context) => StarterChangePage(id, applicationData, row))); } + + @override + void startDelete(int id, Map row) { + applicationData.pushCaller(this); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => StarterDeletePage(id, applicationData, row))); + } } diff --git a/lib/src/page/starter/starter_create_page.dart b/lib/src/page/starter/starter_create_page.dart index 3c47cb3..b140ef6 100644 --- a/lib/src/page/starter/starter_create_page.dart +++ b/lib/src/page/starter/starter_create_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -39,8 +39,8 @@ abstract class StarterCreatePageState extends State implements RedrawPage { final ApplicationData applicationData; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$starterCreatePageModule-$starterCreatePageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$starterCreatePageModule-$starterCreatePageName'); StarterController controller; @@ -59,7 +59,7 @@ abstract class StarterCreatePageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, @@ -79,8 +79,8 @@ abstract class StarterCreatePageState extends State @override void initState() { super.initState(); - controller = - StarterController(_formKey, this, starterCreatePageName, context, applicationData); + controller = StarterController( + _formKey, this, starterCreatePageName, context, applicationData); controller.initialize(); customize(); } diff --git a/lib/src/page/starter/starter_delete_page.dart b/lib/src/page/starter/starter_delete_page.dart new file mode 100644 index 0000000..5e94064 --- /dev/null +++ b/lib/src/page/starter/starter_delete_page.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; + +import '../../helper/settings.dart'; +import '../../widget/dialog_bones.dart'; +import '../../widget/page_controller_bones.dart'; +import '../application_data.dart'; +import '../../model/page_model.dart'; +import 'starter_controller.dart'; + +const starterDeletePageModule = 'starter'; +const starterDeletePageName = 'delete'; +const starterDeletePageType = PageModelType.delete; +//! === BeginOfGeneratedCode: Delete only in areas marked as customized + +class StarterDeletePage extends StatefulWidget { + final ApplicationData applicationData; + final Map initialRow; + final logger = Settings().logger; + final int primaryId; + + //! === BeginOfCustomizedVars1 === + //! === EndOfCustomizedVars1 === + //! === BeginOfConstructor1 === + StarterDeletePage(this.primaryId, this.applicationData, this.initialRow, + {Key key}) + : super(key: key); + + //! === EndOfConstructor1 === + + @override + StarterDeletePageState createState() { + //! === BeginOfCall === + final rc = StarterDeletePageStateCustomized( + primaryId, applicationData, initialRow); + //! === EndOfCall === + + /// for unittests: + applicationData.lastModuleState = rc; + return rc; + } +} + +abstract class StarterDeletePageState extends State + implements RedrawPage { + final ApplicationData applicationData; + final int primaryId; + final Map initialRow; + final GlobalKey _formKey = GlobalKey( + debugLabel: '$starterDeletePageModule-$starterDeletePageName'); + + StarterController controller; + + //! === BeginOfCustomizedVars2 === + //! === EndOfCustomizedVars2 === + //! === BeginOfConstructor2 === + StarterDeletePageState(this.primaryId, this.applicationData, this.initialRow); + + //! === EndOfConstructor2 === + + @override + Widget build(BuildContext context) { + controller.beginOfBuild(context); + //! === BeginOfScaffold === + return Scaffold( + appBar: applicationData.appBarBuilder(controller.page.title), + drawer: applicationData.drawerBuilder(context), + bottomNavigationBar: applicationData.footerBuilder().widget(controller), + body: formDialog( + key: _formKey, + pageController: controller, + configuration: applicationData.configuration, + primaryId: primaryId, + initialRow: initialRow, + )); + //! === EndOfScaffold === + } + + /// Customize the page, e.g. modify the default widget list given by the model + void customize(); + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + controller = StarterController( + _formKey, this, starterDeletePageName, context, applicationData); + controller.initialize(); + customize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } +} + +//! === EndOfGeneratedCode: Below you can change manually: + +class StarterDeletePageStateCustomized extends StarterDeletePageState { + StarterDeletePageStateCustomized( + int primaryId, ApplicationData applicationData, Map initialRow) + : super(primaryId, applicationData, initialRow); + + @override + void customize() { + // ToDo: write code if needed + } +} diff --git a/lib/src/page/starter/starter_list_page.dart b/lib/src/page/starter/starter_list_page.dart index 41d00ed..093afbd 100644 --- a/lib/src/page/starter/starter_list_page.dart +++ b/lib/src/page/starter/starter_list_page.dart @@ -2,7 +2,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/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'starter_controller.dart'; @@ -38,8 +38,8 @@ abstract class StarterListPageState extends State implements RedrawPage { final ApplicationData applicationData; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$starterListPageModule-$starterListPageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$starterListPageModule-$starterListPageName'); Iterable rowsDeprecated; StarterController controller; @@ -65,7 +65,10 @@ abstract class StarterListPageState extends State titles: ListForm.stringsToTitles(controller.page.tableTitles), columnNames: controller.page.tableColumns ?? [], rows: controller.listRows ?? [], - showEditIcon: true, + showDeleteIcon: controller.rightOf(starterListPageModule, + starterListPageName, starterListPageType, TaskRight.listEdit), + showEditIcon: controller.rightOf(starterListPageModule, + starterListPageName, starterListPageType, TaskRight.listDelete), pageController: controller, buttons: [ ButtonBar( @@ -88,8 +91,8 @@ abstract class StarterListPageState extends State @override void initState() { super.initState(); - controller = - StarterController(_formKey, this, starterListPageName, context, applicationData); + controller = StarterController( + _formKey, this, starterListPageName, context, applicationData); controller.initialize(); customize(); } diff --git a/lib/src/page/user/user_change_page.dart b/lib/src/page/user/user_change_page.dart index 984fcb2..6804f68 100644 --- a/lib/src/page/user/user_change_page.dart +++ b/lib/src/page/user/user_change_page.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; import '../../model/button_model.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -47,8 +47,8 @@ abstract class UserChangePageState extends State final ApplicationData applicationData; final int primaryId; final Map initialRow; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$userChangePageModule-$userChangePageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$userChangePageModule-$userChangePageName'); UserController controller; @@ -67,7 +67,7 @@ abstract class UserChangePageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, @@ -89,8 +89,8 @@ abstract class UserChangePageState extends State @override void initState() { super.initState(); - controller = - UserController(_formKey, this, 'change', context, applicationData); + controller = UserController( + _formKey, this, userChangePageName, context, applicationData); controller.initialize(); customize(); } @@ -115,14 +115,20 @@ class UserChangePageStateCustomized extends UserChangePageState { @override void customize() { ButtonModel button = controller.page.buttonByName('set_password'); - button?.onPressed = () { - String userName = controller.page.fieldByName('user_name').value; - applicationData.pushCaller(controller); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => UserPasswordPage( - primaryId, userName, applicationData, null))); - }; + if (applicationData.currentRolePriority > 20) { + if (!button.hasOption('disabled')) { + button.options.add('disabled'); + } + } else { + button?.onPressed = () { + String userName = controller.page.fieldByName('user_name').value; + applicationData.pushCaller(controller); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UserPasswordPage( + primaryId, userName, applicationData, null))); + }; + } } } diff --git a/lib/src/page/user/user_controller.dart b/lib/src/page/user/user_controller.dart index 6519eae..d31d1e6 100644 --- a/lib/src/page/user/user_controller.dart +++ b/lib/src/page/user/user_controller.dart @@ -1,10 +1,11 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../model/standard/user_model.dart'; +import '../../model/module/user_model.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'user_change_page.dart'; +import 'user_delete_page.dart'; class UserController extends PageControllerBones { /// Controller for a page named [pageName]. @@ -23,4 +24,13 @@ class UserController extends PageControllerBones { MaterialPageRoute( builder: (context) => UserChangePage(id, applicationData, row))); } + + @override + void startDelete(int id, Map row) { + applicationData.pushCaller(this); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => UserDeletePage(id, applicationData, row))); + } } diff --git a/lib/src/page/user/user_create_page.dart b/lib/src/page/user/user_create_page.dart index 9dcb2ec..0be8214 100644 --- a/lib/src/page/user/user_create_page.dart +++ b/lib/src/page/user/user_create_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -39,8 +39,8 @@ abstract class UserCreatePageState extends State implements RedrawPage { final ApplicationData applicationData; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$userCreatePageModule-$userCreatePageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$userCreatePageModule-$userCreatePageName'); UserController controller; @@ -59,7 +59,7 @@ abstract class UserCreatePageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, @@ -79,8 +79,8 @@ abstract class UserCreatePageState extends State @override void initState() { super.initState(); - controller = - UserController(_formKey, this, 'create', context, applicationData); + controller = UserController( + _formKey, this, userCreatePageName, context, applicationData); controller.initialize(); customize(); } diff --git a/lib/src/page/user/user_delete_page.dart b/lib/src/page/user/user_delete_page.dart new file mode 100644 index 0000000..8d04db4 --- /dev/null +++ b/lib/src/page/user/user_delete_page.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; + +import '../../helper/settings.dart'; +import '../../widget/dialog_bones.dart'; +import '../../widget/page_controller_bones.dart'; +import '../application_data.dart'; +import '../../model/page_model.dart'; +import 'user_controller.dart'; + +const userDeletePageModule = 'user'; +const userDeletePageName = 'delete'; +const userDeletePageType = PageModelType.delete; +//! === BeginOfGeneratedCode: Delete only in areas marked as customized + +class UserDeletePage extends StatefulWidget { + final ApplicationData applicationData; + final Map initialRow; + final logger = Settings().logger; + final int primaryId; + + //! === BeginOfCustomizedVars1 === + //! === EndOfCustomizedVars1 === + //! === BeginOfConstructor1 === + UserDeletePage(this.primaryId, this.applicationData, this.initialRow, + {Key key}) + : super(key: key); + + //! === EndOfConstructor1 === + + @override + UserDeletePageState createState() { + //! === BeginOfCall === + final rc = + UserDeletePageStateCustomized(primaryId, applicationData, initialRow); + //! === EndOfCall === + + /// for unittests: + applicationData.lastModuleState = rc; + return rc; + } +} + +abstract class UserDeletePageState extends State + implements RedrawPage { + final ApplicationData applicationData; + final int primaryId; + final Map initialRow; + final GlobalKey _formKey = GlobalKey( + debugLabel: '$userDeletePageModule-$userDeletePageName'); + + UserController controller; + + //! === BeginOfCustomizedVars2 === + //! === EndOfCustomizedVars2 === + //! === BeginOfConstructor2 === + UserDeletePageState(this.primaryId, this.applicationData, this.initialRow); + + //! === EndOfConstructor2 === + + @override + Widget build(BuildContext context) { + controller.beginOfBuild(context); + //! === BeginOfScaffold === + return Scaffold( + appBar: applicationData.appBarBuilder(controller.page.title), + drawer: applicationData.drawerBuilder(context), + bottomNavigationBar: applicationData.footerBuilder().widget(controller), + body: formDialog( + key: _formKey, + pageController: controller, + configuration: applicationData.configuration, + primaryId: primaryId, + initialRow: initialRow, + )); + //! === EndOfScaffold === + } + + /// Customize the page, e.g. modify the default widget list given by the model + void customize(); + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + void initState() { + super.initState(); + controller = UserController( + _formKey, this, userDeletePageName, context, applicationData); + controller.initialize(); + customize(); + } + + @override + void redraw(RedrawReason reason, + {String customString, RedrawCallbackFunctionSimple callback}) { + setState(() { + controller.afterSetState(reason, + customString: customString, callback: callback); + }); + } +} + +//! === EndOfGeneratedCode: Below you can change manually: + +class UserDeletePageStateCustomized extends UserDeletePageState { + UserDeletePageStateCustomized( + int primaryId, ApplicationData applicationData, Map initialRow) + : super(primaryId, applicationData, initialRow); + + @override + void customize() { + // ToDo: write code if needed + } +} diff --git a/lib/src/page/user/user_list_page.dart b/lib/src/page/user/user_list_page.dart index a537dc9..ce1563f 100644 --- a/lib/src/page/user/user_list_page.dart +++ b/lib/src/page/user/user_list_page.dart @@ -2,7 +2,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/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../application_data.dart'; import 'user_controller.dart'; @@ -65,7 +65,10 @@ abstract class UserListPageState extends State titles: ListForm.stringsToTitles(controller.page.tableTitles), columnNames: controller.page.tableColumns ?? [], rows: controller.listRows ?? [], - showEditIcon: true, + showDeleteIcon: controller.rightOf(userListPageModule, userListPageName, + userListPageType, TaskRight.listEdit), + showEditIcon: controller.rightOf(userListPageModule, userListPageName, + userListPageType, TaskRight.listDelete), pageController: controller, buttons: [ ButtonBar( @@ -88,8 +91,8 @@ abstract class UserListPageState extends State @override void initState() { super.initState(); - controller = - UserController(_formKey, this, userListPageName, context, applicationData); + controller = UserController( + _formKey, this, userListPageName, context, applicationData); controller.initialize(); customize(); } @@ -116,5 +119,10 @@ class UserListPageStateCustomized extends UserListPageState { button.onPressed = () { controller.goTo(pageType: PageModelType.create); }; + controller.page.taskRightCallback = (task, parameter) { + final row = parameter as Map; + return row['role_priority'] >= + controller.applicationData.currentRolePriority; + }; } } diff --git a/lib/src/page/user/user_login_page.dart b/lib/src/page/user/user_login_page.dart index fd7d9d9..758238f 100644 --- a/lib/src/page/user/user_login_page.dart +++ b/lib/src/page/user/user_login_page.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; import '../../model/button_model.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -41,8 +41,8 @@ abstract class UserLoginPageState extends State implements RedrawPage { final ApplicationData applicationData; - final GlobalKey _formKey = - GlobalKey(debugLabel: '$userLoginPageModule-$userLoginPageName'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$userLoginPageModule-$userLoginPageName'); UserController controller; @@ -61,7 +61,7 @@ abstract class UserLoginPageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, diff --git a/lib/src/page/user/user_password_page.dart b/lib/src/page/user/user_password_page.dart index 950d232..e9e9405 100644 --- a/lib/src/page/user/user_password_page.dart +++ b/lib/src/page/user/user_password_page.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import '../../helper/settings.dart'; import '../../model/button_model.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; import '../../model/page_model.dart'; import '../application_data.dart'; @@ -34,7 +34,8 @@ class UserPasswordPage extends StatefulWidget { @override UserPasswordPageState createState() { //! === BeginOfCall === - final rc = UserPasswordPageStateCustomized(primaryId, this.userName, applicationData, initialRow); + final rc = UserPasswordPageStateCustomized( + primaryId, this.userName, applicationData, initialRow); //! === EndOfCall === /// for unittests: @@ -47,9 +48,9 @@ abstract class UserPasswordPageState extends State implements RedrawPage { final ApplicationData applicationData; final int primaryId; - final GlobalKey _formKey = - GlobalKey(debugLabel: 'user.password'); final Map initialRow; + final GlobalKey _formKey = GlobalKey( + debugLabel: '$userPasswordPageModule-$userPasswordPageName'); UserController controller; @@ -57,7 +58,8 @@ abstract class UserPasswordPageState extends State final String userName; //! === EndOfCustomizedVars2 === //! === BeginOfConstructor2 === - UserPasswordPageState(this.primaryId, this.userName, this.applicationData, this.initialRow); + UserPasswordPageState( + this.primaryId, this.userName, this.applicationData, this.initialRow); //! === EndOfConstructor2 === @override @@ -68,7 +70,7 @@ abstract class UserPasswordPageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, @@ -90,8 +92,8 @@ abstract class UserPasswordPageState extends State @override void initState() { super.initState(); - controller = - UserController(_formKey, this, userPasswordPageName, context, applicationData); + controller = UserController( + _formKey, this, userPasswordPageName, context, applicationData); controller.initialize(); customize(); } @@ -109,8 +111,8 @@ abstract class UserPasswordPageState extends State //! === EndOfGeneratedCode: Below you can change manually: class UserPasswordPageStateCustomized extends UserPasswordPageState { - UserPasswordPageStateCustomized( - int primaryId, String userName, ApplicationData applicationData, Map initialRow) + UserPasswordPageStateCustomized(int primaryId, String userName, + ApplicationData applicationData, Map initialRow) : super(primaryId, userName, applicationData, initialRow); @override diff --git a/lib/src/widget/dialog_bones.dart b/lib/src/widget/dialog_bones.dart new file mode 100644 index 0000000..589b315 --- /dev/null +++ b/lib/src/widget/dialog_bones.dart @@ -0,0 +1,200 @@ +import 'package:dart_bones/dart_bones.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bones/src/widget/page_controller_bones.dart'; + +import '../helper/string_helper.dart'; +import 'model_list.dart'; +import 'page_controller_bones.dart'; +import 'view.dart'; + +typedef Function OnEditTap(Map row, int index); + +abstract class TableCallbackController { + OnEditTap getOnEditTap(String customString, + TableCallbackController controller, Map row); +} + +class ListForm { + /// Converts a string with [titles] into a list of widgets of type Text(). + /// Format of [titles]: first character is the delimiter, e.g. ";Id;Name;" + /// This allows to use different delimiters when the title text + /// contains characters normally used as delimiters. + static List stringsToTitles(List titles) { + final rc = titles + .map((String title) => + Text(title, style: TextStyle(fontWeight: FontWeight.bold))) + .toList(); + return rc; + } + + /// Returns a widget with a data table. + /// [titles] is used for the table header. + /// [columnNames] are the keys to display: @precondition: titles.length == columnNames.length + /// [rows] is a list of rows normally delivered from a database query: + /// each row is a map with (key, value) pairs. + /// If [showEditItems] is true the edit icon is shown in the additional first column. + /// If [sortIndex] is not null the rows will be sorted by this index. + static Widget table({ + @required List titles, + @required List columnNames, + @required PageControllerBones controller, + @required Iterable rows, + bool showEditIcon = false, + bool showDeleteIcon = false, + int sortIndex, + TableCallbackController tableCallbackController, + String customString, + }) { + if (titles.length != columnNames.length) { + controller.moduleModel.logger.error( + 'titles.length != columnNames.length: ${titles.length}/${columnNames.length}'); + } + final titles2 = titles.map((item) => DataColumn(label: item)).toList(); + if (showEditIcon) { + titles2.insert(0, DataColumn(label: SizedBox(width: 1))); + sortIndex = sortIndex == null ? null : sortIndex + 1; + } + if (showDeleteIcon) { + titles2.add(DataColumn(label: SizedBox(width: 1))); + } + final taskRightCallBack = controller.page.taskRightCallback; + Widget rc = Container( + margin: EdgeInsets.all(2), + child: DataTable( + showCheckboxColumn: true, + showBottomBorder: true, + sortColumnIndex: sortIndex, + columns: titles2, + rows: rows.map((row) { + final cells = []; + if (showEditIcon) { + final canEdit = taskRightCallBack == null + ? true + : taskRightCallBack(TaskRight.listEdit, row); + cells.add(DataCell(SizedBox(width: 1), showEditIcon: canEdit, + onTap: () { + if (canEdit) { + controller.onEditTap(customString, controller, row); + } + })); + } + for (var key in columnNames) { + cells.add(DataCell( + Text(StringHelper.asString(row[key], nullString: '')), + )); + } + if (showDeleteIcon) { + final canDelete = taskRightCallBack == null + ? true + : taskRightCallBack(TaskRight.listDelete, row); + cells.add(canDelete + ? DataCell( + Tooltip( + message: 'Endgültiges Löschen des Eintrags', + child: Icon( + Icons.delete_forever_outlined, + semanticLabel: 'Eintrag löschen', + )), + onTap: () => controller.onDeleteTap( + customString, controller, row)) + : DataCell(Tooltip( + message: 'Löschen nicht erlaubt', + child: Icon(Icons.delete_rounded)))); + } + return DataRow(cells: cells); + }).toList())); + return rc; + } + + /// Returns a widget with a form containing some [filters] and a data table. + /// [titles] is used for the table header. + /// [columnNames] are the keys to display: @precondition: titles.length == columnNames.length + /// [rows] is a list of rows normally delivered from a database query: + /// each row is a map with (key, value) pairs. + /// If [showEditItems] is true the edit icon is shown in the first column. + /// If [errorMessage] is not null this message will be shown. + static Form listForm( + {@required Key key, + @required ModelList filters, + @required List buttons, + @required List titles, + @required List columnNames, + @required Iterable rows, + bool showDeleteIcon = false, + bool showEditIcon = false, + String errorMessage, + PageControllerBones pageController, + @required BaseConfiguration configuration, + String customString}) { + final padding = + configuration.asFloat('form.card.padding', defaultValue: 16.0); + + final view = View(pageController.moduleModel.logger); + final widgets = [ + ...view.modelsToWidgets(filters.models, pageController), + SizedBox( + height: configuration.asFloat('form.gap.field_button.height', + defaultValue: 16.0)), + ...buttons + ]; + if (errorMessage != null) { + widgets.add(View().errorMessage(errorMessage)); + } + widgets.add(table( + titles: titles, + columnNames: columnNames, + rows: rows, + showDeleteIcon: showDeleteIcon, + showEditIcon: showEditIcon, + controller: pageController, + customString: customString, + )); + return Form( + key: key, + child: Card( + margin: EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0), + child: ListView(children: widgets)), + )); + } +} + +/// Returns a widget with a form containing at least some input fields +/// and a save/cancel button. +/// a change page. +/// [page] is the name of the page in the model. +Form formDialog({ + @required Key key, + @required PageControllerBones pageController, + @required BaseConfiguration configuration, + int primaryId, + Map initialRow, +}) { + final padding = + configuration.asFloat('form.card.padding', defaultValue: 16.0); + pageController.buildModelList(initialRow); + final widgets = pageController.getWidgets(); + final view = View(); + final buttons = view.modelsToWidgets( + pageController.page.sections[0].buttonBar, pageController); + return Form( + key: key, + child: Card( + margin: EdgeInsets.symmetric(vertical: padding, horizontal: padding), + child: Padding( + padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0), + child: ListView( + children: [ + ...widgets, + SizedBox( + height: configuration.asFloat( + 'form.gap.field_button.height', + defaultValue: 16.0)), + ButtonBar( + children: buttons, + ), + ], + ))), + ); +} diff --git a/lib/src/widget/edit_form.dart b/lib/src/widget/edit_form.dart deleted file mode 100644 index ec50521..0000000 --- a/lib/src/widget/edit_form.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:dart_bones/dart_bones.dart'; -import 'package:flutter/material.dart'; - -import 'page_controller_bones.dart'; -import 'view.dart'; - -typedef Function OnEditTap(Map row, int index); - -/// Contains helper functions for creating/changing data of a model based module. -class EditForm { - /// Returns a widget with a form containing at least some input fields - /// and a save/cancel button. - /// a change page. - /// [page] is the name of the page in the model. - static Form editForm({ - @required Key key, - @required PageControllerBones pageController, - @required BaseConfiguration configuration, - int primaryId, - Map initialRow, - }) { - final padding = - configuration.asFloat('form.card.padding', defaultValue: 16.0); - pageController.buildModelList(initialRow); - final widgets = pageController.getWidgets(); - final view = View(); - final buttons = view.modelsToWidgets( - pageController.page.sections[0].buttonBar, pageController); - return Form( - key: key, - child: Card( - margin: EdgeInsets.symmetric(vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0), - child: ListView( - children: [ - ...widgets, - SizedBox( - height: configuration.asFloat( - 'form.gap.field_button.height', - defaultValue: 16.0)), - ButtonBar( - children: buttons, - ), - ], - ))), - ); - } -} diff --git a/lib/src/widget/list_form.dart b/lib/src/widget/list_form.dart deleted file mode 100644 index 61dfe09..0000000 --- a/lib/src/widget/list_form.dart +++ /dev/null @@ -1,131 +0,0 @@ -import 'package:dart_bones/dart_bones.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bones/src/widget/page_controller_bones.dart'; - -import '../helper/string_helper.dart'; -import 'model_list.dart'; -import 'page_controller_bones.dart'; -import 'view.dart'; - -typedef Function OnEditTap(Map row, int index); - -abstract class TableCallbackController { - OnEditTap getOnEditTap(String customString, - TableCallbackController controller, Map row); -} - -class ListForm { - /// Converts a string with [titles] into a list of widgets of type Text(). - /// Format of [titles]: first character is the delimiter, e.g. ";Id;Name;" - /// This allows to use different delimiters when the title text - /// contains characters normally used as delimiters. - static List stringsToTitles(List titles) { - final rc = titles - .map((String title) => - Text(title, style: TextStyle(fontWeight: FontWeight.bold))) - .toList(); - return rc; - } - - /// Returns a widget with a data table. - /// [titles] is used for the table header. - /// [columnNames] are the keys to display: @precondition: titles.length == columnNames.length - /// [rows] is a list of rows normally delivered from a database query: - /// each row is a map with (key, value) pairs. - /// If [showEditItems] is true the edit icon is shown in the additional first column. - /// If [sortIndex] is not null the rows will be sorted by this index. - static Widget table({ - @required List titles, - @required List columnNames, - @required PageControllerBones controller, - @required Iterable rows, - bool showEditIcon = false, - int sortIndex, - TableCallbackController tableCallbackController, - String customString, - }) { - if (titles.length != columnNames.length) { - controller.moduleModel.logger.error( - 'titles.length != columnNames.length: ${titles.length}/${columnNames.length}'); - } - final titles2 = titles.map((item) => DataColumn(label: item)).toList(); - if (showEditIcon) { - titles2.insert(0, DataColumn(label: SizedBox(width: 1))); - sortIndex = sortIndex == null ? null : sortIndex + 1; - } - Widget rc = Container( - margin: EdgeInsets.all(2), - child: DataTable( - showCheckboxColumn: true, - showBottomBorder: true, - sortColumnIndex: sortIndex, - columns: titles2, - rows: rows.map((row) { - final cells = []; - if (showEditIcon) { - cells.add( - DataCell(SizedBox(width: 1), showEditIcon: true, onTap: () { - controller.onEditTap(customString, controller, row); - })); - } - for (var key in columnNames) { - cells.add(DataCell( - Text(StringHelper.asString(row[key], nullString: '')), - )); - } - return DataRow(cells: cells); - }).toList())); - return rc; - } - - /// Returns a widget with a form containing some [filters] and a data table. - /// [titles] is used for the table header. - /// [columnNames] are the keys to display: @precondition: titles.length == columnNames.length - /// [rows] is a list of rows normally delivered from a database query: - /// each row is a map with (key, value) pairs. - /// If [showEditItems] is true the edit icon is shown in the first column. - /// If [errorMessage] is not null this message will be shown. - static Form listForm( - {@required Key key, - @required ModelList filters, - @required List buttons, - @required List titles, - @required List columnNames, - @required Iterable rows, - bool showEditIcon = false, - String errorMessage, - PageControllerBones pageController, - @required BaseConfiguration configuration, - String customString}) { - final padding = - configuration.asFloat('form.card.padding', defaultValue: 16.0); - - final view = View(pageController.moduleModel.logger); - final widgets = [ - ...view.modelsToWidgets(filters.models, pageController), - SizedBox( - height: configuration.asFloat('form.gap.field_button.height', - defaultValue: 16.0)), - ...buttons - ]; - if (errorMessage != null) { - widgets.add(View().errorMessage(errorMessage)); - } - widgets.add(table( - titles: titles, - columnNames: columnNames, - rows: rows, - showEditIcon: showEditIcon, - controller: pageController, - customString: customString, - )); - return Form( - key: key, - child: Card( - margin: EdgeInsets.symmetric(vertical: padding, horizontal: padding), - child: Padding( - padding: EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0), - child: ListView(children: widgets)), - )); - } -} diff --git a/lib/src/widget/page_controller_bones.dart b/lib/src/widget/page_controller_bones.dart index 3062ed3..0f30d34 100644 --- a/lib/src/widget/page_controller_bones.dart +++ b/lib/src/widget/page_controller_bones.dart @@ -40,22 +40,12 @@ class PageControllerBones implements ValidatorController { 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]); + ThemeData get themeData => Theme.of(context); + void afterSetState(RedrawReason reason, {String customString, RedrawCallbackFunctionSimple callback}) { if (callback != null) { @@ -63,14 +53,6 @@ class PageControllerBones implements ValidatorController { } } - Future buildComboboxDataFromPersistence( - ComboBaseModel model) async { - 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 beginOfBuild(BuildContext context) { @@ -82,6 +64,39 @@ class PageControllerBones implements ValidatorController { } } + Future buildComboboxDataFromPersistence( + ComboBaseModel model) async { + final rc = await applicationData.persistenceCache.comboboxAsync( + model.listType, model.listOption, + hasUndef: model.hasOption('undef')); + return rc; + } + + /// Prepares the widgetList: builds the widgets of the page. + /// [initialRow] is null or a map with the field values, + /// e.g. { 'role_name': 'admin', ...} + void buildModelList([Map initialRow]) { + modelList.clear(); + page.sections[0].children.forEach((model) { + if (initialRow != null && model is FieldModel) { + model.valueFromRow(initialRow); + } + final name = model is FieldModel ? model.name : null; + final value = + initialRow == null || name == null ? null : initialRow[name]; + if (model is FieldModel) { + model.value = value; + } + completeModels(model); + modelList.addModel(name, model); + }); + page.sections[0].children.forEach((element) { + if (element is FieldModel) { + prepareModel(element); + } + }); + } + /// Retrieves the rows shown in the list page. void buildRows() { final persistence = applicationData.persistence; @@ -120,31 +135,6 @@ class PageControllerBones implements ValidatorController { return rc; } - /// Prepares the widgetList: builds the widgets of the page. - /// [initialRow] is null or a map with the field values, - /// e.g. { 'role_name': 'admin', ...} - void buildModelList([Map initialRow]) { - modelList.clear(); - page.sections[0].children.forEach((model) { - if (initialRow != null && model is FieldModel) { - model.valueFromRow(initialRow); - } - final name = model is FieldModel ? model.name : null; - final value = - initialRow == null || name == null ? null : initialRow[name]; - if (model is FieldModel) { - model.value = value; - } - completeModels(model); - modelList.addModel(name, model); - }); - page.sections[0].children.forEach((element) { - if (element is FieldModel) { - prepareModel(element); - } - }); - } - /// Completes the widgets asynchronously if needed. void completeAsync() { waitForCompletion = completeModelsAsync(); @@ -313,6 +303,20 @@ class PageControllerBones implements ValidatorController { } }; break; + case 'delete': + rc = () { + if (globalKey.currentState.validate()) { + final id = (parent as SingleRecordPage).getPrimaryKey(); + applicationData.persistence + .delete(module: moduleModel.name, id: id) + .then((id) { + applicationData.callerRedraw(RedrawReason.fetchList); + applicationData.popCaller(); + Navigator.pop(controller.getContext()); + }); + } + }; + break; case 'cancel': rc = () { applicationData.popCaller(); @@ -373,7 +377,17 @@ class PageControllerBones implements ValidatorController { return rc ?? []; } - ThemeData get themeData => Theme.of(context); + /// 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); + } /// Initializes the controller when the instance is complete constructed, /// e.g. ModuleModel.parse() is called. @@ -397,6 +411,14 @@ class PageControllerBones implements ValidatorController { }); } + onDeleteTap(String customString, PageControllerBones controller, Map row) { + ColumnModel primary = moduleModel.mainTable().primary; + final id = row[primary.name]; + applicationData.persistence + .recordById(module: moduleModel.name, id: id) + .then((row) => startDelete(id, row)); + } + onEditTap(String customString, PageControllerBones controller, Map row) { ColumnModel primary = moduleModel.mainTable().primary; final id = row[primary.name]; @@ -425,6 +447,27 @@ class PageControllerBones implements ValidatorController { Navigator.pushNamed(context, route); } + /// Returns whether the current user has the right to do the [task]. + /// [module], [pageName] and [pageType] specify the location of the task. + bool rightOf( + String module, String pageName, PageModelType pageType, TaskRight task) { + bool rc; + switch (task) { + case TaskRight.listEdit: + case TaskRight.listDelete: + if (module == 'role' || module == 'user' || module == 'configuration') { + rc = applicationData.currentRolePriority <= 20; + } else { + rc = applicationData.currentRolePriority <= 30; + } + break; + default: + rc = false; + break; + } + return rc; + } + /// Returns a standard search button for the list page. Widget searchButton() { final rc = View(moduleModel.logger) @@ -439,6 +482,13 @@ class PageControllerBones implements ValidatorController { 'missing override of startChange() in ${moduleModel.fullName()}'); } + /// Starts the change page to edit the record with primary key [id]. + /// [row] contains the db record of [id]. + void startDelete(int id, Map row) { + moduleModel.logger.error( + 'missing override of startDelete() in ${moduleModel.fullName()}'); + } + /// Returns the [TextEditingController] instance of a [TextFormField] assigned /// to a model named [name]. TextEditingController textController(String name) { @@ -471,3 +521,16 @@ enum RedrawReason { redraw, setError, } + +abstract class SingleRecordPage { + /// Returns the primary key of the record displayed by the page: + int getPrimaryKey(); +} + +enum TaskRight { + custom1, + custom2, + custom3, + listEdit, + listDelete, +} diff --git a/lib/src/widget/utilities.dart b/lib/src/widget/utilities.dart new file mode 100644 index 0000000..c3a7e19 --- /dev/null +++ b/lib/src/widget/utilities.dart @@ -0,0 +1,20 @@ +import 'package:dart_bones/dart_bones.dart'; +import 'page_controller_bones.dart'; +import '../model/page_model.dart'; + +Utilities utilities; + +class Utilities { + BaseLogger logger; + Utilities(this.logger); + TaskRightCallback rightsOfRoleListPage(PageControllerBones controller) { + return (TaskRight task, dynamic parameter) { + final row = parameter as Map; + final rc = (task != TaskRight.listDelete && task != TaskRight.listEdit || + controller.applicationData.currentRolePriority <= + row['role_priority']) && + (task != TaskRight.listDelete || row['role_name'] != 'Administrator'); + return rc; + }; + } +} diff --git a/lib/src/widget/view.dart b/lib/src/widget/view.dart index f76f408..2694ad1 100644 --- a/lib/src/widget/view.dart +++ b/lib/src/widget/view.dart @@ -89,6 +89,7 @@ class View { if (model != null && model.data?.waitState == WaitState.ready) { rc = comboboxRaw(model, controller, initialValue); } else { + controller.waitForCompletion = null; rc = FutureBuilder( future: controller.waitForCompletion, builder: (BuildContext context, AsyncSnapshot snapshot) { diff --git a/model_tool/lib/src/model_tool_io.dart b/model_tool/lib/src/model_tool_io.dart index cbc744d..be4d0a3 100644 --- a/model_tool/lib/src/model_tool_io.dart +++ b/model_tool/lib/src/model_tool_io.dart @@ -1,13 +1,16 @@ import 'dart:io'; import 'package:dart_bones/dart_bones.dart'; +import 'package:flutter_bones_tool/src/helper/url_helper.dart'; import 'package:flutter_bones_tool/src/model/model_helper.dart'; import 'package:flutter_bones_tool/src/model/model_tool.dart'; /// Completes the ModelTool with io specific functionality. class ModelToolIo extends ModelTool { ModelToolIo(ModelHelper modelHelper, BaseLogger logger) - : super(modelHelper, logger); + : super(modelHelper, logger) { + FileSync.setLogger(logger); + } @override void ensureDirectory(String path) { @@ -39,7 +42,16 @@ class ModelToolIo extends ModelTool { } @override - void writeFile(String filename, String content) { - FileSync.toFile(filename, content); + void writeFile(String filename, String content, + {bool mayExist, bool ensureDirectory}) { + if (ensureDirectory == null || ensureDirectory) { + FileSync.ensureDirectory( + UrlHelper.parentOf(filename, trailingSlash: false)); + } + if (mayExist != null && !mayExist && FileSync.isFile(filename)) { + logger.error('$filename already exists'); + } else { + FileSync.toFile(filename, content); + } } } diff --git a/test/helpers/string_helper_test.dart b/test/helpers/string_helper_test.dart index 41d8e1b..c621258 100644 --- a/test/helpers/string_helper_test.dart +++ b/test/helpers/string_helper_test.dart @@ -284,7 +284,6 @@ cAC equals('')); }); test('addRangeToList-converter', () { - List output; final list = ['a1', 'a2', 'a3', 'a4']; expect( StringHelper.addRangeToList(list, null, diff --git a/test/helpers/url_helper_test.dart b/test/helpers/url_helper_test.dart new file mode 100644 index 0000000..4390e62 --- /dev/null +++ b/test/helpers/url_helper_test.dart @@ -0,0 +1,48 @@ +import 'package:dart_bones/dart_bones.dart'; +import 'package:flutter_bones/src/helper/url_helper.dart'; +import 'package:test/test.dart'; + +void main() { + final logger = MemoryLogger(LEVEL_FINE); + group('filename', () { + logger.log('start'); + test('joinPath', () { + expect(UrlHelper.joinPaths('/a', '/b'), equals('/a/b')); + expect(UrlHelper.joinPaths('/a/', '/b'), equals('/a/b')); + expect(UrlHelper.joinPaths('/a/', 'b'), equals('/a/b')); + expect(UrlHelper.joinPaths('/a', ''), equals('/a')); + expect(UrlHelper.joinPaths('/a', '/b', '/c'), equals('/a/b/c')); + expect(UrlHelper.joinPaths('/a', './b', './c'), equals('/a/b/c')); + expect(UrlHelper.joinPaths('/a/', '/b/', '/c'), equals('/a/b/c')); + expect(UrlHelper.joinPaths('/a/', '/b/', 'c'), equals('/a/b/c')); + expect(UrlHelper.joinPaths('/a/', '', 'c'), equals('/a/c')); + }); + }); + test('nodeOf', () { + expect(UrlHelper.nodeOf('abc.de'), equals('abc.de')); + expect(UrlHelper.nodeOf('path/a'), equals('a')); + expect(UrlHelper.nodeOf('/base/in/path/abc.de'), equals('abc.de')); + }); + test('parentOf', () { + expect(UrlHelper.parentOf('abc.de'), equals('')); + expect(UrlHelper.parentOf('path/a'), equals('path/')); + expect(UrlHelper.parentOf('path/a', trailingSlash: false), equals('path')); + expect(UrlHelper.parentOf('/', trailingSlash: false), equals('')); + expect( + UrlHelper.parentOf('/base/in/path/abc.de'), equals('/base/in/path/')); + }); + test('extensionOf', () { + expect(UrlHelper.extensionOf('abc.blub.de'), equals('.de')); + expect(UrlHelper.extensionOf('.de'), equals('')); + expect(UrlHelper.extensionOf('path/a'), equals('')); + expect(UrlHelper.extensionOf('/base/in.path/abc.de'), equals('.de')); + expect(UrlHelper.extensionOf('/base/in.path/.de'), equals('')); + }); + test('filenameOf', () { + expect(UrlHelper.filenameOf('abc.blub.de'), equals('abc.blub')); + expect(UrlHelper.filenameOf('.de'), equals('.de')); + expect(UrlHelper.filenameOf('path/a'), equals('a')); + expect(UrlHelper.filenameOf('/base/in.path/abc.de'), equals('abc')); + expect(UrlHelper.filenameOf('/base/in.path/.de'), equals('.de')); + }); +} diff --git a/test/model/db_model_test.dart b/test/model/db_model_test.dart index 0950f96..03352e0 100644 --- a/test/model/db_model_test.dart +++ b/test/model/db_model_test.dart @@ -40,7 +40,7 @@ void main() { column role_changedby: DataType.string "Geändert von" options: hidden == page create: PageModelType.create options: = section section1: SectionModelType.simpleForm options: [ - textField user: "User" options: required unique primary notnull unique + textField user: "User" options: required unique primary notnull textField role: "Id" options: primary notnull unique button buttonStore: label: Save options: Save ] # create.section1 @@ -237,6 +237,7 @@ final userModel = { { 'page': 'create', 'pageType': 'create', + 'title': 'Demo', 'sections': [ { 'sectionType': 'simpleForm', diff --git a/test/model/model_test.dart b/test/model/model_test.dart index 5f14f51..b3491c5 100644 --- a/test/model/model_test.dart +++ b/test/model/model_test.dart @@ -34,6 +34,9 @@ void main() { ] # create.section1 == page change: PageModelType.change options: = section section1: SectionModelType.simpleForm options: [ + textField user_id: "Id" options: primary notnull unique readonly + textField user_name: "User" options: unique notnull + textField user_role: "Role" options: allDbFields allDbFields1 options: ] # change.section1 ''')); @@ -143,19 +146,19 @@ void main() { page.buttonByName('unknown'); page.fieldByName('nothing'); final errors = logger.errors; - expect(errors.length, equals(7)); + expect(errors.length, equals(8)); expect(errors.contains('missing column user_name in table demo1.user'), isTrue); expect( errors.contains('different sizes of tableTitles/tableColumns: 2/1'), isTrue); - expect(errors.contains('button null.a already defined: null.a'), + expect(errors.contains('model null.a already defined: null.a'), isTrue); expect(errors.contains('missing button unknown in page demo1.list'), isTrue); expect(errors.contains('missing field nothing in page demo1.list'), isTrue); - expect(errors.contains('field list.n already defined: list.n'), + expect(errors.contains('model list.n already defined: list.n'), isTrue); }); test('missing-section', () { @@ -167,6 +170,7 @@ void main() { 'pages': [ { "page": "list", + 'title': 'Demo', "pageType": "list", }, ], @@ -176,7 +180,7 @@ void main() { final page = module.pageByName('list'); expect(page, isNotNull); final errors = logger.errors; - expect(errors.length, equals(1)); + expect(errors.length, equals(2)); expect(errors.contains('missing sections in page demo1.list'), isTrue); }); @@ -198,7 +202,7 @@ void main() { final page = module.pageByName('list'); expect(page, isNotNull); final errors = logger.errors; - expect(errors.length, equals(1)); + expect(errors.length, equals(3)); expect(errors.contains('"sections" is not an array in demo1.list: wrong'), isTrue); }); @@ -219,7 +223,7 @@ void main() { module.parse(); final page = module.pageByName('list'); final errors = logger.errors; - expect(errors.length, equals(2)); + expect(errors.length, equals(5)); expect(errors.contains( 'tableTitles and tableColumns are only meaningful in list pages: demo1.list'), isTrue); @@ -244,7 +248,7 @@ void main() { module.parse(); final page = module.pageByName('list'); final errors = logger.errors; - expect(errors.length, equals(1)); + expect(errors.length, equals(4)); expect(errors.contains('curious item in section list of demo1.list: []'), isTrue); expect(page.fullName() + page.widgetName(), equals('demo1.listlist')); @@ -365,6 +369,9 @@ void main() { ] # create.section1 == page change: PageModelType.change options: = section section1: SectionModelType.simpleForm options: [ + textField user_id: "Id" options: primary notnull unique readonly + textField user_name: "User" options: unique notnull + textField user_role: "Role" options: allDbFields allDbFields1 options: ] # change.section1 ''')); @@ -409,6 +416,9 @@ void main() { ] # create.section1 == page change: PageModelType.change options: = section section1: SectionModelType.simpleForm options: [ + textField user_id: "Id" options: primary notnull unique readonly + textField user_name: "User" options: unique notnull + textField user_role: "Role" options: allDbFields allDbFields1 options: ] # change.section1 ''')); @@ -425,7 +435,7 @@ void main() { expect(errors.length, equals(0)); final page = module.pageByName('change'); expect(page, isNotNull); - expect(page.models.values.length, equals(3)); + expect(page.models.values.length, equals(6)); expect(page.hasField('user_id'), isTrue); expect(page.hasField('user_name'), isTrue); expect(page.hasField('user_role'), isTrue); @@ -485,7 +495,7 @@ void main() { final module = ModuleModel(map, logger); module.parse(); final errors = logger.errors; - expect(errors.length, equals(1)); + expect(errors.length, equals(2)); expect(errors.contains('missing children in list.section1'), isTrue); }); @@ -510,7 +520,7 @@ void main() { final module = ModuleModel(map, logger); module.parse(); final errors = logger.errors; - expect(errors.length, equals(1)); + expect(errors.length, equals(2)); expect(errors.contains('"children" is not a list in list.section1: a'), isTrue); }); @@ -537,7 +547,7 @@ void main() { final module = ModuleModel(map, logger); module.parse(); final errors = logger.errors; - expect(errors.length, equals(1)); + expect(errors.length, equals(2)); expect(errors.contains('child 1 of "children" is not a map in list.section1: []'), isTrue); }); @@ -564,7 +574,7 @@ void main() { final module = ModuleModel(map, logger); module.parse(); final errors = logger.errors; - expect(errors.length, equals(1)); + expect(errors.length, equals(2)); expect(errors.contains('child 1 of "children" does not have "modelType" in list.section1: {}'), isTrue); }); @@ -593,7 +603,7 @@ void main() { final module = ModuleModel(map, logger); module.parse(); final errors = logger.errors; - expect(errors.length, equals(1)); + expect(errors.length, equals(2)); expect(errors.contains('Section: unknown "modelType" in list.section1'), isTrue); }); @@ -632,6 +642,7 @@ final userModel = { 'pages': [ { 'page': 'create', + 'title': 'Demo', 'pageType': 'create', 'sections': [ { @@ -655,6 +666,7 @@ final userModel = { { 'page': 'change', 'pageType': 'change', + 'title': 'Demo-Change', 'sections': [ { 'sectionType': 'simpleForm', diff --git a/test/model/standard_test.dart b/test/model/standard_test.dart index a672919..254e8c0 100644 --- a/test/model/standard_test.dart +++ b/test/model/standard_test.dart @@ -27,10 +27,17 @@ void main() { column role_changedby: DataType.string "Geändert von" options: hidden == page create: PageModelType.create options: = section section1: SectionModelType.simpleForm options: [ + textField role_name: "Rolle" options: unique notnull + textField role_priority: "Priorität" options: + textField role_active: "Aktiv" options: allDbFields allDbFields1 options: ] # create.section1 == page change: PageModelType.change options: = section section1: SectionModelType.simpleForm options: [ + textField role_id: "Id" options: primary notnull unique readonly + textField role_name: "Rolle" options: unique notnull + textField role_priority: "Priorität" options: + textField role_active: "Aktiv" options: allDbFields allDbFields1 options: ] # change.section1 == page list: PageModelType.list options: @@ -63,10 +70,19 @@ void main() { column user_changedby: DataType.string "Geändert von" options: hidden == page create: PageModelType.create options: = section section1: SectionModelType.simpleForm options: [ + textField user_name: "User" options: unique notnull + textField user_displayname: "Anzeigename" options: unique notnull + textField user_email: "EMail" options: unique notnull + textField user_role: "Rolle" options: undef allDbFields allDbFields1 options: ] # create.section1 == page change: PageModelType.change options: = section section1: SectionModelType.simpleForm options: [ + textField user_id: "Id" options: primary notnull unique readonly + textField user_name: "User" options: unique notnull + textField user_displayname: "Anzeigename" options: unique notnull + textField user_email: "EMail" options: unique notnull + textField user_role: "Rolle" options: undef allDbFields allDbFields1 options: button set_password: label: Passwort ändern options: Passwort ändern ] # change.section1 @@ -78,14 +94,13 @@ void main() { ] # password.section1 == page list: PageModelType.list options: = section section1: SectionModelType.filterPanel options: [ - textField user_name: "User" options: unique notnull + textField user_name: "User" options: textField user_role: "Rolle" options: undef ] # list.section1 == page login: PageModelType.change options: noAutoButton = section section1: SectionModelType.simpleForm options: [ textField user: options: textField password: options: password - button login: label: Anmelden options: Anmelden ] # login.section1 ''')); }); @@ -114,10 +129,23 @@ void main() { column configuration_changedby: DataType.string "Geändert von" options: hidden == page create: PageModelType.create options: = section section1: SectionModelType.simpleForm options: [ + textField configuration_scope: "Bereich" options: notnull + textField configuration_property: "Eigenschaft" options: + textField configuration_order: "Reihe" options: + textField configuration_type: "Datentyp" options: + textField configuration_value: "Wert" options: + textField configuration_description: "Beschreibung" options: allDbFields allDbFields1 options: ] # create.section1 == page change: PageModelType.change options: = section section1: SectionModelType.simpleForm options: [ + textField configuration_id: "Id" options: primary notnull unique readonly + textField configuration_scope: "Bereich" options: notnull + textField configuration_property: "Eigenschaft" options: + textField configuration_order: "Reihe" options: + textField configuration_type: "Datentyp" options: + textField configuration_value: "Wert" options: + textField configuration_description: "Beschreibung" options: allDbFields allDbFields1 options: ] # change.section1 == page list: PageModelType.list options: @@ -140,25 +168,42 @@ void main() { expect(dump, equals('''= module menu: options: == table menu: options: column menu_id: DataType.int "Id" options: primary notnull unique - column menu_name: DataType.string "Name" options: unique notnull - column menu_icon: DataType.reference "Bild" options: + column menu_role: DataType.reference "Rolle" options: + column menu_starter: DataType.reference "Programmpunkt" options: column menu_createdat: DataType.dateTime "Erzeugt" options: hidden null column menu_createdby: DataType.string "Erzeugt von" options: hidden column menu_changedat: DataType.dateTime "Geändert" options: hidden null column menu_changedby: DataType.string "Geändert von" options: hidden == page create: PageModelType.create options: = section section1: SectionModelType.simpleForm options: [ + textField menu_role: "Rolle" options: + textField menu_starter: "Programmpunkt" options: allDbFields allDbFields1 options: ] # create.section1 == page change: PageModelType.change options: = section section1: SectionModelType.simpleForm options: [ + textField menu_id: "Id" options: primary notnull unique readonly + textField menu_role: "Rolle" options: + textField menu_starter: "Programmpunkt" options: allDbFields allDbFields1 options: ] # change.section1 == page list: PageModelType.list options: = section section1: SectionModelType.filterPanel options: [ - textField menu_name: "Name" options: unique notnull + textField menu_role: "Rolle" options: ] # list.section1 ''')); }); + test('starter', () { + ModelBase.lastId = 0; + logger.clear(); + final module = StarterModel(logger); + module.parse(); + expect(module.fullName(), equals('starter')); + expect(module.widgetName(), equals('starter')); + final errors = logger.errors; + expect(errors.length, equals(0)); + expect(module.dump(StringBuffer()).toString().length, 1377); + }); +}); } diff --git a/test/tool/tool_test.dart b/test/tool/tool_test.dart index cc616e4..895f5c2 100644 --- a/test/tool/tool_test.dart +++ b/test/tool/tool_test.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:dart_bones/dart_bones.dart'; import 'package:flutter_bones/flutter_bones.dart'; -import 'package:flutter_bones/src/model/model_helper.dart'; +import 'package:flutter_bones/src/helper/url_helper.dart'; import 'package:test/test.dart'; import '../../lib/src/model/model_helper.dart' as mh; @@ -10,6 +10,16 @@ import '../../lib/src/model/model_tool.dart'; void main() { final logger = MemoryLogger(LEVEL_FINE); + final configuration = + Configuration('/etc/flutter_bones', 'flutter_bones', logger); + setUpAll(() { + final directory = configuration.asString('baseDirectory', section: 'test'); + if (directory == null) { + logger.error('missing baseDirectory in ' + configuration.filename); + } else { + FileSync.chdir(directory); + } + }); group('sql', () { test('exportSqlCreateTablel', () { logger.clear(); @@ -99,28 +109,38 @@ modules: String target = FileSync.tempDirectory('model_tool_test'); tool.modifyModule([], ['--directory=$target']); expect(logger.errors.length, equals(0)); - expect(tool.fileCache['/tmp/model_tool_test/user/user_change_page.dart'], - equals(bodyUserChangePage)); - expect( + final value = + tool.fileCache['/tmp/model_tool_test/user/user_change_page.dart']; + expect(value, equals(bodyUserChangePage)); + final expected = 'user-create: ' + tool.fileCache['/tmp/model_tool_test/user/user_create_page.dart'] - .length, - equals(2756)); - expect( - tool.fileCache['/tmp/model_tool_test/user/user_list_page.dart'] - .length, - equals(3455)); - expect( - tool.fileCache['/tmp/model_tool_test/configuration/configuration_create_page.dart'] - .length, - equals(2885)); + .length + .toString() + + ' user-list: ' + + tool.fileCache['/tmp/model_tool_test/user/user_list_page.dart'].length + .toString() + + ' config-create: ' + + tool + .fileCache[ + '/tmp/model_tool_test/configuration/configuration_create_page.dart'] + .length + .toString() + + ' config-change: ' + + tool + .fileCache[ + '/tmp/model_tool_test/configuration/configuration_change_page.dart'] + .length + .toString() + + ' config-list: ' + + tool + .fileCache[ + '/tmp/model_tool_test/configuration/configuration_list_page.dart'] + .length + .toString(); expect( - tool.fileCache['/tmp/model_tool_test/configuration/configuration_change_page.dart'] - .length, - equals(3285)); - expect( - tool.fileCache['/tmp/model_tool_test/configuration/configuration_list_page.dart'] - .length, - equals(3583)); + expected, + equals( + 'user-create: 3017 user-list: 4014 config-create: 3200 config-change: 3537 config-list: 4063')); }); test('modify-modules-overwrite-constructors', () { logger.clear(); @@ -132,30 +152,69 @@ modules: expect(logger.errors.length, equals(0)); expect(tool.fileCache['/tmp/model_tool_test/user/user_change_page.dart'], equals(bodyUserChangePage)); - expect( + final expected = 'user-create: ' + tool.fileCache['/tmp/model_tool_test/user/user_create_page.dart'] - .length, - equals(2756)); - expect( - tool.fileCache['/tmp/model_tool_test/user/user_list_page.dart'] - .length, - equals(3455)); + .length + .toString() + + ' user-list: ' + + tool.fileCache['/tmp/model_tool_test/user/user_list_page.dart'].length + .toString(); + expect(expected, equals('user-create: 3017 user-list: 4014')); + }); + }); + group('create-module', () { + test('create-module-single', () { + logger.clear(); + final helper = mh.ModelHelper(); + final tool = ModelToolIo(helper, logger); + String target = FileSync.tempDirectory('model_tool_test'); + FileSync.clearDirectory(target); + tool.createModule( + ['demo', 'admin'], ['--page-type=list', '--directory=$target']); + expect(logger.errors.length, equals(1)); + final expected = tool.fileCache['/tmp/model_tool_test/demo/demo_admin_page.dart'] + .length.toString(); + expect(expected, equals('3974')); + }); + test('create-module-all', () { + logger.clear(); + final helper = mh.ModelHelper(); + final tool = ModelToolIo(helper, logger); + String target = FileSync.tempDirectory('model_tool_test'); + FileSync.clearDirectory(target); + tool.createModule(['demo'], ['--directory=$target']); + expect(logger.errors.length, equals(1)); + final expected = 'change: ' + + tool.fileCache['/tmp/model_tool_test/demo/demo_change_page.dart'] + .length + .toString() + + ' create: ' + + tool.fileCache['/tmp/model_tool_test/demo/demo_create_page.dart'] + .length + .toString() + + ' list: ' + + tool.fileCache['/tmp/model_tool_test/demo/demo_list_page.dart'].length + .toString(); + expect(expected, equals('change: 3440 create: 3017 list: 3951')); }); }); } -final bodyUserChangePage = '''import 'package:flutter/material.dart'; +final bodyUserChangePage = r'''import 'package:flutter/material.dart'; import '../../helper/settings.dart'; import '../../model/button_model.dart'; -import '../../widget/edit_form.dart'; +import '../../widget/dialog_bones.dart'; import '../../widget/page_controller_bones.dart'; +import '../../model/page_model.dart'; import '../application_data.dart'; import 'user_controller.dart'; import 'user_password_page.dart'; +const userChangePageModule = 'user'; +const userChangePageName = 'change'; +const userChangePageType = PageModelType.change; //! === BeginOfGeneratedCode: Change only in areas marked as customized -//! pageType: change class UserChangePage extends StatefulWidget { final ApplicationData applicationData; @@ -190,8 +249,8 @@ abstract class UserChangePageState extends State final ApplicationData applicationData; final int primaryId; final Map initialRow; - final GlobalKey _formKey = - GlobalKey(debugLabel: 'user_change'); + final GlobalKey _formKey = GlobalKey( + debugLabel: '$userChangePageModule-$userChangePageName'); UserController controller; @@ -210,7 +269,7 @@ abstract class UserChangePageState extends State appBar: applicationData.appBarBuilder(controller.page.title), drawer: applicationData.drawerBuilder(context), bottomNavigationBar: applicationData.footerBuilder().widget(controller), - body: EditForm.editForm( + body: formDialog( key: _formKey, pageController: controller, configuration: applicationData.configuration, @@ -223,6 +282,7 @@ abstract class UserChangePageState extends State /// Customize the page, e.g. modify the default widget list given by the model void customize(); + @override void dispose() { controller.dispose(); super.dispose(); @@ -231,8 +291,8 @@ abstract class UserChangePageState extends State @override void initState() { super.initState(); - controller = - UserController(_formKey, this, 'change', context, applicationData); + controller = UserController( + _formKey, this, userChangePageName, context, applicationData); controller.initialize(); customize(); } @@ -257,15 +317,24 @@ class UserChangePageStateCustomized extends UserChangePageState { @override void customize() { ButtonModel button = controller.page.buttonByName('set_password'); - button?.onPressed = () { - String userName = controller.page.fieldByName('user_name').value; - applicationData.pushCaller(controller); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => UserPasswordPage( - primaryId, userName, applicationData, null))); - }; + if (applicationData.currentRolePriority > 20) { + if (! button.hasOption('disabled')) { + button.options.add('disabled'); + } + } else { + button?.onPressed = () { + String userName = controller.page + .fieldByName('user_name') + .value; + applicationData.pushCaller(controller); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + UserPasswordPage( + primaryId, userName, applicationData, null))); + }; + } } }'''; @@ -305,8 +374,20 @@ class ModelToolIo extends ModelTool { } @override - void writeFile(String filename, String content) { + bool writeFile(String filename, String content, + {bool mayExist, bool ensureDirectory}) { + bool rc = true; fileCache[filename] = content; - FileSync.toFile(filename, content); + if (ensureDirectory == null || ensureDirectory) { + FileSync.ensureDirectory( + UrlHelper.parentOf(filename, trailingSlash: false)); + } + if (mayExist != null && !mayExist && FileSync.isFile(filename)) { + logger.error('$filename already exists'); + rc = false; + } else { + FileSync.toFile(filename, content); + } + return rc; } }