]> gitweb.hamatoma.de Git - exhibition.git/commitdiff
Refactoring, installation of a derived project
authorHamatoma <author.hamatoma.de>
Mon, 24 Jan 2022 19:11:35 +0000 (20:11 +0100)
committerHamatoma <author.hamatoma.de>
Mon, 24 Jan 2022 19:11:35 +0000 (20:11 +0100)
* Benchmark: new birthday
* widget:
** attended_page: fix: condition for icon
** new: date_form_field and date_time_form_field
* PageGenerator: Fix: wrong substition for #REQ_RECORD
* Tools:
** metatool/Compile: using absolute path for better understanding
** InitProject: many fixes
** PackRestServer: fix in output

17 files changed:
SvRS [new file with mode: 0755]
X [new file with mode: 0755]
lib/page/benchmarks/edit_benchmark_custom.dart
lib/page/rolestarter/mapping_rolestarter_custom.00.dart [new file with mode: 0644]
lib/page/rolestarter/mapping_rolestarter_custom.01.dart [new file with mode: 0644]
lib/page/start_page.dart
lib/widget/attended_page.dart
lib/widget/date_form_field.dart [new file with mode: 0644]
lib/widget/date_time_form_field.dart [new file with mode: 0644]
metatool/Compile
metatool/bin/page_generator.dart
metatool/rest_server/data/sql/roles.sql.yaml [deleted file]
metatool/rest_server/data/sql/users.sql.yaml [deleted file]
metatool/test/helper_test.dart [deleted file]
metatool/test/validators_test.dart [deleted file]
tools/InitProject
tools/PackRestServer

diff --git a/SvRS b/SvRS
new file mode 100755 (executable)
index 0000000..21a171a
--- /dev/null
+++ b/SvRS
@@ -0,0 +1,7 @@
+#! /bin/bash
+MODULE=rolestarter
+MODULE2=rolestarter
+PAGE=mapping
+cd lib/page/$MODULE
+cp -a ${PAGE}_${MODULE2}_custom.dart ${PAGE}_${MODULE2}_custom.01.dart 
+ls -ld ${PAGE}_${MODULE2}_custom.01.dart 
diff --git a/X b/X
new file mode 100755 (executable)
index 0000000..0545cf0
--- /dev/null
+++ b/X
@@ -0,0 +1,8 @@
+#! /bin/bash
+./ReCreateMetaTool
+cd lib/page
+find -name "*custom.dart" -a ! -name "list_benchmark*.dart" -a ! -name "mapping_*.dart" -delete
+#find -name "*custom.dart" -delete
+#rm edit_user_custom.dart 
+cd ../..
+./Meta update-modules-files | grep custom | grep -v "override"
index de6c688a2a2f20bad8c4bbc1e8590951181864ef..158d44364f7df86dc685feadd5d7d88a03d6034a 100644 (file)
@@ -10,6 +10,7 @@ import '../../persistence/persistence.dart';
 import '../../services/global_widget.dart';
 import '../../setting/global_data.dart';
 import '../../widget/attended_page.dart';
+import '../../widget/date_form_field.dart';
 import '../../widget/message_line.dart';
 import '../../widget/widget_form.dart';
 import 'edit_benchmark_page.dart';
@@ -30,7 +31,12 @@ class EditBenchmarkCustom extends State<EditBenchmarkPage> with MessageLine {
   final birthdayController = TextEditingController();
   final weightController = TextEditingController();
   final incomeController = TextEditingController();
+  final birthdayData = DateData(
+      initialDate: DateTime(1970),
+      firstDate: DateTime(1900),
+      lastDate: DateTime(DateTime.now().year));
   EditBenchmarkCustom(this.primaryKey);
+
   @override
   Widget build(BuildContext context) {
     final rc = Scaffold(
@@ -57,6 +63,7 @@ class EditBenchmarkCustom extends State<EditBenchmarkPage> with MessageLine {
     birthdayController.text = asString(_fieldData.birthday, dateOnly: true);
     weightController.text = asString(_fieldData.weight);
     incomeController.text = asString(_fieldData.income);
+    birthdayData.setInitialDate(_fieldData.birthday);
     final formItems = <FormItem>[
       FormItem(
           TextFormField(
@@ -80,8 +87,10 @@ class EditBenchmarkCustom extends State<EditBenchmarkPage> with MessageLine {
               onSaved: (value) => _fieldData.email = value ?? ''),
           weight: 6),
       FormItem(
-          TextFormField(
-              controller: birthdayController,
+          DateFormField(
+              context: context,
+              dateData: birthdayData,
+              dateController: birthdayData.dateController,
               decoration: InputDecoration(labelText: i18n.tr('Birthday')),
               validator: (input) => notEmpty(input),
               onSaved: (value) => _fieldData.birthday =
@@ -171,10 +180,6 @@ class EditBenchmarkCustom extends State<EditBenchmarkPage> with MessageLine {
     super.initState();
   }
 
-  void requestRecord() => _futureDbData = globalData.restPersistence.query(
-      what: 'query',
-      data: {'module': 'Users', 'sql': 'byId', ':id': primaryKey});
-
   void onStore() {
     final parameters = <String, dynamic>{
       'module': 'Benchmarks',
@@ -197,6 +202,10 @@ class EditBenchmarkCustom extends State<EditBenchmarkPage> with MessageLine {
       onStore();
     }
   }
+
+  void requestRecord() => _futureDbData = globalData.restPersistence.query(
+      what: 'query',
+      data: {'module': 'Benchmarks', 'sql': 'byId', ':id': primaryKey});
 }
 
 class _FieldData {
diff --git a/lib/page/rolestarter/mapping_rolestarter_custom.00.dart b/lib/page/rolestarter/mapping_rolestarter_custom.00.dart
new file mode 100644 (file)
index 0000000..aeddd0f
--- /dev/null
@@ -0,0 +1,183 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/defines.dart';
+import '../../base/helper.dart';
+import '../../base/i18n.dart';
+import '../../services/global_widget.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import '../../persistence/persistence.dart';
+import 'mapping_rolestarter_page.dart';
+
+final i18n = I18N();
+
+class MappingRoleStarterCustom extends State<MappingRoleStarterPage> {
+  final globalData = GlobalData();
+  late Future<DbData> _futureDbDataMember;
+  late Future<DbData> _futureDbDataNotMember;
+  AttendedPage? attendedPage;
+  final _fieldData = _FieldData();
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'CreateRole');
+  final textController = TextEditingController();
+  MappingRoleStarterCustom();
+  @override
+  Widget build(BuildContext context) {
+    final rc = Scaffold(
+      appBar: globalData.appBarBuilder(i18n.tr('#LABEL')),
+      drawer: globalData.drawerBuilder(context),
+      body: SafeArea(
+          child: FutureBuilder<List<DbData>>(
+        future: Future.wait([_futureDbDataMember, _futureDbDataNotMember]),
+        builder: (context, AsyncSnapshot<List<DbData>> snapshot) {
+          Widget rc;
+          if (snapshot.connectionState != ConnectionState.done) {
+            rc = const CircularProgressIndicator();
+          } else {
+            if (snapshot.hasData) {
+              final rowsMembers = attendedPage!.getRows(
+                  dbData: snapshot.data![0],
+                  columnList: 'starter_name;starter_link',
+                  onDone: () => setState(() => 1),
+                  routeEdit: '',
+                  context: context);
+              final rowsNotMembers = attendedPage!.getRows(
+                  dbData: snapshot.data![1],
+                  columnList: 'starter_name;starter_link',
+                  onDone: () => setState(() => 1),
+                  routeEdit: '',
+                  context: context);
+              rc = buildFrame(
+                  rowsMembers: rowsMembers, rowsNotMembers: rowsNotMembers);
+            } else if (snapshot.hasError) {
+              rc = Text('Backend problem: ${snapshot.error}');
+            } else {
+              rc = const CircularProgressIndicator();
+            }
+          }
+          return rc;
+        },
+      )),
+    );
+    return rc;
+  }
+
+  Widget buildFrame(
+      {required JsonList rowsMembers, required JsonList rowsNotMembers}) {
+    final padding = GlobalThemeData.padding;
+    final itemsRoles = <DropdownMenuItem<int>>[];
+    final formItems = <FormItem>[
+      FormItem(
+          DropdownButtonFormField<int>(
+            value: itemsRoles.length == 1 ? 0 : _fieldData.role,
+            items: itemsRoles,
+            isExpanded: true,
+            decoration: InputDecoration(labelText: i18n.tr('Role')),
+            onChanged: (value) => _fieldData.role = value ?? 0,
+          ),
+          weight: 6),
+      FormItem(
+          ElevatedButton(
+              onPressed: () => search(), child: Text(i18n.tr('Search'))),
+          weight: 12,
+          gapAbove: padding),
+    ];
+    final form = Form(
+        key: _formKey,
+        child: Card(
+            color: GlobalThemeData.formBackgroundColor,
+            elevation: GlobalThemeData.formElevation,
+            margin:
+                EdgeInsets.symmetric(vertical: padding, horizontal: padding),
+            child: Padding(
+                padding: EdgeInsets.symmetric(
+                    vertical: padding, horizontal: padding),
+                child: WidgetForm.flexibleGrid(formItems,
+                    screenWidth: attendedPage!.pageStates.screenWidth,
+                    padding: padding))));
+    final tableMembers = DataTable(
+      columns: <DataColumn>[
+        DataColumn(
+          label: Text(i18n.tr('Id')),
+        ),
+        DataColumn(
+          label: Text(i18n.tr('Name')),
+        ),
+      ],
+      rows: rowsMembers as List<DataRow>,
+    );
+    final tableNotMembers = DataTable(
+      columns: <DataColumn>[
+        DataColumn(
+          label: Text(i18n.tr('Id')),
+        ),
+        DataColumn(
+          label: Text(i18n.tr('Name')),
+        ),
+      ],
+      rows: rowsNotMembers as List<DataRow>,
+    );
+    final frameWidget = ListView(children: [
+      form,
+      Text(i18n.tr('The following items are assigned. Click it to remove.')),
+      SizedBox(height: padding),
+      tableMembers,
+      SizedBox(height: padding),
+      Text(
+          i18n.tr('The following items are not assigned. Click it to assign.')),
+      SizedBox(height: padding),
+      tableNotMembers,
+    ]);
+    return frameWidget;
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    requestRecords();
+  }
+
+  @override
+  void dispose() {
+    helperDummyUsage(DataType.string);
+    globalWidgetDummyUsage();
+    textController.dispose();
+    super.dispose();
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  void requestRecords() {
+    _futureDbDataMember =
+        globalData.restPersistence.query(what: 'query', data: {
+      'module': 'RoleStarter',
+      'sql': 'members',
+      ':role': _fieldData.role,
+    });
+    _futureDbDataNotMember =
+        globalData.restPersistence.query(what: 'query', data: {
+      'module': 'RoleStarter',
+      'sql': 'notmembers',
+      ':role': _fieldData.role,
+    });
+  }
+
+  void search() {
+    attendedPage!.pageStates.dbDataState.clear();
+    if (_formKey.currentState!.validate()) {
+      _formKey.currentState!.save();
+      requestRecords();
+      setState(() => 1);
+    }
+  }
+}
+
+class _FieldData {
+  int role = 0;
+}
diff --git a/lib/page/rolestarter/mapping_rolestarter_custom.01.dart b/lib/page/rolestarter/mapping_rolestarter_custom.01.dart
new file mode 100644 (file)
index 0000000..c03e7da
--- /dev/null
@@ -0,0 +1,304 @@
+// This file is created by the meta_tool. But it can be customized.
+// It will never overridden by the meta_tool.
+import 'package:flutter/material.dart';
+
+import '../../base/defines.dart';
+import '../../base/helper.dart';
+import '../../base/i18n.dart';
+import '../../persistence/persistence.dart';
+import '../../services/global_widget.dart';
+import '../../setting/global_data.dart';
+import '../../widget/attended_page.dart';
+import '../../widget/widget_form.dart';
+import 'mapping_rolestarter_page.dart';
+
+final i18n = I18N();
+
+class CommonInfo {
+  final List<String> columns;
+  final List<MemberInfo> members = [];
+  CommonInfo({required this.columns});
+}
+
+class MappingRoleStarterCustom extends State<MappingRoleStarterPage> {
+  final globalData = GlobalData();
+  late Future<DbData> _futureDbDataCommon;
+  late Future<DbData> _futureDbDataMember;
+  late Future<DbData> _futureDbDataConnection;
+  var membersOfCommon = <int, List<int>>{};
+  late AttendedPage attendedPage;
+  final _fieldData = _FieldData();
+  final GlobalKey<FormState> _formKey =
+      GlobalKey<FormState>(debugLabel: 'CreateRole');
+  final textController = TextEditingController();
+  final commonColumns = ['role_name'];
+  final memberColumns = ['starter_name', 'starter_route'];
+  final commonInfos = <int, CommonInfo>{};
+
+  /// Members of the current common module:
+  final memberInfos = <MemberInfo>[];
+
+  final notMemberInfos = <MemberInfo>[];
+  JsonList membersRows = [];
+  JsonList commonRows = [];
+  JsonList connectionRows = [];
+  MappingRoleStarterCustom();
+
+  @override
+  Widget build(BuildContext context) {
+    final rc = Scaffold(
+      appBar: globalData.appBarBuilder(i18n.tr('Role/Starter Assignment')),
+      drawer: globalData.drawerBuilder(context),
+      body: SafeArea(
+          child: FutureBuilder<List<DbData>>(
+        future: Future.wait([
+          _futureDbDataMember,
+          _futureDbDataCommon,
+          _futureDbDataConnection
+        ]),
+        builder: (context, AsyncSnapshot<List<DbData>> snapshot) {
+          Widget rc;
+          if (snapshot.connectionState != ConnectionState.done) {
+            rc = const CircularProgressIndicator();
+          } else {
+            if (snapshot.hasData) {
+              commonRows = snapshot.data![0].recordList ?? [];
+              membersRows = snapshot.data![1].recordList ?? [];
+              connectionRows = snapshot.data![2].recordList ?? [];
+              handleConnectionRows();
+              rc = buildFrame();
+            } else if (snapshot.hasError) {
+              rc = Text('Backend problem: ${snapshot.error}');
+            } else {
+              rc = const CircularProgressIndicator();
+            }
+          }
+          return rc;
+        },
+      )),
+    );
+    return rc;
+  }
+
+  /// Builds the items of the common module combobox from the database records.
+  List<DropdownMenuItem<int>> buildCommonItems() {
+    final rc = <DropdownMenuItem<int>>[];
+    for (var record in commonRows) {
+      rc.add(DropdownMenuItem<int>(
+          value: record['role_id'], child: Text(record['role_name'])));
+    }
+    return rc;
+  }
+
+  Widget buildFrame() {
+    final padding = GlobalThemeData.padding;
+    final itemsRoles = <DropdownMenuItem<int>>[];
+    final formItems = <FormItem>[
+      FormItem(
+          DropdownButtonFormField<int>(
+            value: itemsRoles.length == 1 ? 0 : _fieldData.role,
+            items: buildCommonItems(),
+            isExpanded: true,
+            decoration: InputDecoration(labelText: i18n.tr('Role')),
+            onChanged: (value) => _fieldData.role = value ?? 0,
+          ),
+          weight: 6),
+      FormItem(
+          ElevatedButton(onPressed: () => save(), child: Text(i18n.tr('Save'))),
+          weight: 12,
+          gapAbove: padding),
+    ];
+    final form = Form(
+        key: _formKey,
+        child: Card(
+            color: GlobalThemeData.formBackgroundColor,
+            elevation: GlobalThemeData.formElevation,
+            margin:
+                EdgeInsets.symmetric(vertical: padding, horizontal: padding),
+            child: Padding(
+                padding: EdgeInsets.symmetric(
+                    vertical: padding, horizontal: padding),
+                child: WidgetForm.flexibleGrid(formItems,
+                    screenWidth: attendedPage.pageStates.screenWidth,
+                    padding: padding))));
+    final tableMembers = buildMemberTable();
+    final tableNotMembers = buildNotMemberTable();
+    final frameWidget = ListView(children: [
+      form,
+      Text(i18n.tr('The following items are assigned. Click it to remove.')),
+      SizedBox(height: padding),
+      tableMembers,
+      SizedBox(height: padding),
+      Text(
+          i18n.tr('The following items are not assigned. Click it to assign.')),
+      SizedBox(height: padding),
+      tableNotMembers,
+    ]);
+    return frameWidget;
+  }
+
+  buildMemberTable() {
+    final rows = buildTableItems(
+        commonInfos[_fieldData.role]!.members, 'rolestarter_id');
+    final headers = <DataColumn>[];
+    for (var column in commonInfos[_fieldData.role]!.columns) {
+      final label =
+          attendedPage.moduleMetaData.propertyByColumnName(column)?.label ??
+              i18n.tr('Info');
+      headers.add(DataColumn(label: Text(label)));
+    }
+    final rc = DataTable(
+      columns: headers,
+      rows: rows,
+    );
+    return rc;
+  }
+
+  buildNotMemberTable() {
+    final rows = buildTableItems(
+        commonInfos[_fieldData.role]!.members, 'rolestarter_id');
+    final headers = <DataColumn>[];
+    for (var column in commonInfos[_fieldData.role]!.columns) {
+      final label =
+          attendedPage.moduleMetaData.propertyByColumnName(column)?.label ??
+              i18n.tr('Info');
+      headers.add(DataColumn(label: Text(label)));
+    }
+    final rc = DataTable(
+      columns: headers,
+      rows: rows,
+    );
+    return rc;
+  }
+
+  /// Build the [notMemberInfos] list of items not being members of the common
+  /// module with [commonId].
+  void buildNotMembers(int commonId) {
+    notMemberInfos.clear();
+    int order = 10;
+    for (var record in membersRows) {
+      final id = record['rolestarter_starter'];
+      if (record['rolestarter_role'] == commonId && !hasMember(id)) {
+        notMemberInfos.add(MemberInfo(
+            memberId: id, position: order++, columns: memberColumns));
+      }
+    }
+  }
+
+  buildTableItems(List<MemberInfo> members, String primary) {
+    members.map((MemberInfo element) {
+      final cells = <DataCell>[];
+      final record = findRecord(membersRows, primary, element.memberId);
+      if (record != null) {
+        for (var column in element.columns) {
+          cells.add(DataCell(Text(record[column])));
+        }
+      }
+      return DataRow(cells: cells);
+    });
+  }
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    requestRecords();
+  }
+
+  @override
+  void dispose() {
+    helperDummyUsage(DataType.string);
+    globalWidgetDummyUsage();
+    textController.dispose();
+    super.dispose();
+  }
+
+  /// Finds a record in a list of records by a given [id] in a given [column].
+  JsonMap? findRecord(JsonList rows, String column, int id) {
+    JsonMap? rc;
+    for (var record in rows) {
+      if (record[column] == id) {
+        rc = record;
+        break;
+      }
+    }
+    return rc;
+  }
+
+  /// Inspects the [connectionRows] and build the internal structures.
+  void handleConnectionRows() {
+    commonInfos.clear();
+    for (var record in commonRows) {
+      int common = record['role_id'];
+      commonInfos[common] = CommonInfo(columns: commonColumns);
+    }
+    for (var record in connectionRows) {
+      int common = record['rolestarter_role'];
+      if (!commonInfos.containsKey(common)) {
+        commonInfos[common] = CommonInfo(columns: commonColumns);
+      }
+      commonInfos[common]!.members.add(MemberInfo(
+          memberId: record['rolestarter_starter'],
+          columns: memberColumns,
+          position: record['rolestarter_position']));
+    }
+    for (var key in commonInfos.keys) {
+      commonInfos[key]!.members.sort((a, b) => a.position - b.position);
+    }
+  }
+
+  /// Returns whether the given [memberId] is part of [memberInfos].
+  bool hasMember(int memberId) {
+    bool rc = false;
+    for (var item in memberInfos) {
+      if (item.memberId == memberId) {
+        rc = true;
+        break;
+      }
+    }
+    return rc;
+  }
+
+  @override
+  void initState() {
+    super.initState();
+  }
+
+  void requestRecords() {
+    _futureDbDataMember =
+        globalData.restPersistence.query(what: 'query', data: {
+      'module': 'Starters',
+      'sql': 'all',
+    });
+    _futureDbDataCommon =
+        globalData.restPersistence.query(what: 'query', data: {
+      'module': 'Roles',
+      'sql': 'all',
+    });
+    _futureDbDataConnection =
+        globalData.restPersistence.query(what: 'query', data: {
+      'module': 'RoleStarter',
+      'sql': 'all',
+    });
+  }
+
+  void save() {
+    attendedPage.pageStates.dbDataState.clear();
+    if (_formKey.currentState!.validate()) {
+      _formKey.currentState!.save();
+      requestRecords();
+      setState(() => 1);
+    }
+  }
+}
+
+class MemberInfo {
+  final int memberId;
+  final int position;
+  final List<String> columns;
+  MemberInfo(
+      {required this.memberId, required this.position, required this.columns});
+}
+
+class _FieldData {
+  int role = 0;
+}
index 845dc5c65cceb07f85e111efdf32b3546491c589..129f8d460b07401f414214a0a41f0e46766c7991 100644 (file)
@@ -96,7 +96,8 @@ class StartPageState extends State<StartPage> {
                     RestPersistence.fromConfig(configuration, logger),
                     logger);
                 globalData.initializeAsync().then((value) {
-                  globalData.navigate(context, '/RoleStarter/mapping');
+                  // globalData.navigate(context, '/RoleStarter/mapping');
+                  globalData.navigate(context, '/Benchmarks/list');
                 });
               }
             });
index 7ddf1d1c044a7ed032543addf9d89cff8d7bb15b..638c3796e0d004da4ed57e43842c38ed5ddc319d 100644 (file)
@@ -122,7 +122,7 @@ class AttendedPage extends ChangeNotifier {
             ? Icons.beenhere_outlined
             : Icons.first_page_outlined;
       } else if (ix == chipCount - 1) {
-        icon = currentPage == 1
+        icon = currentPage == pageCount
             ? Icons.beenhere_outlined
             : Icons.last_page_outlined;
       } else if (ix == currentIndex) {
diff --git a/lib/widget/date_form_field.dart b/lib/widget/date_form_field.dart
new file mode 100644 (file)
index 0000000..8533d8b
--- /dev/null
@@ -0,0 +1,153 @@
+import 'package:flutter/material.dart';
+
+import '../base/helper.dart';
+import '../base/i18n.dart';
+import '../setting/global_data.dart';
+
+final i18n = I18N();
+
+/// Manages the main attributes of a DateFormField.
+class DateData {
+  DateTime? initialDate;
+  late final DateTime firstDate;
+  late final DateTime lastDate;
+  DateTime? selectedDate;
+  final Function? onSelected;
+  final dateController = TextEditingController();
+
+  /// Constructor.
+  ///
+  /// [inititialDate]: the date that is displayed while starting the date picker.
+  ///
+  /// [firstDate]: the oldest date showed in the date picker.
+  ///
+  /// [lastDate]: the youngest date showed in the date picker.
+  ///
+  /// [onSelected]: this function is called after a date is selected.
+  DateData(
+      {DateTime? initialDate,
+      DateTime? firstDate,
+      DateTime? lastDate,
+      this.onSelected}) {
+    final now = DateTime.now();
+    this.initialDate = initialDate ?? now;
+    this.lastDate = lastDate ?? DateTime(now.year + 1);
+    this.firstDate = firstDate ?? DateTime(now.year - 1);
+    selectedDate = now;
+  }
+
+  /// Handles the "button pressed" event in DateFormField.
+  onButtonPressed(BuildContext context,
+      {required Function(DateTime selected) storeSelected}) async {
+    final DateTime? picked = await showDatePicker(
+      context: context,
+      initialDate: initialDate ?? DateTime(1970), // Refer step 1
+      firstDate: firstDate,
+      lastDate: lastDate,
+    );
+    if (picked != null) {
+      selectedDate = picked;
+      dateController.text = asString(picked, dateOnly: true);
+      if (onSelected != null) {
+        onSelected!();
+      }
+    }
+  }
+
+  /// Sets the initial date to [dateTime].
+  void setInitialDate(DateTime? dateTime) {
+    initialDate = dateTime;
+    dateController.text =
+        dateTime == null ? '' : asString(dateTime, dateOnly: true);
+  }
+}
+
+/// Manages a text field with a button to call a date picker.
+class DateFormField extends Container {
+  final DateData dateData;
+  final TextEditingController dateController;
+  DateFormField(
+      {required BuildContext context,
+      required this.dateData,
+      required this.dateController,
+      Key? key,
+      FocusNode? focusNode,
+      InputDecoration? decoration = const InputDecoration(),
+      TextInputType? keyboardType = TextInputType.datetime,
+      TextInputAction? textInputAction,
+      TextStyle? style,
+      StrutStyle? strutStyle,
+      TextAlign textAlign = TextAlign.start,
+      TextAlignVertical? textAlignVertical,
+      bool autofocus = false,
+      bool readOnly = false,
+      ToolbarOptions? toolbarOptions,
+      bool? showCursor,
+      bool autocorrect = true,
+      SmartDashesType? smartDashesType,
+      SmartQuotesType? smartQuotesType,
+      bool enableSuggestions = true,
+      bool autovalidate = false,
+      bool maxLengthEnforced = true,
+      bool expands = false,
+      int? maxLength,
+      ValueChanged<String>? onChanged,
+      GestureTapCallback? onTap,
+      VoidCallback? onEditingComplete,
+      ValueChanged<String>? onFieldSubmitted,
+      FormFieldSetter<String>? onSaved,
+      FormFieldValidator<String>? validator,
+      bool? enabled,
+      double cursorWidth = 2.0,
+      double? cursorHeight,
+      Radius? cursorRadius,
+      Color? cursorColor,
+      Brightness? keyboardAppearance,
+      EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
+      bool enableInteractiveSelection = true,
+      TextSelectionControls? selectionControls,
+      InputCounterWidgetBuilder? buildCounter,
+      ScrollPhysics? scrollPhysics,
+      Iterable<String>? autofillHints,
+      AutovalidateMode? autovalidateMode,
+      ScrollController? scrollController,
+      String? restorationId,
+      bool enableIMEPersonalizedLearning = true,
+      double? gapWidth,
+      DateTime? initialDate})
+      : super(
+            child: Row(children: <Widget>[
+          Expanded(
+              child: TextFormField(
+            onTap: onTap,
+            controller: dateController,
+            onEditingComplete: onEditingComplete,
+            onFieldSubmitted: onFieldSubmitted,
+            onSaved: onSaved,
+            validator: validator,
+            enabled: enabled,
+            cursorWidth: cursorWidth,
+            cursorHeight: cursorHeight,
+            cursorRadius: cursorRadius,
+            cursorColor: cursorColor,
+            keyboardAppearance: keyboardAppearance,
+            scrollPadding: scrollPadding,
+            enableInteractiveSelection: enableInteractiveSelection,
+            selectionControls: selectionControls,
+            buildCounter: buildCounter,
+            scrollPhysics: scrollPhysics,
+            autofillHints: autofillHints,
+            autovalidateMode: autovalidateMode,
+            scrollController: scrollController,
+            restorationId: restorationId,
+            enableIMEPersonalizedLearning: enableIMEPersonalizedLearning,
+          )),
+          SizedBox(width: gapWidth ?? GlobalThemeData.padding),
+          ElevatedButton(
+              onPressed: () =>
+                  dateData.onButtonPressed(context, storeSelected: (selected) {
+                    dateData.selectedDate = selected;
+                  }),
+              child: Icon(Icons.calendar_today_outlined)),
+        ]));
+}
diff --git a/lib/widget/date_time_form_field.dart b/lib/widget/date_time_form_field.dart
new file mode 100644 (file)
index 0000000..e9fc3fb
--- /dev/null
@@ -0,0 +1,180 @@
+import 'package:flutter/material.dart';
+import 'package:sprintf/sprintf.dart';
+
+import '../base/helper.dart';
+import '../base/i18n.dart';
+import '../setting/global_data.dart';
+
+final i18n = I18N();
+
+/// Manages the main attributes of a DateFormField.
+class DateTimeData {
+  DateTime? initialDate;
+  TimeOfDay? initialTime;
+  late final DateTime firstDate;
+  late final DateTime lastDate;
+  DateTime? selectedDate;
+  TimeOfDay? selectedTime;
+  final Function? onSelected;
+  final dateController = TextEditingController();
+
+  /// Constructor.
+  ///
+  /// [inititialDate]: the date that is displayed while starting the date picker.
+  /// [inititialDate]: the date that is displayed while starting the date picker.
+  ///
+  /// [firstDate]: the oldest date showed in the date picker.
+  ///
+  /// [lastDate]: the youngest date showed in the date picker.
+  ///
+  /// [onSelected]: this function is called after a date is selected.
+  DateTimeData(
+      {DateTime? initialDateTime,
+      DateTime? initialDate,
+      TimeOfDay? initialTime,
+      DateTime? firstDate,
+      DateTime? lastDate,
+      this.onSelected}) {
+    final now = DateTime.now();
+    if (initialDateTime != null) {
+      this.initialDate = DateTime(
+          initialDateTime.year, initialDateTime.month, initialDateTime.day);
+      this.initialTime =
+          TimeOfDay(hour: initialDateTime.hour, minute: initialDateTime.minute);
+    } else {
+      this.initialDate = initialDate;
+      this.initialTime = initialTime;
+    }
+    this.initialDate = initialDate ?? now;
+    this.initialTime = initialTime ?? TimeOfDay(hour: 8, minute: 0);
+    this.lastDate = lastDate ?? DateTime(now.year + 1);
+    this.firstDate = firstDate ?? DateTime(now.year - 1);
+    selectedDate = initialDate;
+  }
+
+  /// Handles the "button pressed" event in DateFormField.
+  onButtonPressed(BuildContext context,
+      {required Function(DateTime selected) storeSelected}) async {
+    final TimeOfDay? picked = await showTimePicker(
+      context: context,
+      initialTime: initialTime ?? TimeOfDay(hour: 8, minute: 0),
+    );
+    if (picked != null) {
+      selectedTime = picked;
+      dateController.text = asString(selectedDate, dateOnly: true) +
+          sprintf(' %02d:%02d', [picked.hour, picked.minute]);
+      if (onSelected != null) {
+        onSelected!();
+      }
+    }
+  }
+
+  /// Sets the initial date to [dateTime].
+  void setInitialDate(DateTime? dateTime) {
+    if (dateTime == null) {
+      dateController.text = '';
+      initialDate = null;
+      initialTime = null;
+    } else {
+      initialDate = DateTime(dateTime.year, dateTime.month, dateTime.day);
+      dateController.text = asString(dateTime, dateOnly: true);
+    }
+  }
+}
+
+/// Manages a text field with a button to call a date picker.
+class DateTimeFormField extends Container {
+  final DateTimeData dateData;
+  final TextEditingController dateController;
+  DateTimeFormField(
+      {required BuildContext context,
+      required this.dateData,
+      required this.dateController,
+      Key? key,
+      FocusNode? focusNode,
+      InputDecoration? decoration = const InputDecoration(),
+      TextInputType? keyboardType = TextInputType.datetime,
+      TextInputAction? textInputAction,
+      TextStyle? style,
+      StrutStyle? strutStyle,
+      TextAlign textAlign = TextAlign.start,
+      TextAlignVertical? textAlignVertical,
+      bool autofocus = false,
+      bool readOnly = false,
+      ToolbarOptions? toolbarOptions,
+      bool? showCursor,
+      bool autocorrect = true,
+      SmartDashesType? smartDashesType,
+      SmartQuotesType? smartQuotesType,
+      bool enableSuggestions = true,
+      bool autovalidate = false,
+      bool maxLengthEnforced = true,
+      bool expands = false,
+      int? maxLength,
+      ValueChanged<String>? onChanged,
+      GestureTapCallback? onTap,
+      VoidCallback? onEditingComplete,
+      ValueChanged<String>? onFieldSubmitted,
+      FormFieldSetter<String>? onSaved,
+      FormFieldValidator<String>? validator,
+      bool? enabled,
+      double cursorWidth = 2.0,
+      double? cursorHeight,
+      Radius? cursorRadius,
+      Color? cursorColor,
+      Brightness? keyboardAppearance,
+      EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
+      bool enableInteractiveSelection = true,
+      TextSelectionControls? selectionControls,
+      InputCounterWidgetBuilder? buildCounter,
+      ScrollPhysics? scrollPhysics,
+      Iterable<String>? autofillHints,
+      AutovalidateMode? autovalidateMode,
+      ScrollController? scrollController,
+      String? restorationId,
+      bool enableIMEPersonalizedLearning = true,
+      double? gapWidth,
+      DateTime? initialDate})
+      : super(
+            child: Row(children: <Widget>[
+          Expanded(
+              child: TextFormField(
+            onTap: onTap,
+            controller: dateController,
+            onEditingComplete: onEditingComplete,
+            onFieldSubmitted: onFieldSubmitted,
+            onSaved: onSaved,
+            validator: validator,
+            enabled: enabled,
+            cursorWidth: cursorWidth,
+            cursorHeight: cursorHeight,
+            cursorRadius: cursorRadius,
+            cursorColor: cursorColor,
+            keyboardAppearance: keyboardAppearance,
+            scrollPadding: scrollPadding,
+            enableInteractiveSelection: enableInteractiveSelection,
+            selectionControls: selectionControls,
+            buildCounter: buildCounter,
+            scrollPhysics: scrollPhysics,
+            autofillHints: autofillHints,
+            autovalidateMode: autovalidateMode,
+            scrollController: scrollController,
+            restorationId: restorationId,
+            enableIMEPersonalizedLearning: enableIMEPersonalizedLearning,
+          )),
+          SizedBox(width: gapWidth ?? GlobalThemeData.padding),
+          ElevatedButton(
+              onPressed: () =>
+                  dateData.onButtonPressed(context, storeSelected: (selected) {
+                    dateData.selectedDate = selected;
+                  }),
+              child: Icon(Icons.calendar_today_outlined)),
+          SizedBox(width: gapWidth ?? GlobalThemeData.padding),
+          ElevatedButton(
+              onPressed: () =>
+                  dateData.onButtonPressed(context, storeSelected: (selected) {
+                    dateData.selectedDate = selected;
+                  }),
+              child: Icon(Icons.lock_clock_outlined)),
+        ]));
+}
index 4fb107951c4a295fc914b5e9bb165943bcc38fbc..43dd5c890c789ce163123e5199c0ebc8e2e562d8 100755 (executable)
@@ -1,5 +1,4 @@
 #! /bin/bash
 APP=meta_tool
-/usr/bin/dart compile exe bin/$APP.dart -o ../tools/$APP
-
-
+OUT=$(dirname $(pwd))/tools
+/usr/bin/dart compile exe bin/$APP.dart -o $OUT/$APP
index 982d8c9f962e52ce5e12798bf2240bbe2efb5616..519d72db50fb52e11583fea0c8d4de5a0102f457 100644 (file)
@@ -1068,7 +1068,7 @@ StatefulWidget? customPageByRoute(String route) {
         break;
     }
     var rc = replaceVariables(templateRecordCustom, page)
-        .replaceFirst('#REQ_RECORD', requestRecord)
+        .replaceFirst('#REQ_RECORD', replaceVariables(requestRecord, page))
         .replaceFirst('#DIDCHANGE', didChangeDependencies)
         .replaceFirst('#DEF_CONTROLLER', buildDefinitionControllers(page))
         .replaceFirst('#INIT_COMBO', buildInitializeComboBoxes(page))
diff --git a/metatool/rest_server/data/sql/roles.sql.yaml b/metatool/rest_server/data/sql/roles.sql.yaml
deleted file mode 100644 (file)
index 3f68681..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
----
-# DO NOT CHANGE. This file is created by the meta_tool
-# SQL statements of the module "Roles":
-
-module: Roles
-list:
-  type: list
-  parameters: []
-  sql: "SELECT
-    t0.*
-    FROM Roles t0
-
-    ;"
-byId:
-  type: record
-  parameters: [ "id" ]
-  sql: "SELECT * FROM Roles WHERE role_id=:id;"
-delete:
-  type: delete
-  parameters: [ "id" ]
-  sql: "DELETE * FROM Roles WHERE role_id=:id;"
-update:
-  type: update
-  parameters: [":id",":name",":changedBy"]
-  sql: "UPDATE Roles SET
-    role_id=:id,role_name=:name,role_changedby=:changedBy,role_changed=NOW()
-    WHERE role_id=:id;"
-insert:
-  type: insert
-  parameters: [":id",":name",":createdBy"]
-  sql: "INSERT INTO Roles(role_id,role_name,role_createdby,role_created)
-    VALUES(:id,:name,:createdBy,NOW());"
diff --git a/metatool/rest_server/data/sql/users.sql.yaml b/metatool/rest_server/data/sql/users.sql.yaml
deleted file mode 100644 (file)
index 85a06af..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
----
-# DO NOT CHANGE. This file is created by the meta_tool
-# SQL statements of the module "Users":
-
-module: Users
-list:
-  type: list
-  parameters: []
-  sql: "SELECT
-    t0.*,t1.role_name AS role
-    FROM Users t0
-    JOIN roles t1 ON t1.role_id=t0.user_role
-    ;"
-byId:
-  type: record
-  parameters: [ "id" ]
-  sql: "SELECT * FROM Users WHERE user_id=:id;"
-delete:
-  type: delete
-  parameters: [ "id" ]
-  sql: "DELETE * FROM Users WHERE user_id=:id;"
-update:
-  type: update
-  parameters: [":id",":name",":displayName",":email",":role",":changedBy"]
-  sql: "UPDATE Users SET
-    user_id=:id,user_name=:name,user_displayname=:displayName,user_email=:email,
-    user_role=:role,user_changedby=:changedBy,user_changed=NOW()
-    WHERE user_id=:id;"
-insert:
-  type: insert
-  parameters: [":id",":name",":displayName",":email",":role",":createdBy"]
-  sql: "INSERT INTO Users(user_id,user_name,user_displayname,user_email,
-      user_role,user_createdby,user_created)
-    VALUES(:id,:name,:displayName,:email,:role,:createdBy,NOW());"
diff --git a/metatool/test/helper_test.dart b/metatool/test/helper_test.dart
deleted file mode 100644 (file)
index df176d6..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-import 'package:dart_bones/dart_bones.dart';
-import 'package:test/test.dart';
-//import 'package:path/path.dart';
-
-import '../lib/base/defines.dart';
-import '../lib/base/helper.dart';
-import '../lib/base/i18n.dart';
-
-final i18n = I18N();
-
-void main() {
-  final logger = MemoryLogger(LEVEL_FINE);
-  I18N.internal(logger);
-  FileSync.initialize(logger);
-  //final fileSync = FileSync();
-  //final baseDir = init(logger);
-  //final targetDir = join(baseDir, nodeTarget);
-  //fileSync.ensureDirectory(targetDir);
-  group('secondsAsTime', () {
-    test('days', () {
-      expect(secondsAsTime(3 * 86400 + 5 * 3600 + 14 * 60 + 32), '3:05:14:32');
-    });
-    test('hours', () {
-      expect(secondsAsTime(23 * 3600 + 9 * 60 + 4), '23:09:04');
-    });
-    test('minutes', () {
-      expect(secondsAsTime(3 * 60 + 19), '03:19');
-    });
-    test('seconds', () {
-      expect(secondsAsTime(59), '59');
-    });
-    test('days-millisecnds', () {
-      expect(
-          secondsAsTime((3 * 86400 + 5 * 3600 + 14 * 60 + 32) * 1000 + 49,
-              isMilliSeconds: true),
-          '3:05:14:32.049');
-    });
-  });
-  group('jsonToObject', () {
-    test('bool', () {
-      expect(jsonToObject('T', dataType: DataType.bool, defaultValue: false),
-          isTrue);
-      expect(jsonToObject('F', dataType: DataType.bool, defaultValue: true),
-          isFalse);
-      expect(jsonToObject('x', dataType: DataType.bool, defaultValue: true),
-          isTrue);
-      expect(jsonToObject('y', dataType: DataType.bool, defaultValue: false),
-          isFalse);
-    });
-    test('datetime', () {
-      expect(
-          jsonToObject('2021-8-3 2:4',
-              dataType: DataType.datetime, defaultValue: false),
-          DateTime(2021, 8, 3, 2, 4));
-    });
-    test('date', () {
-      expect(
-          jsonToObject('2021-10-3',
-              dataType: DataType.date, defaultValue: false),
-          DateTime(2021, 10, 3));
-    });
-    test('int-nat-reference', () {
-      expect(jsonToObject(332211, dataType: DataType.nat), 332211);
-      expect(jsonToObject('332209', dataType: DataType.int), 332209);
-      expect(jsonToObject('220944', dataType: DataType.reference), 220944);
-      expect(jsonToObject(2209443, dataType: DataType.reference), 2209443);
-      expect(jsonToObject('xy', dataType: DataType.reference), isNull);
-    });
-  });
-  group('fromString', () {
-    test('bool', () {
-      expect(fromString('T', dataType: DataType.bool, defaultValue: false),
-          isTrue);
-      expect(fromString('F', dataType: DataType.bool, defaultValue: true),
-          isFalse);
-      expect(
-          fromString(i18n.tr('Yes'),
-              dataType: DataType.bool, defaultValue: false),
-          isTrue);
-      expect(
-          fromString(i18n.tr('No'),
-              dataType: DataType.bool, defaultValue: true),
-          isFalse);
-      expect(fromString('', dataType: DataType.bool, defaultValue: false),
-          isFalse);
-      expect(
-          fromString('', dataType: DataType.bool, defaultValue: true), isTrue);
-    });
-    test('int', () {
-      expect(fromString('123', dataType: DataType.int, defaultValue: -1), 123);
-      expect(
-          fromString('-1234', dataType: DataType.int, defaultValue: -1), -1234);
-      expect(fromString('', dataType: DataType.int, defaultValue: -1), -1);
-      expect(fromString('a15', dataType: DataType.int, defaultValue: -1), -1);
-    });
-    test('nat', () {
-      expect(fromString('123', dataType: DataType.nat, defaultValue: -1), 123);
-      expect(fromString('-1234', dataType: DataType.nat, defaultValue: -1), -1);
-      expect(fromString('', dataType: DataType.nat, defaultValue: -1), -1);
-      expect(fromString('a15', dataType: DataType.nat, defaultValue: -1), -1);
-    });
-    test('reference', () {
-      expect(fromString('123', dataType: DataType.reference, defaultValue: -1),
-          123);
-      expect(
-          fromString('-1234', dataType: DataType.reference, defaultValue: -1),
-          -1234);
-      expect(
-          fromString('', dataType: DataType.reference, defaultValue: -1), -1);
-      expect(fromString('a15', dataType: DataType.reference, defaultValue: -1),
-          -1);
-    });
-    test('float', () {
-      expect(fromString('123.76', dataType: DataType.float, defaultValue: -1),
-          123.76);
-      expect(fromString('-4123.76', dataType: DataType.float, defaultValue: -1),
-          -4123.76);
-      expect(fromString('', dataType: DataType.float, defaultValue: -99999999),
-          -99999999);
-      expect(
-          fromString('Nothing',
-              dataType: DataType.float, defaultValue: -99999999),
-          -99999999);
-    });
-    test('currency', () {
-      expect(
-          fromString('123.76', dataType: DataType.currency, defaultValue: -1),
-          123.76);
-      expect(
-          fromString('-4123.76', dataType: DataType.currency, defaultValue: -1),
-          -4123.76);
-      expect(
-          fromString('', dataType: DataType.currency, defaultValue: -99999999),
-          -99999999);
-      expect(
-          fromString('Nothing',
-              dataType: DataType.currency, defaultValue: -99999999),
-          -99999999);
-    });
-    test('date', () {
-      expect(fromString('7.3.1987', dataType: DataType.date),
-          DateTime(1987, 3, 7));
-      expect(fromString('17.12.2035', dataType: DataType.date),
-          DateTime(2035, 12, 17));
-      expect(fromString('2012.6.2', dataType: DataType.date),
-          DateTime(2012, 6, 2));
-      expect(fromString('1923.10.31', dataType: DataType.date),
-          DateTime(1923, 10, 31));
-      expect(fromString('17.12.21', dataType: DataType.date), isNull);
-    });
-    test('datetime', () {
-      expect(fromString('17.12.2035', dataType: DataType.datetime),
-          DateTime(2035, 12, 17));
-      expect(fromString('7.3.1987', dataType: DataType.datetime),
-          DateTime(1987, 3, 7));
-      expect(fromString('2012.6.2', dataType: DataType.datetime),
-          DateTime(2012, 6, 2));
-      expect(fromString('1923.10.31', dataType: DataType.datetime),
-          DateTime(1923, 10, 31));
-      expect(fromString('17.12.21', dataType: DataType.datetime), isNull);
-      expect(fromString('7.3.1987T2:44', dataType: DataType.datetime),
-          DateTime(1987, 3, 7, 2, 44));
-      expect(fromString('17.12.2035 13:59:22', dataType: DataType.datetime),
-          DateTime(2035, 12, 17, 13, 59, 22));
-      expect(fromString('2012.6.2/23:59:59', dataType: DataType.datetime),
-          DateTime(2012, 6, 2, 23, 59, 59));
-      expect(fromString('1923.10.31-3:4', dataType: DataType.datetime),
-          DateTime(1923, 10, 31, 3, 4));
-    });
-  });
-  group('asPattern', () {
-    test('unchanged', () {
-      expect(asPattern('A test!123'), 'A test!123%');
-    });
-    test('with *', () {
-      expect(asPattern('*Wu*ff'), '%Wu%ff%');
-    });
-    test('empty', () {
-      expect(asPattern(''), '%');
-    });
-    test('with ?', () {
-      expect(asPattern('M??r'), 'M__r%');
-    });
-    test('mixed', () {
-      expect(asPattern('*M?i*r'), '%M_i%r%');
-    });
-  });
-  group('asString', () {
-    test('bool', () {
-      expect(asString(true), i18n.tr('Yes'));
-      expect(asString(false), i18n.tr('No'));
-      expect(asString(true, dbFormat: true), 'T');
-      expect(asString(false, dbFormat: true), 'F');
-    });
-    test('int', () {
-      expect(asString(12345), '12345');
-      expect(asString(-33), '-33');
-      expect(asString(-33, dbFormat: true), '-33');
-      expect(asString(884433, dbFormat: true), '884433');
-    });
-    test('double', () {
-      expect(asString(7733.0), '7733.00');
-      expect(asString(-123.77), '-123.77');
-      expect(asString(1E-99), '.00');
-      expect(asString(7733.0, dbFormat: true), '7733.00');
-      expect(asString(-123.77, dbFormat: true), '-123.77');
-    });
-    test('Date', () {
-      expect(asString(DateTime(2021, 10, 3), dateOnly: true), '03.10.2021');
-      expect(
-          asString(DateTime(2021, 10, 3, 9, 33), dateOnly: true), '03.10.2021');
-      expect(asString(DateTime(2021, 10, 3, 9, 33), dateOnly: false),
-          '03.10.2021 09:33');
-      expect(asString(DateTime(2021, 10, 3, 9, 33, 14), dateOnly: false),
-          '03.10.2021 09:33');
-      expect(
-          asString(DateTime(2021, 10, 3, 9, 33, 14),
-              dateOnly: false, withSeconds: true),
-          '03.10.2021 09:33:14');
-      expect(asString(DateTime(2021, 7, 3), dbFormat: true, dateOnly: true),
-          '2021-07-03');
-      expect(
-          asString(DateTime(2021, 10, 3, 9, 33),
-              dbFormat: true, dateOnly: true),
-          '2021-10-03');
-      expect(
-          asString(DateTime(2021, 7, 3, 9, 33),
-              dbFormat: true, dateOnly: false),
-          '2021-07-03 09:33:00');
-      expect(
-          asString(DateTime(2021, 10, 3, 9, 33, 14),
-              dbFormat: true, dateOnly: false),
-          '2021-10-03 09:33:14');
-      expect(
-          asString(DateTime(2021, 10, 3, 9, 33, 14),
-              dbFormat: true, dateOnly: false, withSeconds: true),
-          '2021-10-03 09:33:14');
-    });
-    test('string', () {
-      expect(asString('A test!123'), 'A test!123');
-      expect(asString('A test!123', dbFormat: true), 'A test!123');
-    });
-    test('string', () {
-      expect(asString('A test!123'), 'A test!123');
-    });
-    test('string', () {
-      expect(asString('A test!123'), 'A test!123');
-    });
-  });
-}
diff --git a/metatool/test/validators_test.dart b/metatool/test/validators_test.dart
deleted file mode 100644 (file)
index fa5c35e..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-import 'package:dart_bones/dart_bones.dart';
-import 'package:test/test.dart';
-
-import '../lib/base/validators.dart' as validators;
-import '../lib/base/i18n.dart';
-
-final i18n = I18N();
-
-void main() {
-  final logger = MemoryLogger(LEVEL_FINE);
-  I18N.internal(logger);
-  FileSync.initialize(logger);
-  group('isEmail', () {
-    test('standard', () {
-      expect(validators.isEmail('abc@example.com'), isNull);
-      expect(validators.isEmail(null), isNull);
-    });
-    test('wrong char', () {
-      expect(validators.isEmail('/abc@example.com'), 'Illegal character "/" in email address');
-    });
-    test('wrong syntax', () {
-      expect(validators.isEmail('abc@example'), 'Not an email address: abc@example');
-    });
-  });
-  group('isInt', () {
-    test('OK', () {
-      expect(validators.isInt('123456'), isNull);
-      expect(validators.isInt('-123456'), isNull);
-      expect(validators.isInt(null), isNull);
-    });
-    test('errors', () {
-      expect(validators.isInt('x'), 'Not an integer: x');
-      expect(validators.isInt('123.77'), 'Not an integer: 123.77');
-    });
-  });
-  group('isNat', () {
-    test('OK', () {
-      expect(validators.isNat('123456'), isNull);
-      expect(validators.isNat('0'), isNull);
-      expect(validators.isNat(null), isNull);
-    });
-    test('errors', () {
-      expect(validators.isNat('-123456'), 'Not negative integer expected, not: -123456');
-      expect(validators.isNat('x'), 'Not an integer: x');
-      expect(validators.isNat('123.77'), 'Not an integer: 123.77');
-    });
-  });
-  group('notEmpty', () {
-    test('OK', () {
-      expect(validators.notEmpty('Go'), isNull);
-    });
-    test('error', () {
-      expect(validators.notEmpty(''), 'Please fill in.');
-      expect(validators.notEmpty(null), 'Please fill in.');
-    });
-  });
-  group('validateMultiple', () {
-    test('OK', () {
-      expect(validators.validateMultiple('abc@example.com', [(x) => validators.notEmpty(x),
-        (x) => validators.isEmail(x)]), isNull);
-    });
-    test('Error', () {
-      expect(validators.validateMultiple('0x72z', [(x) => validators.notEmpty(x),
-        (x) => validators.isInt(x)]), 'Not an integer: 0x72z');
-    });
-  });
-}
index 4ff2bfa375530c94bcdfe4bad532a645c9b7ade2..5fbfaa3195d44c026f52cb3e10b77e52c5ba7934 100755 (executable)
@@ -11,9 +11,11 @@ function Usage(){
   echo "+++ $*"
 }
 function MkDirs(){
-    for dir in tools rest_server/data; do
-      mkdir -p $dir
-    done
+  cd $APP
+  for dir in tools rest_server/data; do
+    test -d $dir || mkdir -p $dir
+  done
+  cd ..
 }
 function EnsureDir(){
     local dir=$1
@@ -22,12 +24,13 @@ function EnsureDir(){
       mkdir -p $dir
     fi
 }
+# Copy many files from exhibition.
 function CopyFiles(){
+    cd $APP
     local projDir=$(pwd)
     cd ../exhibition
     local srcDir=$(pwd)    
-    for file in lib/base/*.dart \
-        lib/meta/module_meta_data.dart \
+    for file in lib/meta/module_meta_data.dart \
         lib/meta/modules.dart \
         rest_server/pubspec.yaml \
         tools/PackRestServer \
@@ -36,62 +39,68 @@ function CopyFiles(){
       if [ -z "$DO_UPDATE" -o ! -e $file ]; then
         local dir=$(dirname $file)
         EnsureDir $dir
-        echo "creating $file"
-        cp -a ../exhibition/$file $file
+        #echo "creating $file"
+        cp -av $srcDir/$file $file
       fi
       cd $srcDir
     done
     cd $projDir
+    cd ..
 }
-function CopyFile(){
+# Copy one file.
+function CopyAndReplaceOne(){
   local projDir=$1
   local file=$2
   local srcDir=$3
   cd $projDir
   local dir=$(dirname $file)
   EnsureDir $dir
-  local file2=${file/exhibition/$APP}
-  echo "copy and replace $file -> $(basename $file2)"
-  sed <../exhibition/$file >$file2 -e "s/exhibition/$APP/g" -e "s/Exhibition/$APP2/g"
-  chmod --reference=../exhibition/$file $file2
+  local fileTrg=${file/exhibition/$APP}
+  echo "copy and replace $file -> $(basename $fileTrg)"
+  sed <../exhibition/$file >$fileTrg -e "s/exhibition/$APP/g" -e "s/Exhibition/$APP2/g"
+  chmod --reference=../exhibition/$file $fileTrg
   cd $srcDir
  
 }
+# Copy many files and replaces variables in the file content.
 function CopyAndReplace(){
+    cd $APP
     local projDir=$(pwd)
     cd ../exhibition
     local srcDir=$(pwd)
     local filesNewOnly=$(echo pubspec.yaml \
+      lib/base/*.dart \
+      lib/common/*.dart \
+      lib/meta/*_meta.dart \
       lib/persistence/*.dart \
       lib/page/*.dart \
-      lib/main.dart \
-      lib/meta/*_meta.dart \
-      bin/*.dart \
+      lib/page/benchmarks/*.dart \
+      lib/page/roles/*.dart \
+      lib/page/rolestarter/*.dart \
+      lib/page/starters/*.dart \
+      lib/page/structures/*.dart \
+      lib/page/users/*.dart \
+      lib/services/*.dart \
+      lib/setting/*.dart \
+      lib/widget/*.dart \
+      lib/*.dart
     )
     local filesAlways=$(echo rest_server/lib/*.dart \
       rest_server/bin/*.dart \
       rest_server/tools/project.inc \
       rest_server/CR
     )
-
-    if [ -z "$DO_UPDATE" ]; then
-      for file in lib/setting/*.dart \
-        pubspec.yaml \
-        lib/persistence/*.dart \
-        lib/page/*.dart \
-        rest_server/lib/*.dart rest_server/bin/*.dart rest_server/tools/project.inc \
-        rest_server/CR \
-        lib/main.dart \
-        lib/meta/*_meta.dart \
-        bin/*.dart \
-      ; do
-        CopyFile $projDir $file $srcDir
-      done
-    else
-    fi
+    local files=$filesAlways
+    test -z "$DO_UPDATE" && files="$files $filesNewOnly"
+    for file in $files; do
+      CopyAndReplaceOne $projDir $file $srcDir
+    done
     cd $projDir
+    cd ..
 }
+# Creates symbolic links to the exhibition directory.
 function SymbolicLinks(){
+  cd $APP
   for links in ReCreateMetaTool:. Meta:. dart_tools:. \
       ; do
     local src=../exhibition/${links%:*}
@@ -104,59 +113,92 @@ function SymbolicLinks(){
       ln -s $src $trg2
     fi
   done
-  for links in dart_tools/tools/i18n_text_parser:tools \
+  for link in dart_tools/tools/i18n_text_parser:tools \
       dart_tools/tools/yaml_merger:tools \
       ; do
-    local src=../../exhibition/${links%:*}
-    local trg=${links#*:}
-    local trg2=$trg
-    test $trg = . && trg2=$(basename $src)
-    echo "trg: $trg2"
+    local src=../../exhibition/${link%:*}
+    local trg=${link#*:}
+    local trg2=$trg/$(basename $src)
+    #echo "trg: $trg2"
     if [ ! -L $trg2 ]; then
-      echo "linking $src -> $trg2"
-      ln -s $src $trg2
+      #echo "linking $src -> $trg2"
+      ln -sv $src $trg2
     fi
   done
+  cd ..
 }
+# Executes scripts in exhibition/tools
 function PrepareTools(){
   local tool
-  local curDir=$(pwd)
-  echo "PrepareTools: pwd=$curDir"
-  for tool in i18n_text_parser:CompileI18n \
-      yaml_merger:CompileMerge; do
-    local exe=${tool%:*}
-    local script=${tool#*:}
-    if [ ! -e ../exhibition/dart_tools/tools/$exe ]; then
-      cd ../exhibition/dart_tools
-      echo "executing $script"
-      tools/$script
-    fi
-    cd $curDir
-  done
+  if [ ! -d exhibition ]; then
+    echo "+++ wrong current directory: missing exhibition"
+  else
+    local baseDir=$( pwd)
+    cd exhibition/dart_tools
+    local curDir=$( pwd)
+    for tool in i18n_text_parser:CompileI18n \
+        yaml_merger:CompileMerge; do
+      local exe=${tool%:*}
+      local script=${tool#*:}
+      if [ -e tools/$exe ]; then
+        echo "already exists: $exe"
+      else
+        echo "executing $script"
+        tools/$script
+      fi
+      cd $curDir
+    done
+    cd $baseDir
+  fi
+}
+# Copies and links files of the metatool directory.
+function CreateMetaData(){
+  cd $APP
+  mkdir -p metatool/lib/base metatool/lib/meta
+  cd metatool/lib/base
+  ln -sv ../../../../exhibition/metatool/lib/base/* .
+  rm -f application_name.dart
+  cat <<EOS >application_name.dart
+const theApplicationName = '$APP';
+const theVariantName = '$APP';
+EOS
+  cd ../..
+  ln -vs ../../exhibition/metatool/bin .
+  cp -av ../../exhibition/metatool/lib/meta/* lib/meta
+  ln -vs ../../exhibition/metatool/Compile .
+  ln -vs ../../exhibition/metatool/pubspec.yaml .
+  cp -av ../../exhibition/metatool/local.properties .
+  cp -av ../../exhibition/metatool/metatool.iml .
+  cd ../..
 }
+# Initializes the base directory with flutter
 function Flutter(){
   local app=$1
   flutter create $app
   cd $app
   flutter config linux
+  cd ..
 }
 function Finish(){
+  cd $APP
   dart pub upgrade
+  cd ..
 }
 if [ -z "$APP" ]; then
   Usage "missing PROJECT"
-elif [ -n $DO_UPDATE -a $DO_UPDATE != '--update' ]; then
+elif [ -n "$DO_UPDATE" -a "$DO_UPDATE" != '--update' ]; then
   Usage "Unknown option: $DO_UPDATE"
-elif [ -d $APP ]; then
+elif [ -n "$DO_UPDATE" -a -d $APP ]; then
   Usage "PROJECT already exists."
 elif [ ! -d exhibition/dart_tools ]; then
   Usage "wrong current directory: Please go into the base folder of the project."
 else
-  test -n "$DO_UPDATE" && Flutter $APP
+  test -z "$DO_UPDATE" && Flutter $APP
   PrepareTools
   MkDirs
   CopyFiles
   CopyAndReplace
   SymbolicLinks
+  CreateMetaData
   Finish
 fi
index 3fc61f450213d6ae4084a3838cc5b4d5c14ec2a3..28592e5301cc3c63564512deecc8e33e0b88569a 100755 (executable)
@@ -19,11 +19,12 @@ function DoIt(){
   cat <<EOS >$BASE_DIR/INSTALL.TXT
 # ------------
 # Installation:
-./InstallRestServer
+cd /tmp
+sudo ./InstallRestServer
 # or manually:
 DIR=\$(pwd)
 mkdir -p /usr/share/$PROJECT && cd /usr/share/$PROJECT
-tar xzf \DIR/$TAR_NODE
+tar xzf \$DIR/$TAR_NODE
 # ./$EXE install <executable> <service-name>
 ./$EXE install /usr/share/$PROJECT/$EXE $PROJECT
 # see /usr/share/$PROJECT and /etc/$PROJECT