diff --git a/Admin/Install/Media.install.json b/Admin/Install/Media.install.json
index d9d8638..5f3875f 100755
--- a/Admin/Install/Media.install.json
+++ b/Admin/Install/Media.install.json
@@ -13,47 +13,164 @@
"virtualPath": "/Modules/Admin",
"user": 1
},
+ {
+ "type": "collection",
+ "create_directory": true,
+ "name": "Global",
+ "virtualPath": "/Modules/Admin/Templates",
+ "user": 1
+ },
+ {
+ "type": "collection",
+ "create_directory": true,
+ "name": "Helper",
+ "virtualPath": "/Modules/Admin/Templates/Global",
+ "user": 1
+ },
+ {
+ "type": "collection",
+ "create_directory": true,
+ "name": "Lists",
+ "virtualPath": "/Modules/Admin/Templates/Global",
+ "user": 1
+ },
+ {
+ "type": "collection",
+ "create_directory": true,
+ "name": "Letters",
+ "virtualPath": "/Modules/Admin/Templates/Global",
+ "user": 1
+ },
+ {
+ "type": "collection",
+ "create_directory": true,
+ "name": "Emails",
+ "virtualPath": "/Modules/Admin/Templates/Global",
+ "user": 1
+ },
+ {
+ "type": "collection",
+ "create_directory": true,
+ "name": "Data",
+ "virtualPath": "/Modules/Admin/Templates/Global",
+ "user": 1
+ },
+ {
+ "type": "collection",
+ "create_directory": true,
+ "name": "Reports",
+ "virtualPath": "/Modules/Admin/Templates/Global",
+ "user": 1
+ },
{
"type": "upload",
"create_collection": true,
- "name": "Pdf Exporter",
- "virtualPath": "/Modules/Admin/Templates",
- "path": "/Modules/Admin/Templates/Pdf Exporter",
+ "name": "Assets",
+ "virtualPath": "/Modules/Admin/Templates/Global/Helper",
+ "path": "/Modules/Admin/Templates/Global/Helper/Assets",
"files": [
- "/Modules/Admin/Admin/Install/Media/PdfExporter"
+ "/Modules/Admin/Admin/Install/Media/Assets"
],
"user": 1
},
{
"type": "upload",
"create_collection": true,
- "name": "Excel Exporter",
- "virtualPath": "/Modules/Admin/Templates",
- "path": "/Modules/Admin/Templates/Excel Exporter",
+ "name": "Pdf Default Template",
+ "virtualPath": "/Modules/Admin/Templates/Global/Helper",
+ "path": "/Modules/Admin/Templates/Global/Helper/Pdf Default Template",
"files": [
- "/Modules/Admin/Admin/Install/Media/ExcelExporter"
+ "/Modules/Admin/Admin/Install/Media/PdfDefaultTemplate"
],
"user": 1
},
{
"type": "upload",
"create_collection": true,
- "name": "Csv Exporter",
- "virtualPath": "/Modules/Admin/Templates",
- "path": "/Modules/Admin/Templates/Csv Exporter",
+ "name": "Word Default Template",
+ "virtualPath": "/Modules/Admin/Templates/Global/Helper",
+ "path": "/Modules/Admin/Templates/Global/Helper/Word Default Template",
"files": [
- "/Modules/Admin/Admin/Install/Media/CsvExporter"
+ "/Modules/Admin/Admin/Install/Media/WordDefaultTemplate"
],
"user": 1
},
{
"type": "upload",
"create_collection": true,
- "name": "Word Exporter",
- "virtualPath": "/Modules/Admin/Templates",
- "path": "/Modules/Admin/Templates/Word Exporter",
+ "name": "Word Plain Template",
+ "virtualPath": "/Modules/Admin/Templates/Global/Helper",
+ "path": "/Modules/Admin/Templates/Global/Helper/Word Plain Template",
"files": [
- "/Modules/Admin/Admin/Install/Media/WordExporter"
+ "/Modules/Admin/Admin/Install/Media/WordPlainTemplate"
+ ],
+ "user": 1
+ },
+ {
+ "type": "upload",
+ "create_collection": true,
+ "name": "Excel Default Template",
+ "virtualPath": "/Modules/Admin/Templates/Global/Helper",
+ "path": "/Modules/Admin/Templates/Global/Helper/Excel Default Template",
+ "files": [
+ "/Modules/Admin/Admin/Install/Media/ExcelDefaultTemplate"
+ ],
+ "user": 1
+ },
+
+ {
+ "type": "upload",
+ "create_collection": true,
+ "name": "Pdf List Exporter",
+ "virtualPath": "/Modules/Admin/Templates/Global/Lists",
+ "path": "/Modules/Admin/Templates/Global/Lists/Pdf List Exporter",
+ "files": [
+ "/Modules/Admin/Admin/Install/Media/PdfListExporter"
+ ],
+ "user": 1
+ },
+ {
+ "type": "upload",
+ "create_collection": true,
+ "name": "Word List Exporter",
+ "virtualPath": "/Modules/Admin/Templates/Global/Lists",
+ "path": "/Modules/Admin/Templates/Global/Lists/Word List Exporter",
+ "files": [
+ "/Modules/Admin/Admin/Install/Media/WordListExporter"
+ ],
+ "user": 1
+ },
+ {
+ "type": "upload",
+ "create_collection": true,
+ "name": "Excel List Exporter",
+ "virtualPath": "/Modules/Admin/Templates/Global/Lists",
+ "path": "/Modules/Admin/Templates/Global/Lists/Excel List Exporter",
+ "files": [
+ "/Modules/Admin/Admin/Install/Media/ExcelListExporter"
+ ],
+ "user": 1
+ },
+ {
+ "type": "upload",
+ "create_collection": true,
+ "name": "Csv List Exporter",
+ "virtualPath": "/Modules/Admin/Templates/Global/Lists",
+ "path": "/Modules/Admin/Templates/Global/Lists/Csv List Exporter",
+ "files": [
+ "/Modules/Admin/Admin/Install/Media/CsvListExporter"
+ ],
+ "user": 1
+ },
+
+ {
+ "type": "upload",
+ "create_collection": true,
+ "name": "Word List Exporter",
+ "virtualPath": "/Modules/Admin/Templates/Global/Letters",
+ "path": "/Modules/Admin/Templates/Global/Letters/Word Letter Exporter",
+ "files": [
+ "/Modules/Admin/Admin/Install/Media/WordLetterExporter"
],
"user": 1
},
@@ -61,11 +178,71 @@
"type": "upload",
"create_collection": true,
"name": "Email Exporter",
- "virtualPath": "/Modules/Admin/Templates",
- "path": "/Modules/Admin/Templates/Email Exporter",
+ "virtualPath": "/Modules/Admin/Templates/Global/Emails",
+ "path": "/Modules/Admin/Templates/Global/Emails/Email Exporter",
"files": [
"/Modules/Admin/Admin/Install/Media/EmailExporter"
],
"user": 1
+ },
+
+ {
+ "type": "reference",
+ "name": "Assets",
+ "from": "/Modules/Admin/Templates/Global/Helper/Pdf Default Template",
+ "to": "/Modules/Admin/Templates/Global/Helper/Assets",
+ "user": 1
+ },
+ {
+ "type": "reference",
+ "name": "Assets",
+ "from": "/Modules/Admin/Templates/Global/Helper/Word Default Template",
+ "to": "/Modules/Admin/Templates/Global/Helper/Assets",
+ "user": 1
+ },
+ {
+ "type": "reference",
+ "name": "Assets",
+ "from": "/Modules/Admin/Templates/Global/Helper/Word Plain Template",
+ "to": "/Modules/Admin/Templates/Global/Helper/Assets",
+ "user": 1
+ },
+
+ {
+ "type": "reference",
+ "name": "Assets",
+ "from": "/Modules/Admin/Templates/Global/Helper/Excel Default Template",
+ "to": "/Modules/Admin/Templates/Global/Helper/Assets",
+ "user": 1
+ },
+
+ {
+ "type": "reference",
+ "name": "Helper",
+ "from": "/Modules/Admin/Templates/Global/Lists/Pdf List Exporter",
+ "to": "/Modules/Admin/Templates/Global/Helper/Pdf Default Template",
+ "user": 1
+ },
+ {
+ "type": "reference",
+ "name": "Helper",
+ "from": "/Modules/Admin/Templates/Global/Lists/Word List Exporter",
+ "to": "/Modules/Admin/Templates/Global/Helper/Word Default Template",
+ "user": 1
+ },
+ {
+ "type": "reference",
+ "name": "Helper",
+ "from": "/Modules/Admin/Templates/Global/Lists/Excel List Exporter",
+ "to": "/Modules/Admin/Templates/Global/Helper/Excel Default Template",
+ "user": 1
+ },
+
+ {
+ "type": "reference",
+ "name": "Helper",
+ "from": "/Modules/Admin/Templates/Global/Letters/Word Letter Exporter",
+ "to": "/Modules/Admin/Templates/Global/Helper/Word Default Template",
+ "user": 1
}
]
\ No newline at end of file
diff --git a/Admin/Install/Media.php b/Admin/Install/Media.php
index 064a5b6..464f745 100755
--- a/Admin/Install/Media.php
+++ b/Admin/Install/Media.php
@@ -43,26 +43,26 @@ class Media
{
$media = \Modules\Media\Admin\Installer::installExternal($app, ['path' => __DIR__ . '/Media.install.json']);
- $defaultPdfExport = (int) \reset($media['upload'][0]);
- $defaultExcelExport = (int) \reset($media['upload'][1]);
- $defaultCsvExport = (int) \reset($media['upload'][2]);
- $defaultWordExport = (int) \reset($media['upload'][3]);
- $defaultEmailExport = (int) \reset($media['upload'][4]);
+ SettingMapper::create()->execute(
+ new Setting(
+ 0,
+ SettingsEnum::DEFAULT_LIST_EXPORTS,
+ (string) $media['collection'][4]['id'],
+ '\\d+',
+ unit: 1,
+ module: 'Admin'
+ )
+ );
SettingMapper::create()->execute(
- new Setting(0, SettingsEnum::DEFAULT_PDF_EXPORT_TEMPLATE, (string) $defaultPdfExport, '\\d+', 1, 'Admin')
- );
- SettingMapper::create()->execute(
- new Setting(0, SettingsEnum::DEFAULT_EXCEL_EXPORT_TEMPLATE, (string) $defaultExcelExport, '\\d+', 1, 'Admin')
- );
- SettingMapper::create()->execute(
- new Setting(0, SettingsEnum::DEFAULT_CSV_EXPORT_TEMPLATE, (string) $defaultCsvExport, '\\d+', 1, 'Admin')
- );
- SettingMapper::create()->execute(
- new Setting(0, SettingsEnum::DEFAULT_WORD_EXPORT_TEMPLATE, (string) $defaultWordExport, '\\d+', 1, 'Admin')
- );
- SettingMapper::create()->execute(
- new Setting(0, SettingsEnum::DEFAULT_EMAIL_EXPORT_TEMPLATE, (string) $defaultEmailExport, '\\d+', 1, 'Admin')
+ new Setting(
+ 0,
+ SettingsEnum::DEFAULT_LETTERS,
+ (string) $media['collection'][5]['id'],
+ '\\d+',
+ unit: 1,
+ module: 'Admin'
+ )
);
}
}
diff --git a/Admin/Install/Media/CsvExporter/defaultCsvExporter.csv.php b/Admin/Install/Media/CsvListExporter/defaultCsvExporter.csv.php
similarity index 59%
rename from Admin/Install/Media/CsvExporter/defaultCsvExporter.csv.php
rename to Admin/Install/Media/CsvListExporter/defaultCsvExporter.csv.php
index 0da7105..2be87ca 100755
--- a/Admin/Install/Media/CsvExporter/defaultCsvExporter.csv.php
+++ b/Admin/Install/Media/CsvListExporter/defaultCsvExporter.csv.php
@@ -12,4 +12,12 @@
*/
declare(strict_types=1);
-$exporter = null;
+$data = $this->getData('data') ?? [];
+
+$out = \fopen('php://output', 'w');
+
+foreach ($data as $row) {
+ fputcsv($out, $row);
+}
+
+\fclose($out);
diff --git a/Admin/Install/Media/ExcelDefaultTemplate/template.php b/Admin/Install/Media/ExcelDefaultTemplate/template.php
new file mode 100644
index 0000000..1479aa2
--- /dev/null
+++ b/Admin/Install/Media/ExcelDefaultTemplate/template.php
@@ -0,0 +1,46 @@
+getActiveSheet()
+ ->getPageSetup()
+ ->setPaperSize(PageSetup::PAPERSIZE_A4);
+
+ $this->getActiveSheet()
+ ->getHeaderFooter()
+ ->setOddHeader("&L&B&20Jingga\n&B&10Business solutions made simple.");
+
+ $this->getActiveSheet()
+ ->getHeaderFooter()
+ ->setOddFooter('&RPage &P/&N');
+
+ /*
+ Tested with LibreOffice and not working (requires &G above in the text).
+ Either it is broken or LibreOffice cannot show the image.
+ $drawing = new HeaderFooterDrawing();
+ $drawing->setName('PhpSpreadsheet logo');
+ $drawing->setPath(__DIR__ . '/../Web/Backend/img/logo.png');
+ $drawing->setHeight(50);
+
+ $this->getActiveSheet()
+ ->getHeaderFooter()
+ ->addImage($drawing, HeaderFooter::IMAGE_HEADER_LEFT);
+ */
+ }
+
+}
diff --git a/Admin/Install/Media/ExcelExporter/defaultExcelExporter.xls.php b/Admin/Install/Media/ExcelExporter/defaultExcelExporter.xls.php
deleted file mode 100755
index 0da7105..0000000
--- a/Admin/Install/Media/ExcelExporter/defaultExcelExporter.xls.php
+++ /dev/null
@@ -1,15 +0,0 @@
-getData('media');
+$data = $this->getData('data') ?? [];
+
+include $media->getSourceByName('template.php')->getAbsolutePath();
+
+$excel = new DefaultExcel();
+
+foreach ($data as $i => $row) {
+ foreach ($row as $j => $cell) {
+ $excel->getActiveSheet()->setCellValueByColumnAndRow($j + 1, $i + 1, $cell);
+ }
+}
+
+$file = \tempnam(\sys_get_temp_dir(), 'oms_');
+
+$writer = IOFactory::createWriter($excel, 'Xlsx');
+$writer->save($file);
+
+echo \file_get_contents($file);
+
+\unlink($file);
\ No newline at end of file
diff --git a/Admin/Install/Media/PdfDefaultTemplate/template.php b/Admin/Install/Media/PdfDefaultTemplate/template.php
new file mode 100644
index 0000000..2fc0ef7
--- /dev/null
+++ b/Admin/Install/Media/PdfDefaultTemplate/template.php
@@ -0,0 +1,137 @@
+header_xobjid === false) {
+ $this->header_xobjid = $this->startTemplate($this->w, 0);
+
+ // Set Logo
+ $image_file = __DIR__ . '/../Web/Backend/img/logo.png';
+ $this->Image($image_file, 15, 15, 15, 15, 'PNG', '', 'T', false, 300, '', false, false, 0, false, false, false);
+
+ // Set Title
+ $this->SetFont('helvetica', 'B', 20);
+ $this->setX(15 + 15 + 3);
+ $this->Cell(0, 14, $this->header_title, 0, false, 'L', 0, '', 0, false, 'T', 'M');
+
+ $this->SetFont('helvetica', '', 10);
+ $this->setX(15 + 15 + 3);
+ $this->Cell(0, 26, $this->header_string, 0, false, 'L', 0, '', 0, false, 'T', 'M');
+
+ $this->endTemplate();
+ }
+
+ $x = 0;
+ $dx = 0;
+
+ if (!$this->header_xobj_autoreset AND $this->booklet AND (($this->page % 2) == 0)) {
+ // adjust margins for booklet mode
+ $dx = ($this->original_lMargin - $this->original_rMargin);
+ }
+
+ if ($this->rtl) {
+ $x = $this->w + $dx;
+ } else {
+ $x = 0 + $dx;
+ }
+
+ $this->printTemplate($this->header_xobjid, $x, 0, 0, 0, '', '', false);
+ if ($this->header_xobj_autoreset) {
+ // reset header xobject template at each page
+ $this->header_xobjid = false;
+ }
+ }
+
+ // Page footer
+ public function Footer() {
+ $this->SetY(-25);
+
+ $this->SetFont('helvetica', 'I', 7);
+ $this->Cell($this->getPageWidth() - 22, 0, 'Page '.$this->getAliasNumPage().'/'.$this->getAliasNbPages(), 0, false, 'R', 0, '', 0, false, 'T', 'M');
+ $this->Ln();
+ $this->Ln();
+
+ $this->SetFillColor(245, 245, 245);
+ $this->SetX(0);
+ $this->Cell($this->getPageWidth(), 25, '', 0, 0, 'L', true, '', 0, false, 'T', 'T');
+
+ $this->SetFont('helvetica', '', 7);
+ $this->SetXY(15 + 10, -15, true);
+ $this->MultiCell(30, 0, "Jingga e.K.\nGartenstr. 26\n61206 Woellstadt", 0, 'L', false, 1, null, null, true, 0, false, true, 0, 'B');
+
+ $this->SetXY(25 + 15 + 20, -15, true);
+ $this->MultiCell(40, 0, "Geschäftsführer: Dennis Eichhorn\nFinanzamt: HRB ???\nUSt Id: DE ??????????", 0, 'L', false, 1, null, null, true, 0, false, true, 0, 'B');
+
+ $this->SetXY(25 + 45 + 15 + 30, -15, true);
+ $this->MultiCell(35, 0, "Volksbank Mittelhessen\nBIC: ??????????\nIBAN: ???????????", 0, 'L', false, 1, null, null, true, 0, false, true, 0, 'B');
+
+ $this->SetXY(25 + 45 + 35 + 15 + 40, -15, true);
+ $this->MultiCell(35, 0, "www.jingga.app\ninfo@jingga.app\n+49 0152 ???????", 0, 'L', false, 1, null, null, true, 0, false, true, 0, 'B');
+ }
+
+ public function __construct()
+ {
+ parent::__construct('P', 'mm', 'A4', true, 'UTF-8', false);
+
+ $this->SetCreator("Jingga");
+
+ // set default header data
+ $this->SetHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, 'Jingga', 'Business solutions made simple.');
+
+ // set header and footer fonts
+ $this->SetHeaderFont([PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN]);
+ $this->SetFooterFont([PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA]);
+
+ // set default monospaced font
+ $this->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
+
+ // set margins
+ $this->SetMargins(15, 30, 15);
+
+ // set auto page breaks
+ $this->SetAutoPageBreak(true, 25);
+
+ // set image scale factor
+ $this->SetImageScale(PDF_IMAGE_SCALE_RATIO);
+
+ // add a page
+ $this->AddPage();
+ }
+}
+
+/*
+[
+ 'company' => '',
+ 'slogan' => '',
+ 'company_full' => '',
+ 'address' => '',
+ 'ciry' => '',
+ 'manager' => '',
+ 'tax_office' => '',
+ 'tax_id' => '',
+ 'tax_vat' => '',
+ 'bank_name' => '',
+ 'bank_bic' => '',
+ 'bank_iban' => '',
+ 'website' => '',
+ 'email' => '',
+ 'phone' => '',
+ 'creator' => '',
+ 'date' => '',
+]
+*/
diff --git a/Admin/Install/Media/PdfExporter/defaultPdfExporter.pdf.php b/Admin/Install/Media/PdfExporter/defaultPdfExporter.pdf.php
deleted file mode 100755
index 0da7105..0000000
--- a/Admin/Install/Media/PdfExporter/defaultPdfExporter.pdf.php
+++ /dev/null
@@ -1,15 +0,0 @@
-getData('media');
+$data = $this->getData('data') ?? [];
+
+include $media->getSourceByName('template.php')->getAbsolutePath();
+
+$excel = new DefaultPdf();
+
+$topPos = $pdf->getY();
+
+$tbl = '
';
+foreach ($data as $i => $row) {
+ if ($i === 0) {
+ $tbl = '';
+
+ foreach ($row as $j => $cell) {
+ $tbl .= '| ' . $cell . ' | ';
+ }
+
+ $tbl .= '
';
+ } else {
+ $tbl .= '';
+ foreach ($row as $j => $cell) {
+ $tbl .= '| ' . $cell . ' | ';
+ }
+ $tbl .= '
';
+ }
+}
+$tbl .= '
';
+
+$pdf->Output('list.pdf', 'I');
\ No newline at end of file
diff --git a/Admin/Install/Media/WordDefaultTemplate/template.php b/Admin/Install/Media/WordDefaultTemplate/template.php
new file mode 100644
index 0000000..51e3a14
--- /dev/null
+++ b/Admin/Install/Media/WordDefaultTemplate/template.php
@@ -0,0 +1,186 @@
+ 100, 'bgColor' => 'f5f5f5'];
+ $this->addTableStyle('FooterTableStyle', $generalTableStyle);
+ }
+
+ public function createFirstPage()
+ {
+ $section = $this->addSection([
+ 'marginLeft' => 1000,
+ 'marginRight' => 1000,
+ 'marginTop' => 2000,
+ 'marginBottom' => 2000,
+ // 'headerHeight' => 50,
+ // 'footerHeight' => 50,
+ ]);
+
+ // first page header
+ $firstHeader = $section->addHeader();
+ $firstHeader->firstPage();
+
+ $table = $firstHeader->addTable();
+ $table->addRow();
+
+ // first column
+ $table->addCell(1300)->addImage(__DIR__ . '/../Web/Backend/img/logo.png', ['width' => 50, 'height' => 50]);
+
+ //second column
+ $cell = $table->addCell(8700, ['valign' => 'bottom']);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Jingga', ['name' => 'helvetica', 'bold' => true, 'size' => 20]);
+
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Business solutions made simple.', ['name' => 'helvetica', 'size' => 10]);
+
+ // first page footer
+ $firstFooter = $section->addFooter();
+ $firstFooter->firstPage();
+ $firstFooter->addPreserveText('Page {PAGE}/{NUMPAGES}', ['name' => 'helvetica', 'italic' => true], ['alignment' => \PhpOffice\PhpWord\SimpleType\Jc::END]);
+ $firstFooter->addTextRun();
+
+ $table = $firstFooter->addTable('FooterTableStyle');
+ $table->addRow();
+
+ // columns
+ $cell = $table->addCell(500);
+
+ $cell = $table->addCell(2000);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Jingga e.K.', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Gartenstr. 26', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('61206 Woellstadt', ['name' => 'helvetica', 'size' => 7]);
+
+ $cell = $table->addCell(2700);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Geschäftsführer: Dennis Eichhorn', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Finanzamt: HRB ???', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('USt Id: DE ??????????', ['name' => 'helvetica', 'size' => 7]);
+
+ $cell = $table->addCell(2700);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Volksbank Mittelhessen', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('BIC: ??????????', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('IBAN: ???????????', ['name' => 'helvetica', 'size' => 7]);
+
+ $cell = $table->addCell(2100);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('www.jingga.app', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('info@jingga.app', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('+49 0152 ???????', ['name' => 'helvetica', 'size' => 7]);
+
+ return $section;
+ }
+
+ public function createSecondPage()
+ {
+ $section = $this->addSection([
+ 'marginLeft' => 1000,
+ 'marginRight' => 1000,
+ 'marginTop' => 2000,
+ 'marginBottom' => 2000,
+ // 'headerHeight' => 50,
+ // 'footerHeight' => 50,
+ ]);
+
+ $header = $section->addHeader();
+ $table = $header->addTable();
+ $table->addRow();
+
+ // first column
+ $table->addCell(1300)->addImage(__DIR__ . '/../Web/Backend/img/logo.png', ['width' => 50, 'height' => 50]);
+
+ //second column
+ $cell = $table->addCell(8700, ['valign' => 'bottom']);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Jingga', ['name' => 'helvetica', 'bold' => true, 'size' => 20]);
+
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Business solutions made simple.', ['name' => 'helvetica', 'size' => 10]);
+
+ $footer = $section->addFooter();
+ $footer->addPreserveText('Page {PAGE}/{NUMPAGES}', ['name' => 'helvetica', 'italic' => true], ['alignment' => \PhpOffice\PhpWord\SimpleType\Jc::END]);
+ $footer->addTextRun();
+
+ $table = $footer->addTable('FooterTableStyle');
+ $table->addRow();
+
+ // columns
+ $cell = $table->addCell(500);
+
+ $cell = $table->addCell(2000);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Jingga e.K.', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Gartenstr. 26', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('61206 Woellstadt', ['name' => 'helvetica', 'size' => 7]);
+
+ $cell = $table->addCell(2700);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Geschäftsführer: Dennis Eichhorn', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Finanzamt: HRB ???', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('USt Id: DE ??????????', ['name' => 'helvetica', 'size' => 7]);
+
+ $cell = $table->addCell(2700);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('Volksbank Mittelhessen', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('BIC: ??????????', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('IBAN: ???????????', ['name' => 'helvetica', 'size' => 7]);
+
+ $cell = $table->addCell(2100);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('www.jingga.app', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('info@jingga.app', ['name' => 'helvetica', 'size' => 7]);
+ $textrun = $cell->addTextRun();
+ $textrun->addText('+49 0152 ???????', ['name' => 'helvetica', 'size' => 7]);
+
+ return $section;
+ }
+}
+
+/*
+[
+ 'company' => '',
+ 'slogan' => '',
+ 'company_full' => '',
+ 'address' => '',
+ 'ciry' => '',
+ 'manager' => '',
+ 'tax_office' => '',
+ 'tax_id' => '',
+ 'tax_vat' => '',
+ 'bank_name' => '',
+ 'bank_bic' => '',
+ 'bank_iban' => '',
+ 'website' => '',
+ 'email' => '',
+ 'phone' => '',
+ 'creator' => '',
+ 'date' => '',
+]
+*/
diff --git a/Admin/Install/Media/WordExporter/defaultWordExporter.doc.php b/Admin/Install/Media/WordExporter/defaultWordExporter.doc.php
deleted file mode 100755
index 0da7105..0000000
--- a/Admin/Install/Media/WordExporter/defaultWordExporter.doc.php
+++ /dev/null
@@ -1,15 +0,0 @@
-getData('media');
+$data = $this->getData('data') ?? [];
+
+include $media->getSourceByName('template.php')->getAbsolutePath();
+
+$word = new DefaultWord();
+$section = $word->createFirstPage();
+
+$file = \tempnam(\sys_get_temp_dir(), 'oms_');
+$writer->save($file);
+
+echo \file_get_contents($file);
+
+\unlink($file);
\ No newline at end of file
diff --git a/Admin/Install/Media/WordListExporter/defaultWordListExporter.doc.php b/Admin/Install/Media/WordListExporter/defaultWordListExporter.doc.php
new file mode 100755
index 0000000..e2ece94
--- /dev/null
+++ b/Admin/Install/Media/WordListExporter/defaultWordListExporter.doc.php
@@ -0,0 +1,55 @@
+getData('media');
+$data = $this->getData('data') ?? [];
+
+include $media->getSourceByName('template.php')->getAbsolutePath();
+
+$word = new DefaultWord();
+$section = $word->createFirstPage();
+
+$tbl = '';
+foreach ($data as $i => $row) {
+ if ($i === 0) {
+ $tbl = '';
+
+ foreach ($row as $j => $cell) {
+ $tbl .= '| ' . $cell . ' | ';
+ }
+
+ $tbl .= '
';
+ } else {
+ $tbl .= '';
+ foreach ($row as $j => $cell) {
+ $tbl .= '| ' . $cell . ' | ';
+ }
+ $tbl .= '
';
+ }
+}
+$tbl .= '
';
+
+\PhpOffice\PhpWord\Shared\Html::addHtml($section, $tbl, false, false);
+
+$file = \tempnam(\sys_get_temp_dir(), 'oms_');
+$writer->save($file);
+
+echo \file_get_contents($file);
+
+\unlink($file);
\ No newline at end of file
diff --git a/Admin/Install/Media/WordPlainTemplate/defaultWordExporter.doc.php b/Admin/Install/Media/WordPlainTemplate/defaultWordExporter.doc.php
new file mode 100644
index 0000000..c79b819
--- /dev/null
+++ b/Admin/Install/Media/WordPlainTemplate/defaultWordExporter.doc.php
@@ -0,0 +1,33 @@
+getData('media');
+$data = $this->getData('data') ?? [];
+
+include $media->getSourceByName('template.php')->getAbsolutePath();
+
+$word = new DefaultWord();
+$section = $word->createFirstPage();
+
+$file = \tempnam(\sys_get_temp_dir(), 'oms_');
+$writer->save($file);
+
+echo \file_get_contents($file);
+
+\unlink($file);
\ No newline at end of file
diff --git a/Admin/Install/db.json b/Admin/Install/db.json
index ceb61b2..986774d 100755
--- a/Admin/Install/db.json
+++ b/Admin/Install/db.json
@@ -507,6 +507,56 @@
}
}
},
+ "unit": {
+ "name": "unit",
+ "fields": {
+ "unit_id": {
+ "name": "unit_id",
+ "type": "INT",
+ "null": false,
+ "primary": true,
+ "autoincrement": true
+ },
+ "unit_name": {
+ "name": "unit_name",
+ "type": "VARCHAR(50)",
+ "default": null,
+ "null": true
+ },
+ "unit_image": {
+ "name": "unit_image",
+ "type": "INT",
+ "default": null,
+ "null": true
+ },
+ "unit_description": {
+ "name": "unit_description",
+ "type": "TEXT",
+ "default": null,
+ "null": true
+ },
+ "unit_descriptionraw": {
+ "name": "unit_descriptionraw",
+ "type": "TEXT",
+ "default": null,
+ "null": true
+ },
+ "unit_parent": {
+ "name": "unit_parent",
+ "type": "INT",
+ "default": null,
+ "null": true,
+ "foreignTable": "unit",
+ "foreignKey": "unit_id"
+ },
+ "unit_status": {
+ "name": "unit_status",
+ "type": "TINYINT",
+ "default": null,
+ "null": true
+ }
+ }
+ },
"app": {
"name": "app",
"fields": {
@@ -553,7 +603,8 @@
"group_name": {
"name": "group_name",
"type": "VARCHAR(50)",
- "null": false
+ "null": false,
+ "unique": true
},
"group_status": {
"name": "group_status",
@@ -601,9 +652,12 @@
"name": "group_permission_unit",
"type": "INT",
"default": null,
- "null": true
+ "null": true,
+ "foreignTable": "unit",
+ "foreignKey": "unit_id"
},
"group_permission_app": {
+ "description": "@todo: consider to use int as value and create foreign key",
"name": "group_permission_app",
"type": "VARCHAR(255)",
"default": null,
@@ -681,6 +735,11 @@
"primary": true,
"autoincrement": true
},
+ "account_id_temp": {
+ "name": "account_id_temp",
+ "type": "VARCHAR(65)",
+ "null": false
+ },
"account_status": {
"name": "account_status",
"type": "TINYINT",
@@ -748,6 +807,14 @@
"gdpr": true
}
},
+ "account_email_temp": {
+ "name": "account_email_temp",
+ "type": "VARCHAR(70)",
+ "null": false,
+ "annotations": {
+ "gdpr": true
+ }
+ },
"account_tries": {
"name": "account_tries",
"type": "TINYINT",
@@ -775,6 +842,87 @@
}
}
},
+ "account_address_rel": {
+ "name": "account_address_rel",
+ "fields": {
+ "account_address_rel_id": {
+ "name": "account_address_rel_id",
+ "type": "INT",
+ "null": false,
+ "primary": true,
+ "autoincrement": true
+ },
+ "account_address_rel_contact": {
+ "name": "account_address_rel_contact",
+ "type": "INT",
+ "null": false,
+ "foreignTable": "profile_contact",
+ "foreignKey": "profile_contact_id"
+ },
+ "account_address_rel_module": {
+ "name": "account_address_rel_module",
+ "type": "INT",
+ "null": true,
+ "default": null,
+ "foreignTable": "module",
+ "foreignKey": "module_id"
+ },
+ "account_address_rel_address": {
+ "name": "account_address_rel_address",
+ "type": "INT",
+ "null": false,
+ "foreignTable": "address",
+ "foreignKey": "address_id"
+ }
+ }
+ },
+ "account_contact": {
+ "name": "account_contact",
+ "fields": {
+ "account_contact_id": {
+ "name": "account_contact_id",
+ "type": "INT",
+ "null": false,
+ "primary": true,
+ "autoincrement": true
+ },
+ "account_contact_type": {
+ "name": "account_contact_type",
+ "type": "TINYINT",
+ "null": false
+ },
+ "account_contact_subtype": {
+ "name": "account_contact_subtype",
+ "type": "TINYINT",
+ "null": false
+ },
+ "account_contact_order": {
+ "name": "account_contact_order",
+ "type": "INT",
+ "null": false
+ },
+ "account_contact_content": {
+ "name": "account_contact_content",
+ "type": "VARCHAR(255)",
+ "null": false
+ },
+ "account_contact_module": {
+ "name": "account_contact_module",
+ "type": "INT",
+ "null": true,
+ "default": null,
+ "foreignTable": "module",
+ "foreignKey": "module_id"
+ },
+ "account_contact_account": {
+ "name": "account_contact_account",
+ "type": "INT",
+ "null": false,
+ "foreignTable": "account",
+ "foreignKey": "account_id"
+ }
+ }
+ },
"account_account_rel": {
"description": "Accounts can belong to other accounts. E.g. a user can belong to a company account",
"name": "account_account_rel",
@@ -919,6 +1067,41 @@
}
}
},
+ "account_api": {
+ "name": "account_api",
+ "fields": {
+ "account_api_id": {
+ "name": "account_api_id",
+ "type": "INT",
+ "null": false,
+ "primary": true,
+ "autoincrement": true
+ },
+ "account_api_status": {
+ "name": "account_api_status",
+ "type": "TINYINT",
+ "null": false
+ },
+ "account_api_key": {
+ "name": "account_api_key",
+ "type": "VARCHAR(129)",
+ "null": false,
+ "unique": true
+ },
+ "account_api_created_at": {
+ "name": "account_api_created_at",
+ "type": "DATETIME",
+ "null": false
+ },
+ "account_api_account": {
+ "name": "account_api_account",
+ "type": "INT",
+ "null": false,
+ "foreignTable": "account",
+ "foreignKey": "account_id"
+ }
+ }
+ },
"settings": {
"name": "settings",
"fields": {
@@ -944,6 +1127,14 @@
"type": "TEXT",
"null": true
},
+ "settings_unit": {
+ "name": "settings_unit",
+ "type": "INT",
+ "default": null,
+ "null": true,
+ "foreignTable": "unit",
+ "foreignKey": "unit_id"
+ },
"settings_app": {
"name": "settings_app",
"type": "INT",
@@ -977,5 +1168,46 @@
"foreignKey": "account_id"
}
}
+ },
+ "data_change": {
+ "name": "data_change",
+ "fields": {
+ "data_change_id": {
+ "name": "data_change_id",
+ "type": "INT",
+ "null": false,
+ "primary": true,
+ "autoincrement": true
+ },
+ "data_change_type": {
+ "name": "data_change_type",
+ "type": "VARCHAR(65)",
+ "null": false,
+ "unique": true
+ },
+ "data_change_hash": {
+ "name": "data_change_hash",
+ "type": "VARCHAR(65)",
+ "null": false,
+ "unique": true
+ },
+ "data_change_data": {
+ "name": "data_change_data",
+ "type": "VARCHAR(255)",
+ "null": false
+ },
+ "data_change_created_by": {
+ "name": "data_change_created_by",
+ "type": "INT",
+ "null": false,
+ "foreignTable": "account",
+ "foreignKey": "account_id"
+ },
+ "data_change_created_at": {
+ "name": "data_change_created_at",
+ "type": "DATETIME",
+ "null": false
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Admin/Installer.php b/Admin/Installer.php
index a5d85c6..91ec15d 100755
--- a/Admin/Installer.php
+++ b/Admin/Installer.php
@@ -78,7 +78,7 @@ final class Installer extends InstallerAbstract
**/
private static function installDefaultSettings() : void
{
- SettingMapper::create()->execute(new Setting(0, SettingsEnum::PASSWORD_PATTERN, '', module: 'Admin'));
+ SettingMapper::create()->execute(new Setting(0, SettingsEnum::PASSWORD_PATTERN, '^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$', module: 'Admin'));
SettingMapper::create()->execute(new Setting(0, SettingsEnum::LOGIN_TRIES, '3', '\\d+', module: 'Admin'));
SettingMapper::create()->execute(new Setting(0, SettingsEnum::LOGIN_TIMEOUT, '3', '\\d+', module: 'Admin'));
SettingMapper::create()->execute(new Setting(0, SettingsEnum::PASSWORD_INTERVAL, '90', '\\d+', module: 'Admin'));
@@ -86,7 +86,7 @@ final class Installer extends InstallerAbstract
SettingMapper::create()->execute(new Setting(0, SettingsEnum::LOGGING_STATUS, '1', '[0-3]', module: 'Admin'));
SettingMapper::create()->execute(new Setting(0, SettingsEnum::LOGGING_PATH, '', module: 'Admin'));
- SettingMapper::create()->execute(new Setting(0, SettingsEnum::DEFAULT_ORGANIZATION, '1', '\\d+', module: 'Admin'));
+ SettingMapper::create()->execute(new Setting(0, SettingsEnum::DEFAULT_UNIT, '1', '\\d+', module: 'Admin'));
SettingMapper::create()->execute(new Setting(0, SettingsEnum::LOGIN_STATUS, '1', '[0-3]', module: 'Admin'));
@@ -101,6 +101,8 @@ final class Installer extends InstallerAbstract
SettingMapper::create()->execute(new Setting(0, SettingsEnum::MAIL_SERVER_KEYPASS, '', module: 'Admin'));
SettingMapper::create()->execute(new Setting(0, SettingsEnum::MAIL_SERVER_TLS, (string) false, module: 'Admin'));
+ SettingMapper::create()->execute(new Setting(0, SettingsEnum::GROUP_GENERATE_AUTOMATICALLY_APP, (string) true, module: 'Admin'));
+
$cmdResult = \shell_exec(
(OperatingSystem::getSystem() === SystemType::WIN
? 'php.exe'
diff --git a/Controller/ApiController.php b/Controller/ApiController.php
index 4d2591d..a7de22b 100755
--- a/Controller/ApiController.php
+++ b/Controller/ApiController.php
@@ -14,6 +14,8 @@ declare(strict_types=1);
namespace Modules\Admin\Controller;
+use Model\Setting;
+use Model\SettingMapper;
use Modules\Admin\Models\Account;
use Modules\Admin\Models\AccountCredentialMapper;
use Modules\Admin\Models\AccountMapper;
@@ -36,6 +38,9 @@ use Modules\Media\Models\UploadFile;
use Modules\Admin\Models\App;
use phpOMS\Application\ApplicationType;
use Modules\Admin\Models\AppMapper;
+use Modules\Admin\Models\DataChange;
+use Modules\Admin\Models\NullDataChange;
+use Modules\Admin\Models\DataChangeMapper;
use phpOMS\Account\AccountStatus;
use phpOMS\Account\AccountType;
use phpOMS\Account\GroupStatus;
@@ -420,6 +425,7 @@ final class ApiController extends Controller
'response' => $this->app->appSettings->get(
$id !== null ? (int) $id : $id,
$request->getData('name') ?? '',
+ $request->getData('unit') ?? null,
$request->getData('app') ?? null,
$request->getData('module') ?? null,
$group !== null ? (int) $group : $group,
@@ -478,36 +484,133 @@ final class ApiController extends Controller
$id = isset($data['id']) ? (int) $data['id'] : null;
$name = $data['name'] ?? null;
$content = $data['content'] ?? null;
+ $unit = $data['unit'] ?? null;
$app = $data['app'] ?? null;
$module = $data['module'] ?? null;
$group = isset($data['group']) ? (int) $data['group'] : null;
$account = isset($data['account']) ? (int) $data['account'] : null;
- $this->updateModel(
- $request->header->account,
- $this->app->appSettings->get($id, $name, $app, $module, $group, $account),
- $data,
- function () use ($id, $name, $content, $app, $module, $group, $account) : void {
- $this->app->appSettings->set([
- [
- 'id' => $id,
- 'name' => $name,
- 'content' => $content,
- 'app' => $app,
- 'module' => $module,
- 'group' => $group,
- 'account' => $account,
- ],
- ], true);
- },
- 'settings',
- $request->getOrigin()
- );
+ $old = $this->app->appSettings->get($id, $name, $unit, $app, $module, $group, $account);
+ $new = clone $old;
+
+ $new->name = $name ?? $new->name;
+ $new->content = $content ?? $new->content;
+ $new->unit = $unit ?? $new->unit;
+ $new->app = $app ?? $new->app;
+ $new->module = $module ?? $new->module;
+ $new->group = $group ?? $new->group;
+ $new->account = $account ?? $new->account;
+
+ $this->app->appSettings->set([
+ [
+ 'id' => $new->id,
+ 'name' => $new->name,
+ 'content' => $new->content,
+ 'unit' => $new->unit,
+ 'app' => $new->app,
+ 'module' => $new->module,
+ 'group' => $new->group,
+ 'account' => $new->account
+ ]
+ ], false);
+
+ $this->updateModel($request->header->account, $old, $new, SettingMapper::class, 'settings',$request->getOrigin());
}
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Settings', 'Settings successfully modified', $dataSettings);
}
+ /**
+ * Api method for modifying account password
+ *
+ * @param RequestAbstract $request Request
+ * @param ResponseAbstract $response Response
+ * @param mixed $data Generic data
+ *
+ * @return void
+ *
+ * @api
+ *
+ * @since 1.0.0
+ */
+ public function apiSettingsAccountPasswordSet(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void
+ {
+ // has required data
+ if (!empty($val = $this->validatePasswordUpdate($request))) {
+ $response->set('password_update', new FormValidation($val));
+ $response->header->status = RequestStatusCode::R_400;
+
+ return;
+ }
+
+ $requestAccount = $request->header->account;
+
+ // request account is valid
+ if ($requestAccount <= 0) {
+ $this->fillJsonResponse($request, $response, NotificationLevel::HIDDEN, '', '', []);
+ $response->header->status = RequestStatusCode::R_403;
+
+ return;
+ }
+
+ $account = AccountMapper::get()
+ ->where('id', $requestAccount)
+ ->execute();
+
+ // test old password is correct
+ if (AccountMapper::login($account->login, (string) $request->getData('oldpass')) !== $requestAccount) {
+ $this->fillJsonResponse($request, $response, NotificationLevel::HIDDEN, '', '', []);
+ $response->header->status = RequestStatusCode::R_403;
+
+ return;
+ }
+
+ // test password repetition
+ if (((string) $request->getData('newpass')) !== ((string) $request->getData('reppass'))) {
+ $this->fillJsonResponse($request, $response, NotificationLevel::HIDDEN, '', '', []);
+ $response->header->status = RequestStatusCode::R_403;
+
+ return;
+ }
+
+ // test password complexity
+ $complexity = $this->app->appSettings->get(names: [SettingsEnum::PASSWORD_PATTERN], module: 'Admin');
+ if (\preg_match($complexity->content, (string) $request->getData('newpass')) !== 1) {
+ $this->fillJsonResponse($request, $response, NotificationLevel::HIDDEN, '', '', []);
+ $response->header->status = RequestStatusCode::R_403;
+
+ return;
+ }
+
+ $account->generatePassword((string) $request->getData('newpass'));
+
+ AccountMapper::update()->execute($account);
+
+ $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Password', 'Password successfully modified', $account);
+ }
+
+ /**
+ * Validate password update request
+ *
+ * @param RequestAbstract $request Request
+ *
+ * @return array
+ *
+ * @since 1.0.0
+ */
+ private function validatePasswordUpdate(RequestAbstract $request) : array
+ {
+ $val = [];
+ if (($val['oldpass'] = empty($request->getData('oldpass')))
+ || ($val['newpass'] = empty($request->getData('newpass')))
+ || ($val['reppass'] = empty($request->getData('reppass')))
+ ) {
+ return $val;
+ }
+
+ return [];
+ }
+
/**
* Api method for modifying account localization
*
@@ -529,7 +632,7 @@ final class ApiController extends Controller
if ($requestAccount !== $accountId
&& !$this->app->accountManager->get($accountId)->hasPermission(
PermissionType::MODIFY,
- $this->app->orgId,
+ $this->app->unitId,
$this->app->appName,
self::NAME,
PermissionCategory::ACCOUNT_SETTINGS,
@@ -703,11 +806,33 @@ final class ApiController extends Controller
}
$app = $this->createApplicationFromRequest($request);
-
$this->createModel($request->header->account, $app, AppMapper::class, 'application', $request->getOrigin());
+
+ $this->createDefaultAppSettings($app, $request);
+ /** @var \Model\Setting $setting */
+ $setting = $this->app->appSettings->get(null, SettingsEnum::GROUP_GENERATE_AUTOMATICALLY_APP);
+ if ($setting->content === '1') {
+ $newRequest = new HttpRequest();
+ $newRequest->header->account = $request->header->account;
+ $newRequest->setData('name', 'app:' . \strtolower($app->name));
+ $newRequest->setData('status', GroupStatus::ACTIVE);
+ $this->apiGroupCreate($newRequest, $response, $data);
+ }
+
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Application', 'Application successfully created', $app);
}
+ private function createDefaultAppSettings(App $app, RequestAbstract $request) : void
+ {
+ $settings = [];
+ $settings[] = new Setting(0, SettingsEnum::REGISTRATION_ALLOWED, '0', '\\d+', app: $app->getId(), module: 'Admin');
+ $settings[] = new Setting(0, SettingsEnum::APP_DEFAULT_GROUPS, '[]', app: $app->getId(), module: 'Admin');
+
+ foreach ($settings as $setting) {
+ $this->createModel($request->header->account, $setting, SettingMapper::class, 'setting', $request->getOrigin());
+ }
+ }
+
/**
* Validate app create request
*
@@ -1148,6 +1273,37 @@ final class ApiController extends Controller
$this->createProfileForAccount($account, $request);
$this->createMediaDirForAccount($account->getId(), $account->login ?? '', $request->header->account);
+ // find default groups and create them
+ $defaultGroups = [];
+ $defaultGroupIds = [];
+
+ if ($request->hasData('app')) {
+ $defaultGroupSettings = $this->app->appSettings->get(
+ names: SettingsEnum::APP_DEFAULT_GROUPS,
+ app: (int) $request->getData('app'),
+ module: 'Admin'
+ );
+ $defaultGroups = \array_merge($defaultGroups, \json_decode($defaultGroupSettings->content, true));
+ }
+
+
+ if ($request->hasData('unit')) {
+ $defaultGroupSettings = $this->app->appSettings->get(
+ names: SettingsEnum::UNIT_DEFAULT_GROUPS,
+ unit: (int) $request->getData('unit'),
+ module: 'Admin'
+ );
+ $defaultGroups = \array_merge($defaultGroups, \json_decode($defaultGroupSettings->content, true));
+ }
+
+ foreach ($defaultGroups as $group) {
+ $defaultGroupIds[] = $group->getId();
+ }
+
+ if (!empty($defaultGroupIds)) {
+ $this->createModelRelation($account->getId(), $account->getId(), $defaultGroupIds, AccountMapper::class, 'groups', 'account', $request->getOrigin());
+ }
+
$this->fillJsonResponse(
$request,
$response,
@@ -1160,6 +1316,240 @@ final class ApiController extends Controller
);
}
+ public function apiAccountRegister(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void
+ {
+ if (!empty($val = $this->validateRegistration($request))) {
+ $response->set('account_registration', new FormValidation($val));
+ $response->header->status = RequestStatusCode::R_400;
+
+ return;
+ }
+
+ $allowed = $this->app->appSettings->get(
+ names: [SettingsEnum::REGISTRATION_ALLOWED],
+ app: (int) $request->getData('app'),
+ module: 'Admin'
+ );
+
+ if ($allowed->content !== '1') {
+ $this->fillJsonResponse($request, $response, NotificationLevel::ERROR, 'Registration', 'Registration not allowed', []);
+ $response->header->status = RequestStatusCode::R_400;
+
+ return;
+ }
+
+ $complexity = $this->app->appSettings->get(names: [SettingsEnum::PASSWORD_PATTERN], module: 'Admin');
+ if ($request->hasData('password')
+ && \preg_match($complexity->content, (string) $request->getData('password')) !== 1
+ ) {
+ $this->fillJsonResponse($request, $response, NotificationLevel::ERROR, 'Registration', 'Invalid password format', []);
+ $response->header->status = RequestStatusCode::R_403;
+
+ return;
+ }
+
+ // Check if account already exists
+ /** @var Account $emailAccount */
+ $emailAccount = AccountMapper::get()->where('email', (string) $request->getData('email'))->execute();
+
+ /** @var Account $loginAccount */
+ $loginAccount = AccountMapper::get()->where('login', (string) ($request->getData('login') ?? $request->getData('email')))->execute();
+
+ /** @var null|Account $account */
+ $account = null;
+
+ // email already in use
+ if (!($emailAccount instanceof NullAccount)
+ && AccountMapper::login($emailAccount->login, (string) $request->getData('password')) !== LoginReturnType::OK
+ ) {
+ $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Registration', 'Email already in use, use your login details to login or activate your account also for this service.', []);
+ $response->header->status = RequestStatusCode::R_400;
+
+ return;
+ } elseif (!($emailAccount instanceof NullAccount)) {
+ $account = $emailAccount;
+ }
+
+ // login already in use by different email
+ if ($account === null
+ && !($loginAccount instanceof NullAccount)
+ && $loginAccount->getEmail() !== $request->getData('email')
+ ) {
+ $this->fillJsonResponse($request, $response, NotificationLevel::ERROR, 'Registration', 'Login already in use with a different email', []);
+ $response->header->status = RequestStatusCode::R_400;
+
+ return;
+ } elseif ($account === null
+ && !($loginAccount instanceof NullAccount)
+ && AccountMapper::login($loginAccount->login, (string) $request->getData('password')) !== LoginReturnType::OK
+ ) {
+ $account = $loginAccount;
+ }
+
+ $defaultGroups = [];
+ $defaultGroupIds = [];
+
+ $defaultGroupSettings = $this->app->appSettings->get(
+ names: SettingsEnum::APP_DEFAULT_GROUPS,
+ app: (int) $request->getData('app'),
+ module: 'Admin'
+ );
+ $defaultGroups = \array_merge($defaultGroups, \json_decode($defaultGroupSettings->content, true));
+
+ $defaultGroupSettings = $this->app->appSettings->get(
+ names: SettingsEnum::UNIT_DEFAULT_GROUPS,
+ unit: (int) $request->getData('unit'),
+ module: 'Admin'
+ );
+ $defaultGroups = \array_merge($defaultGroups, \json_decode($defaultGroupSettings->content, true));
+
+ foreach ($defaultGroups as $group) {
+ $defaultGroupIds[] = $group->getId();
+ }
+
+ // Already registered
+ if ($account !== null) {
+ $account = AccountMapper::get()
+ ->with('groups')
+ ->where('id', $account->getId())
+ ->execute();
+
+ foreach ($defaultGroupIds as $index => $id) {
+ if ($account->hasGroup($id)) {
+ unset($defaultGroupIds[$index]);
+ }
+ }
+
+ if (empty($defaultGroupIds)
+ && $account->getStatus() === AccountStatus::ACTIVE
+ ) {
+ $this->fillJsonResponse($request, $response, NotificationLevel::ERROR, 'Registration', 'You are already registered, use your login data.', []);
+ $response->header->status = RequestStatusCode::R_403;
+
+ return;
+ } elseif (empty($defaultGroupIds)
+ && $account->getStatus() === AccountStatus::INACTIVE
+ ) {
+ $this->fillJsonResponse($request, $response, NotificationLevel::ERROR, 'Registration', 'You are already registered, please activate your account through the email we sent you.', []);
+ $response->header->status = RequestStatusCode::R_403;
+
+ return;
+ }
+
+ // Create missing account / group relationships
+ $this->createModelRelation($account->getId(), $account->getId(), $defaultGroupIds, AccountMapper::class, 'groups', 'registration', $request->getOrigin());
+ } else {
+ $request->setData('status', AccountStatus::INACTIVE);
+ $request->setData('type', AccountType::USER);
+ $request->setData('name1', !$request->hasData('name1')
+ ? \explode('@', $request->getData('email'))[0]
+ : $request->getData('name1')
+ );
+ $request->setData('login', $request->getData('login') ?? $request->getData('email'));
+
+ $this->apiAccountCreate($request, $response, $data);
+ $account = $response->get($request->uri->__toString())['response'];
+
+ // Create confirmation pending entry
+ $dataChange = new DataChange();
+ $dataChange->type = 'account';
+ $dataChange->createdBy = $account->getId();
+
+ $dataChange->data = \json_encode([
+ 'status' => AccountStatus::ACTIVE
+ ]);
+
+ $tries = 0;
+ do {
+ $dataChange->reHash();
+ $this->createModel($account->getId(), $dataChange, DataChangeMapper::class, 'datachange', $request->getOrigin());
+
+ ++$tries;
+ } while($dataChange->getId() === 0 && $tries < 5);
+ }
+
+ // Create confirmation email
+ // @todo: send email for activation
+
+ $this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Registration', 'We have sent you an email to confirm your registration.', $account);
+ }
+
+ /**
+ * Method to validate account registration from request
+ *
+ * @param RequestAbstract $request Request
+ *
+ * @return array
+ *
+ * @since 1.0.0
+ */
+ private function validateRegistration(RequestAbstract $request) : array
+ {
+ $val = [];
+ if (($val['email'] = !empty($request->getData('email'))
+ && !EmailValidator::isValid((string) $request->getData('email')))
+ || ($val['unit'] = empty($request->getData('unit')))
+ || ($val['app'] = empty($request->getData('app')))
+ || ($val['password'] = empty($request->getData('password')))
+ ) {
+ return $val;
+ }
+
+ return [];
+ }
+
+ // @todo: maybe move to job/workflow??? This feels very much like a job/event especially if we make the 'type' an event-trigger
+ public function apiDataChange(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void
+ {
+ if (!empty($val = $this->validateDataChange($request))) {
+ $response->set('data_change', new FormValidation($val));
+ $response->header->status = RequestStatusCode::R_400;
+
+ return;
+ }
+
+ /** @var DataChange $dataChange */
+ $dataChange = DataChangeMapper::get()->where('hash', (string) $request->getData('hash'))->execute();
+ if ($dataChange instanceof NullDataChange) {
+ $response->header->status = RequestStatusCode::R_400;
+
+ return;
+ }
+
+ switch ($dataChange->type) {
+ case 'account':
+ $old = AccountMapper::get()->where('id', $dataChange->createdBy)->execute();
+ $new = clone $old;
+
+ $data = \json_decode($dataChange->data, true);
+ $new->setStatus((int) $data['status']);
+
+ $this->updateModel($dataChange->createdBy, $old, $new, AccountMapper::class, 'datachange', $request->getOrigin());
+ $this->deleteModel($dataChange->createdBy, $dataChange, DataChangeMapper::class, 'datachange', $request->getOrigin());
+
+ break;
+ }
+ }
+
+ /**
+ * Method to validate account registration from request
+ *
+ * @param RequestAbstract $request Request
+ *
+ * @return array
+ *
+ * @since 1.0.0
+ */
+ private function validateDataChange(RequestAbstract $request) : array
+ {
+ $val = [];
+ if (($val['hash'] = empty($request->getData('hash')))) {
+ return $val;
+ }
+
+ return [];
+ }
+
/**
* Create directory for an account
*
@@ -1285,11 +1675,18 @@ final class ApiController extends Controller
public function apiAccountUpdate(RequestAbstract $request, ResponseAbstract $response, mixed $data = null) : void
{
/** @var Account $old */
- $old = AccountMapper::get()->where('id', (int) $request->getData('id'))->execute();
+ $old = AccountMapper::get()
+ ->where('id', (int) $request->getData('id'))
+ ->execute();
+
$new = $this->updateAccountFromRequest($request, clone $old);
$this->updateModel($request->header->account, $old, $new, AccountMapper::class, 'account', $request->getOrigin());
- if (\Modules\Profile\Models\ProfileMapper::get()->where('account', $new->getId())->execute() instanceof \Modules\Profile\Models\NullProfile) {
+ $profile = \Modules\Profile\Models\ProfileMapper::get()
+ ->where('account', $new->getId())
+ ->execute();
+
+ if ($profile instanceof \Modules\Profile\Models\NullProfile) {
$this->createProfileForAccount($new, $request);
}
@@ -1418,6 +1815,7 @@ final class ApiController extends Controller
$moduleObj->theme = 'Default';
$moduleObj->path = $moduleInfo->getDirectory();
$moduleObj->version = $moduleInfo->getVersion();
+ $moduleObj->name = $moduleInfo->getExternalName();
$moduleObj->setStatus(ModuleStatus::AVAILABLE);
@@ -1725,7 +2123,7 @@ final class ApiController extends Controller
$permission->setUnit(empty($request->getData('permissionunit')) ? null : (int) $request->getData('permissionunit'));
$permission->setApp(empty($request->getData('permissionapp')) ? null : (string) $request->getData('permissionapp'));
$permission->setModule(empty($request->getData('permissionmodule')) ? null : (string) $request->getData('permissionmodule'));
- $permission->setCategory(empty($request->getData('permissiontype')) ? null : (int) $request->getData('permissiontype'));
+ $permission->setCategory(empty($request->getData('permissioncategory')) ? null : (int) $request->getData('permissioncategory'));
$permission->setElement(empty($request->getData('permissionelement')) ? null : (int) $request->getData('permissionelement'));
$permission->setComponent(empty($request->getData('permissioncomponent')) ? null : (int) $request->getData('permissioncomponent'));
$permission->setPermission(
@@ -1811,7 +2209,7 @@ final class ApiController extends Controller
$permission->setUnit(empty($request->getData('permissionunit')) ? $permission->getUnit() : (int) $request->getData('permissionunit'));
$permission->setApp(empty($request->getData('permissionapp')) ? $permission->getApp() : (string) $request->getData('permissionapp'));
$permission->setModule(empty($request->getData('permissionmodule')) ? $permission->getModule() : (string) $request->getData('permissionmodule'));
- $permission->setCategory(empty($request->getData('permissiontype')) ? $permission->getCategory() : (int) $request->getData('permissiontype'));
+ $permission->setCategory(empty($request->getData('permissioncategory')) ? $permission->getCategory() : (int) $request->getData('permissioncategory'));
$permission->setElement(empty($request->getData('permissionelement')) ? $permission->getElement() : (int) $request->getData('permissionelement'));
$permission->setComponent(empty($request->getData('permissioncomponent')) ? $permission->getComponent() : (int) $request->getData('permissioncomponent'));
$permission->setPermission((int) ($request->getData('permissioncreate') ?? 0)
diff --git a/Controller/BackendController.php b/Controller/BackendController.php
index c25534f..42a8d03 100755
--- a/Controller/BackendController.php
+++ b/Controller/BackendController.php
@@ -720,7 +720,7 @@ final class BackendController extends Controller
/** @var \Model\Setting[] $generalSettings */
$generalSettings = $this->app->appSettings->get(
names: [
- SettingsEnum::PASSWORD_PATTERN, SettingsEnum::LOGIN_TIMEOUT, SettingsEnum::PASSWORD_INTERVAL, SettingsEnum::PASSWORD_HISTORY, SettingsEnum::LOGIN_TRIES, SettingsEnum::LOGGING_STATUS, SettingsEnum::LOGGING_PATH, SettingsEnum::DEFAULT_ORGANIZATION,
+ SettingsEnum::PASSWORD_PATTERN, SettingsEnum::LOGIN_TIMEOUT, SettingsEnum::PASSWORD_INTERVAL, SettingsEnum::PASSWORD_HISTORY, SettingsEnum::LOGIN_TRIES, SettingsEnum::LOGGING_STATUS, SettingsEnum::LOGGING_PATH, SettingsEnum::DEFAULT_UNIT,
SettingsEnum::LOGIN_STATUS, SettingsEnum::DEFAULT_LOCALIZATION, SettingsEnum::MAIL_SERVER_ADDR,
],
module: 'Admin'
diff --git a/Models/AccountMapper.php b/Models/AccountMapper.php
index 079b877..832a90a 100755
--- a/Models/AccountMapper.php
+++ b/Models/AccountMapper.php
@@ -213,7 +213,7 @@ class AccountMapper extends DataMapperFactory
'account_lactive' => new \DateTime('now'),
'account_tries' => 0,
])
- ->where('account_login', '=', $login)
+ ->where('account_id', '=', (int) $result['account_id'])
->execute();
return $result['account_id'];
@@ -230,7 +230,7 @@ class AccountMapper extends DataMapperFactory
'account_lactive' => new \DateTime('now'),
'account_tries' => 0,
])
- ->where('account_login', '=', $login)
+ ->where('account_id', '=', (int) $result['account_id'])
->execute();
return $result['account_id'];
@@ -240,7 +240,7 @@ class AccountMapper extends DataMapperFactory
->set([
'account_tries' => $result['account_tries'] + 1,
])
- ->where('account_login', '=', $login)
+ ->where('account_id', '=', (int) $result['account_id'])
->execute();
return LoginReturnType::WRONG_PASSWORD;
diff --git a/Models/ApiKey.php b/Models/ApiKey.php
new file mode 100644
index 0000000..289de91
--- /dev/null
+++ b/Models/ApiKey.php
@@ -0,0 +1,79 @@
+key = \random_bytes(128);
+ $this->createdAt = new \DateTimeImmutable('now');
+ }
+}
diff --git a/Models/ApiKeyMapper.php b/Models/ApiKeyMapper.php
new file mode 100644
index 0000000..da89ea4
--- /dev/null
+++ b/Models/ApiKeyMapper.php
@@ -0,0 +1,121 @@
+
+ * @since 1.0.0
+ */
+ public const COLUMNS = [
+ 'account_api_id' => ['name' => 'account_api_id', 'type' => 'int', 'internal' => 'id'],
+ 'account_api_key' => ['name' => 'account_api_key', 'type' => 'string', 'internal' => 'key'],
+ 'account_api_status' => ['name' => 'account_api_status', 'type' => 'int', 'internal' => 'status'],
+ 'account_api_account' => ['name' => 'account_api_account', 'type' => 'int', 'internal' => 'account'],
+ 'account_api_created_at' => ['name' => 'account_api_created_at', 'type' => 'DateTime', 'internal' => 'createdAt'],
+ ];
+
+ /**
+ * Model to use by the mapper.
+ *
+ * @var string
+ * @since 1.0.0
+ */
+ public const MODEL = ApiKey::class;
+
+ /**
+ * Primary table.
+ *
+ * @var string
+ * @since 1.0.0
+ */
+ public const TABLE = 'account_api';
+
+ /**
+ * Primary field name.
+ *
+ * @var string
+ * @since 1.0.0
+ */
+ public const PRIMARYFIELD ='account_api_id';
+
+ /**
+ * Created at column
+ *
+ * @var string
+ * @since 1.0.0
+ */
+ public const CREATED_AT = 'account_api_created_at';
+
+ public static function authenticateApiKey(string $api) : int
+ {
+ if (empty($api)) {
+ return LoginReturnType::WRONG_PASSWORD;
+ }
+
+ try {
+ $result = null;
+
+ $query = new Builder(self::$db);
+ $result = $query->select('account.account_id', 'account.account_status')
+ ->from('account')
+ ->innerJoin('account_api')->on('account.account_id', '=', 'account_api.account_api_account')
+ ->where('account_api.account_api_key', '=', $api)
+ ->execute()
+ ?->fetchAll();
+
+ if ($result === null || !isset($result[0])) {
+ return LoginReturnType::WRONG_USERNAME; // wrong api key
+ }
+
+ $result = $result[0];
+
+ if ($result['account_status'] !== AccountStatus::ACTIVE) {
+ return LoginReturnType::INACTIVE;
+ }
+
+ if (empty($result['account_password'])) {
+ return LoginReturnType::EMPTY_PASSWORD;
+ }
+
+ $query->update('account')
+ ->set([
+ 'account_lactive' => new \DateTime('now'),
+ ])
+ ->where('account_id', '=', (int) $result['account_id'])
+ ->execute();
+
+ return (int) $result['account_id'];
+ } catch (\Exception $e) {
+ return LoginReturnType::FAILURE; // @codeCoverageIgnore
+ }
+ }
+}
diff --git a/Models/App.php b/Models/App.php
index ca094ba..094ee71 100755
--- a/Models/App.php
+++ b/Models/App.php
@@ -25,7 +25,7 @@ use phpOMS\Application\ApplicationType;
* @link https://jingga.app
* @since 1.0.0
*/
-class App
+class App implements \JsonSerializable
{
/**
* Id
@@ -78,4 +78,25 @@ class App
{
return $this->id;
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toArray() : array
+ {
+ return [
+ 'id' => $this->id,
+ 'name' => $this->name,
+ 'type' => $this->type,
+ 'status' => $this->status,
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function jsonSerialize() : mixed
+ {
+ return $this->toArray();
+ }
}
diff --git a/Models/DataChange.php b/Models/DataChange.php
new file mode 100644
index 0000000..18b0700
--- /dev/null
+++ b/Models/DataChange.php
@@ -0,0 +1,109 @@
+createdAt = new \DateTimeImmutable('now');
+ $this->reHash();
+ }
+
+ /**
+ * Get id
+ *
+ * @return int
+ *
+ * @since 1.0.0
+ */
+ public function getId() : int
+ {
+ return $this->id;
+ }
+
+ public function reHash() : void
+ {
+ $this->hash = \random_bytes(64);
+ }
+
+ /**
+ * Get hash
+ *
+ * @return string
+ *
+ * @since 1.0.0
+ */
+ public function getHash() : string
+ {
+ return $this->hash;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toArray() : array
+ {
+ return [
+ 'id' => $this->id,
+ 'data' => $this->data,
+ ];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function jsonSerialize() : mixed
+ {
+ return $this->toArray();
+ }
+}
diff --git a/Models/DataChangeMapper.php b/Models/DataChangeMapper.php
new file mode 100644
index 0000000..8b27e80
--- /dev/null
+++ b/Models/DataChangeMapper.php
@@ -0,0 +1,67 @@
+
+ * @since 1.0.0
+ */
+ public const COLUMNS = [
+ 'data_change_id' => ['name' => 'data_change_id', 'type' => 'int', 'internal' => 'id'],
+ 'data_change_type' => ['name' => 'data_change_type', 'type' => 'string', 'internal' => 'type'],
+ 'data_change_hash' => ['name' => 'data_change_hash', 'type' => 'string', 'internal' => 'hash'],
+ 'data_change_data' => ['name' => 'data_change_data', 'type' => 'string', 'internal' => 'data'],
+ 'data_change_created_by' => ['name' => 'data_change_created_by', 'type' => 'int', 'internal' => 'createdBy'],
+ 'data_change_created_at' => ['name' => 'data_change_created_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt'],
+ ];
+
+ /**
+ * Model to use by the mapper.
+ *
+ * @var string
+ * @since 1.0.0
+ */
+ public const MODEL = DataChange::class;
+
+ /**
+ * Primary table.
+ *
+ * @var string
+ * @since 1.0.0
+ */
+ public const TABLE = 'data_change';
+
+ /**
+ * Primary field name.
+ *
+ * @var string
+ * @since 1.0.0
+ */
+ public const PRIMARYFIELD ='data_change_id';
+}
diff --git a/Models/GroupPermission.php b/Models/GroupPermission.php
index 898150d..b9f119d 100755
--- a/Models/GroupPermission.php
+++ b/Models/GroupPermission.php
@@ -41,8 +41,8 @@ class GroupPermission extends PermissionAbstract
* Constructor.
*
* @param int $group Group id
- * @param null|int $unit Unit Unit to check (null if all are acceptable)
- * @param null|string $app App App to check (null if all are acceptable)
+ * @param null|int $unit Unit to check (null if all are acceptable)
+ * @param null|string $app App to check (null if all are acceptable)
* @param null|string $module Module to check (null if all are acceptable)
* @param null|string $from Module providing this permission
* @param null|int $category Category (e.g. customer) (null if all are acceptable)
diff --git a/Models/ModuleMapper.php b/Models/ModuleMapper.php
index 9ff91fc..0591a2d 100755
--- a/Models/ModuleMapper.php
+++ b/Models/ModuleMapper.php
@@ -34,6 +34,7 @@ final class ModuleMapper extends DataMapperFactory
*/
public const COLUMNS = [
'module_id' => ['name' => 'module_id', 'type' => 'string', 'internal' => 'id'],
+ 'module_name' => ['name' => 'module_name', 'type' => 'string', 'internal' => 'name'],
'module_path' => ['name' => 'module_path', 'type' => 'string', 'internal' => 'path'],
'module_theme' => ['name' => 'module_theme', 'type' => 'string', 'internal' => 'theme'],
'module_version' => ['name' => 'module_version', 'type' => 'string', 'internal' => 'version'],
diff --git a/Models/NullDataChange.php b/Models/NullDataChange.php
new file mode 100644
index 0000000..ef47264
--- /dev/null
+++ b/Models/NullDataChange.php
@@ -0,0 +1,47 @@
+id = $id;
+ parent::__construct();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function jsonSerialize() : mixed
+ {
+ return ['id' => $this->id];
+ }
+}
diff --git a/Models/SettingsEnum.php b/Models/SettingsEnum.php
index c8cb8a1..121f453 100755
--- a/Models/SettingsEnum.php
+++ b/Models/SettingsEnum.php
@@ -42,7 +42,9 @@ abstract class SettingsEnum extends Enum
public const LOGGING_PATH = '1000000007';
/* Organization settings */
- public const DEFAULT_ORGANIZATION = '1000000009';
+ public const DEFAULT_UNIT = '1000000008';
+
+ public const UNIT_DEFAULT_GROUPS = '1000000009';
/* Login settings */
public const LOGIN_FORGOTTEN_COUNT = '1000000010';
@@ -77,13 +79,14 @@ abstract class SettingsEnum extends Enum
public const CLI_ACTIVE = '1000000023';
/* Global default templates */
- public const DEFAULT_PDF_EXPORT_TEMPLATE = '1000000024';
+ public const DEFAULT_LIST_EXPORTS = '1000000024';
- public const DEFAULT_CSV_EXPORT_TEMPLATE = '1000000025';
+ public const DEFAULT_LETTERS = '1000000025';
- public const DEFAULT_EXCEL_EXPORT_TEMPLATE = '1000000026';
+ /* App settings */
+ public const REGISTRATION_ALLOWED = '1000000029';
- public const DEFAULT_WORD_EXPORT_TEMPLATE = '1000000027';
+ public const GROUP_GENERATE_AUTOMATICALLY_APP = '1000000030';
- public const DEFAULT_EMAIL_EXPORT_TEMPLATE = '1000000028';
+ public const APP_DEFAULT_GROUPS = '1000000031';
}
diff --git a/tests/Controller/ApiControllerTest.php b/tests/Controller/ApiControllerTest.php
index e22eddd..df05953 100755
--- a/tests/Controller/ApiControllerTest.php
+++ b/tests/Controller/ApiControllerTest.php
@@ -59,7 +59,7 @@ final class ApiControllerTest extends \PHPUnit\Framework\TestCase
};
$this->app->dbPool = $GLOBALS['dbpool'];
- $this->app->orgId = 1;
+ $this->app->unitId = 1;
$this->app->accountManager = new AccountManager($GLOBALS['session']);
$this->app->appSettings = new CoreSettings();
$this->app->moduleManager = new ModuleManager($this->app, __DIR__ . '/../../../../Modules/');