From: Hamatoma Date: Mon, 31 Jan 2022 17:50:45 +0000 (+0100) Subject: Helper, Validators, AttendedPage X-Git-Url: https://gitweb.hamatoma.de/?a=commitdiff_plain;h=a398ae5d694eda885996c3243d1467f3e6eb7832;p=exhibition.git Helper, Validators, AttendedPage * helper: new: weekDaysLongEnglish..weekDaysShortGerman * validator: Refactoring: _regExprX -> _regExpX * AttendedPage: ** new: DbColumnChanger ** getRecord() + getRows(): new parameter dbColumnChanger --- diff --git a/lib/base/helper.dart b/lib/base/helper.dart index f5f69a2..0d64e70 100644 --- a/lib/base/helper.dart +++ b/lib/base/helper.dart @@ -12,6 +12,7 @@ final i18n = I18N(); final regExprDateSortable = RegExp(r'(\d{4})[-.](\d\d?)[.-](\d\d?)'); final regExprDateStandard = RegExp(r'(\d\d?)[-.](\d\d?)[.-](\d{4})'); final regExprDateTimeSortable = + // ........1.....1....2.....2....3.....3.4..5.....5.6.....67.8.....87.4 RegExp(r'(\d{4})[-.](\d\d?)[.-](\d\d?)?(\D(\d\d?):(\d\d?)(:(\d\d?))?)?'); final regExprDateTimeStandard = // ......1.....1....2.....2....3.....34..5.....5.6.....67.8.....87.4 @@ -22,7 +23,47 @@ final standardDateTimeFormat = DateFormat('dd.MM.yyyy HH:mm'); final standardDateTimeFormatSeconds = DateFormat('dd.MM.yyyy HH:mm:ss'); -// ..................................1.....1....2.....2....3....3.4.5.....5.6.....67.....7.4 +const weekDaysLongEnglish = [ + '', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday' +]; +const weekDaysShortEnglish = [ + '', + 'Mo', + 'Tue', + 'Wed', + 'Thu', + 'Fri', + 'Sat', + 'Sun' +]; +const weekDaysLongGerman = [ + '', + 'Montag', + 'Dienstag', + 'Mittwoch', + 'Donnerstag', + 'Freitag', + 'Samstag', + 'Sonntag' +]; +const weekDaysShortGerman = [ + '', + 'Mo', + 'Di', + 'Mi', + 'Do', + 'Fr', + 'Sa', + 'So' +]; + /// Handles the [input] as a pattern in a filter list: /// /// Appends '*' if input does not end with a '*'. diff --git a/lib/base/validators.dart b/lib/base/validators.dart index 97bae75..547bb78 100644 --- a/lib/base/validators.dart +++ b/lib/base/validators.dart @@ -1,16 +1,72 @@ import 'package:dart_bones/dart_bones.dart'; +import 'defines.dart'; +import 'helper.dart'; import 'i18n.dart'; final i18n = I18N(); -final _regExprEMailChar = RegExp(r'[\/ "!$%&()?;:,*<>|^°{}\[\]\\=]'); +final _regExpEMailChar = RegExp(r'[\/ "!$%&()?;:,*<>|^°{}\[\]\\=]'); -final _regExprEMailFormat = RegExp(r'^[^@]+@[^@]+\.[a-zA-Z]+$'); +final _regExpEMailFormat = RegExp(r'^[^@]+@[^@]+\.[a-zA-Z]+$'); -final _regExprTime = RegExp(r'^(\d{1,2})(:(\d{1,2}))?$'); +final _regExpDate = RegExp(r'^(\d{1,4})[.-](\d{1,2})[.-](\d{1,4})?$'); +final _regExpTime = RegExp(r'^(\d{1,2})(:(\d{1,2}))?$'); -final _regExprNat = RegExp(r'^\d+$'); +final _regExpNat = RegExp(r'^\d+$'); + +/// Tests whether the [input] is a date, e.g. "1.02.2021". +/// +/// [input]: string to test +/// +/// [mayBeEmpty]: true: input may be null or empty +/// false: if input is empty an error is returned +String? isDate(String? input, + {bool mayBeEmpty = false, bool placeholderAllowed = true}) { + String? rc; + RegExpMatch? match; + if (input == null || input.isEmpty) { + if (!mayBeEmpty) { + rc = i18n.tr('Nothing is not a date'); + } + } else if (!placeholderAllowed || input != '*') { + if ((match = _regExpDate.firstMatch(input)) == null) { + rc = i18n.trArgs( + 'Not a Date: {0} Examples: 1.2.2021 2021-01-22', '!global', [input]); + } else { + int first = int.parse(match!.group(1)!); + int second = int.parse(match.group(2)!); + int third = int.parse(match.group(3)!); + if (second < 1 || second > 12) { + rc = i18n.trArgs('Wrong month in: {0}: 1..12', '!global', [input]); + } else { + int year = first; + int month = second; + int day = third; + if (third > 1900 && first < 32) { + year = third; + day = first; + } + if (year < 1900 || year > 2100) { + rc = + i18n.trArgs('Wrong year in: {0}: 1900..2100', '!global', [input]); + } else if (month > 12) { + rc = i18n.trArgs('Wrong month in: {0}: 1..12', '!global', [input]); + } else if (day > 31) { + rc = i18n.trArgs('Wrong day in: {0}: 1..31', '!global', [input]); + } else { + final date2 = fromString(input, dataType: DataType.date); + if (int.parse(asString(date2, dateOnly: true).substring(0, 2)) != + day) { + rc = + i18n.trArgs('Day too large for month: {0}', '!global', [input]); + } + } + } + } + } + return rc; +} /// Tests whether [input] is an correct email address. /// @@ -20,13 +76,13 @@ String? isEmail(String? input) { if (input == null) { rc = i18n.tr('null is not an email'); } else { - RegExpMatch? match = _regExprEMailChar.firstMatch(input); + RegExpMatch? match = _regExpEMailChar.firstMatch(input); if (countChar(input, '@') > 1) { rc = i18n.tr('too many "@" in email address: ') + input; } else if (match != null) { rc = i18n.trArgs('Illegal character "{0}" in email address', '!global', [match.group(0)!]); - } else if (_regExprEMailFormat.firstMatch(input) == null) { + } else if (_regExpEMailFormat.firstMatch(input) == null) { rc = i18n.tr('Not an email address: ') + input; } } @@ -53,7 +109,7 @@ String? isNat(String? input) { if (input == null) { rc = i18n.tr('null is not a not negative integer'); } else { - RegExpMatch? match = _regExprNat.firstMatch(input); + RegExpMatch? match = _regExpNat.firstMatch(input); if (match == null) { rc = i18n.trArgs( 'Not negative integer expected, not: {0}', '!global', [input]); @@ -71,7 +127,7 @@ String? isTime(String? input, {bool mayBeEmpty = false}) { rc = i18n.tr('Nothing is not a time'); } } else { - if ((match = _regExprTime.firstMatch(input)) == null) { + if ((match = _regExpTime.firstMatch(input)) == null) { rc = i18n .trArgs('Not a time: {0} Examples: 10 or 9:44 ', '!global', [input]); } else if (int.parse(match!.group(1)!) >= 24) { diff --git a/lib/widget/attended_page.dart b/lib/widget/attended_page.dart index 5c4005b..efd2546 100644 --- a/lib/widget/attended_page.dart +++ b/lib/widget/attended_page.dart @@ -1,7 +1,7 @@ -import 'package:exhibition/base/defines.dart'; import 'package:flutter/material.dart'; import 'package:synchronized/synchronized.dart'; +import '../../base/defines.dart'; import '../../base/helper.dart'; import '../../base/i18n.dart'; import '../../persistence/persistence.dart'; @@ -11,6 +11,14 @@ import '../meta/module_meta_data.dart'; final i18n = I18N(); +/// This function can modify a column of a record. +/// [column] is the name of the column. +/// [index] is the index of the column in the record: 0..N-1. +/// [value] is the current value of the column. +/// [record] is the whole record. +typedef DbColumnChanger = dynamic Function( + String column, int index, dynamic value, JsonMap? record); + /// Links some instances belonging to a module page (given as /// member "statefulWidget"). /// @@ -170,7 +178,8 @@ class AttendedPage extends ChangeNotifier { {required int primaryKey, required String what, required Map parameters, - required Function() onDone}) { + required Function() onDone, + DbColumnChanger? dbColumnChanger}) { JsonMap? rc; String name = 'record'; if (!pageStates.dbDataState.hasEntry(name)) { @@ -188,6 +197,12 @@ class AttendedPage extends ChangeNotifier { rc = dbData.singleRecord!; } } + if (dbColumnChanger != null && rc != null) { + int ix = -1; + for (var column in rc.keys) { + rc[column] = dbColumnChanger(column, ++ix, rc[column], rc); + } + } return rc; } @@ -200,12 +215,14 @@ class AttendedPage extends ChangeNotifier { required String columnList, required Function() onDone, //@ToDo: remove required String routeEdit, - required BuildContext context}) { + required BuildContext context, + DbColumnChanger? dbColumnChanger}) { List? rc; if (dbData.recordList != null) { rc = []; for (var record in dbData.recordList!) { final cells = []; + int ix = -1; for (var column in columnList.split(';')) { final primaryKey = moduleMetaData.primaryOf()?.columnName; String value; @@ -215,7 +232,10 @@ class AttendedPage extends ChangeNotifier { } else { value = dbValueToString(record[column], property.dataType); } - cells.add(DataCell(Text(value), onTap: () { + if (dbColumnChanger != null) { + value = dbColumnChanger(column, ++ix, value, record); + } + cells.add(DataCell(Text(value == 'null' ? '' : value), onTap: () { globalData.navigate(context, routeEdit + ';${record[primaryKey]}'); })); } diff --git a/rest_server/data/sql/precedence/users.sql.yaml b/rest_server/data/sql/precedence/users.sql.yaml new file mode 100644 index 0000000..0963352 --- /dev/null +++ b/rest_server/data/sql/precedence/users.sql.yaml @@ -0,0 +1,25 @@ +--- +# Customized SQL statements of the module "Users": + +module: Users +list: + type: list + parameters: [":text",":role"] + order: "user_id" + sql: | + SELECT + t0.*,t1.role_name AS role + FROM users t0 + JOIN roles t1 ON t1.role_id=t0.user_role + WHERE + (:text='' OR user_name like :text + OR user_displayname like :text OR user_email like :text + OR role_name like :text) + AND (:role=0 OR :role=user_role) +insert: + type: insert + parameters: [":name",":displayName",":email",":role",":createdBy"] + sql: | + INSERT INTO users(user_name,user_displayname,user_email,user_role, + user_status,user_createdby,user_createdat) + VALUES(:name,:displayName,:email,:role,1,:createdBy,NOW()); diff --git a/test/validators_test.dart b/test/validators_test.dart index a7c7a48..f02cbfd 100644 --- a/test/validators_test.dart +++ b/test/validators_test.dart @@ -59,4 +59,17 @@ void main() { expect(notEmpty(''), isNotNull); expect(notEmpty(null), isNotNull); }); + test('isDate', () { + expect(isDate('1.2.2021'), isNull); + expect(isDate('01.02.2021'), isNull); + expect(isDate('2001.02.28'), isNull); + expect(isDate('2001.2.8'), isNull); + expect(isDate('1-2-2021'), isNull); + expect(isDate('01-02-2021'), isNull); + expect(isDate('2001-02-28'), isNull); + expect(isDate('2001-2-8'), isNull); + expect(isDate('02.2.8'), matches('1900..2100')); + expect(isDate('2002.13.8'), matches(RegExp(r'.*1[.][.]12'))); + expect(isDate('2002.1.32'), isNotNull); + }); }