'
+ . '
{PHPUnit\TextUI\Command}'
+ . '
[A]
[',
+ 'c' => 'PHPUnit\TextUI\Command} - error msg
',
+ ];
+ // array provider
+ return [
+ 'A debug: on, print: on, echo: on' => [
+ [
+ 'debug_all' => true,
+ 'print_all' => true,
+ 'echo_all' => true,
+ ],
+ $error_msg['A'],
+ true,
+ $file_msg['A'],
+ $string_msg['A']['s'],
+ $string_msg['A']['c'],
+ ],
+ 'B debug: on, print: off, echo: on' => [
+ [
+ 'debug_all' => true,
+ 'print_all' => false,
+ 'echo_all' => true,
+ ],
+ $error_msg['A'],
+ true,
+ '',
+ $string_msg['A']['s'],
+ $string_msg['A']['c'],
+ ],
+ 'C debug: on, print: on, echo: off' => [
+ [
+ 'debug_all' => true,
+ 'print_all' => true,
+ 'echo_all' => false,
+ ],
+ $error_msg['A'],
+ true,
+ $file_msg['A'],
+ '',
+ '',
+ ],
+ 'D debug: on, print: off, echo: off' => [
+ [
+ 'debug_all' => true,
+ 'print_all' => false,
+ 'echo_all' => false,
+ ],
+ $error_msg['A'],
+ false,
+ '',
+ '',
+ ''
+ ],
+ 'E debug: off, print: off, echo: off' => [
+ [
+ 'debug_all' => false,
+ 'print_all' => false,
+ 'echo_all' => false,
+ ],
+ $error_msg['A'],
+ false,
+ '',
+ '',
+ ''
+ ]
+ // TODO more tests with different error messages
+ ];
+ }
+
+ /**
+ * Test debug flow
+ *
+ * @covers ::debug
+ * @dataProvider debugProvider
+ * @testdox check debug flow: $expected_debug [$_dataName]
+ *
+ * @param array $options
+ * @param array $debug_msg
+ * @param boolean $expected_debug
+ * @param string $expected_file
+ * @param string $expected_string_start
+ * @param string $expected_string_contains
+ * @return void
+ */
+ public function testDebug(
+ array $options,
+ array $debug_msg,
+ bool $expected_debug,
+ string $expected_file,
+ string $expected_string_start,
+ string $expected_string_contains
+ ): void {
+ // must run with below matrix
+ // level | debug | print | echo | debug() | printErrorMsg() | file
+ // A 1/1/1 | on | on | on | true | 'string' | on
+ // B 1/0/1 | on | off | on | true | 'string' | off
+ // C 1/1/0 | on | on | off | true | '' | on
+ // D 1/0/0 | on | off | off | false | '' | off
+ // E 0/1/1 | off | on | on | false | '' | off
+ // F 0/0/1 | off | off | on | false | '' | off
+ // G 0/1/0 | off | on | off | false | '' | off
+ // H 0/0/0 | off | off | off | false | '' | off
+
+
+ // * debug off
+ // return false on debug(),
+ // return false on writeErrorMsg()
+ // empty string on printErrorMsg
+ // * print off
+ // return true on debug(),
+ // return false on writeErrorMsg()
+ // empty string on printErrorMsg
+ // * echo off
+ // return true on debug(),
+ // empty string on printErrorMsg
+ // fillxed error_msg array
+
+ // overwrite any previous set from test
+ $options['file_id'] = 'TestDebug';
+ // set log folder to temp
+ $options['log_folder'] = '/tmp/';
+ // remove any files named /tmp/error_log_TestDebug*.log
+ array_map('unlink', glob($options['log_folder'] . 'error_msg_' . $options['file_id'] . '*.log'));
+ // init logger
+ $log = new \CoreLibs\Debug\Logging($options);
+ // * debug (A/B)
+ // NULL check for strip/prefix
+ $this->assertEquals(
+ $log->debug(
+ $debug_msg['level'],
+ $debug_msg['string'],
+ $debug_msg['strip'],
+ $debug_msg['prefix'],
+ ),
+ $expected_debug
+ );
+ // * if print check data in log file
+ $log_file = $log->getLogFileName();
+ if (!empty($options['debug_all']) && !empty($options['print_all'])) {
+ // file name matching
+ $this->assertStringStartsWith(
+ $options['log_folder'] . 'error_msg_' . $options['file_id'],
+ $log_file,
+ );
+ // cotents check
+ if (!is_file($log_file)) {
+ $this->fail('error msg file not found: ' . $log_file);
+ } else {
+ $log_data = file_get_contents($log_file);
+ if ($log_data === null) {
+ $this->fail('error msg file not readable or not data: ' . $log_file);
+ }
+ // file content matching
+ $this->assertStringEndsWith(
+ $expected_file,
+ $log_data,
+ );
+ }
+ } else {
+ // there should be no file there
+ $this->assertEquals(
+ $log_file,
+ ''
+ );
+ }
+ // ** ECHO ON
+ $log_string = $log->printErrorMsg();
+ // * print
+ if (!empty($options['debug_all']) && !empty($options['echo_all'])) {
+ // print $log->printErrorMsg() . "\n";
+ // echo string must start with
+ $this->assertStringStartsWith(
+ $expected_string_start,
+ $log_string
+ );
+ // echo string must containt
+ $this->assertStringContainsString(
+ $expected_string_contains,
+ $log_string
+ );
+ // TODO: as printing directly is not really done anymore tests below are todo
+ // * get error msg (getErrorMsg)
+ // * merge error msg (mergeErrors)
+ // * print merged (printErrorMsg)
+ // * reset A (resetErrorMsg)
+ // * reset ALL (resetErrorMsg)
+ } else {
+ $this->assertEquals(
+ $log_string,
+ ''
+ );
+ }
+ }
+
+ // TODO: setLogUniqueId/getLogUniqueId
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function logUniqueIdProvider(): array
+ {
+ return [
+ 'option set' => [
+ 'option' => true,
+ 'override' => false,
+ ],
+ 'direct set' => [
+ 'option' => false,
+ 'override' => false,
+ ],
+ 'override set' => [
+ 'option' => false,
+ 'override' => true,
+ ],
+ 'option and override set' => [
+ 'option' => false,
+ 'override' => true,
+ ],
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @covers ::setLogUniqueId
+ * @covers ::getLogUniqueId
+ * @dataProvider logUniqueIdProvider
+ * @testdox per run log id set test: option: $option, override: $override [$_dataName]
+ *
+ * @param bool $option
+ * @param bool $override
+ * @return void
+ */
+ public function testLogUniqueId(bool $option, bool $override): void
+ {
+ if ($option === true) {
+ $log = new \CoreLibs\Debug\Logging(['per_run' => $option]);
+ } else {
+ $log = new \CoreLibs\Debug\Logging();
+ $log->setLogUniqueId();
+ }
+ $per_run_id = $log->getLogUniqueId();
+ $this->assertMatchesRegularExpression(
+ "/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
+ $per_run_id,
+ 'assert per log run id 1st'
+ );
+ if ($override === true) {
+ $log->setLogUniqueId(true);
+ $per_run_id_2nd = $log->getLogUniqueId();
+ $this->assertMatchesRegularExpression(
+ "/^\d{4}-\d{2}-\d{2}_\d{6}_U_[a-z0-9]{8}$/",
+ $per_run_id_2nd,
+ 'assert per log run id 2nd'
+ );
+ $this->assertNotEquals(
+ $per_run_id,
+ $per_run_id_2nd,
+ '1st and 2nd don\'t match'
+ );
+ }
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsDebugMemoryUsageTest.php b/test/phpunit/CoreLibsDebugMemoryUsageTest.php
new file mode 100644
index 0000000..7b8e020
--- /dev/null
+++ b/test/phpunit/CoreLibsDebugMemoryUsageTest.php
@@ -0,0 +1,119 @@
+ '/^[\w\s_-]+$/',
+ 'peak' => '/^\d+$/',
+ 'usage' => '/^\d+$/',
+ 'start' => '/^\d+$/',
+ 'last' => '/^\d+$/',
+ 'set' => '/^\d+$/',
+ ];
+ // 0: prefix
+ // 1: raw flag
+ // 2: set flags array
+ // 3: array output expected (as regex)
+ // 4: string output expected (as regex)
+ return [
+ 'test normal' => [
+ 'test',
+ null,
+ [],
+ $regex_array,
+ $regex_raw_off,
+ ]
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @cover ::resetMemory
+ * @cover ::debugMemoryFlag
+ * @cover ::setStartMemory
+ * @cover ::setMemory
+ * @cover ::memoryUsage
+ * @cover ::printMemoryUsage
+ * @dataProvider memoryUsageProvider
+ * @testdox memoryUsage with $prefix, raw memory $raw [$_dataName]
+ *
+ * @param string $prefix
+ * @param bool|null $raw
+ * @param array $set_flags
+ * @param array $expected_array
+ * @param string $expected_string
+ * @return void
+ */
+ public function testMemoryUsage(
+ string $prefix,
+ ?bool $raw,
+ array $settings,
+ array $expected_array,
+ string $expected_string
+ ): void {
+ // always reeset to null
+ MemoryUsage::resetMemory();
+ MemoryUsage::debugMemoryFlag(true);
+ MemoryUsage::setStartMemory();
+ MemoryUsage::setMemory();
+ // run collector
+ $memory = MemoryUsage::memoryUsage($prefix);
+ if ($raw === null) {
+ $string = MemoryUsage::printMemoryUsage($memory);
+ } else {
+ $string = MemoryUsage::printMemoryUsage($memory, $raw);
+ }
+
+ // expected_array for each
+ foreach ($expected_array as $name => $regex) {
+ $this->assertMatchesRegularExpression(
+ $regex,
+ (string)$memory[$name],
+ 'assert memory usage array ' . $name
+ );
+ }
+
+ // regex match string
+ $this->assertMatchesRegularExpression(
+ $expected_string,
+ $string,
+ 'assert memory usage string as regex'
+ );
+
+ // TODO additional tests with use more memory and check diff matching
+ // TODO reset memory usage test
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsDebugRunningTimeTest.php b/test/phpunit/CoreLibsDebugRunningTimeTest.php
new file mode 100644
index 0000000..2037563
--- /dev/null
+++ b/test/phpunit/CoreLibsDebugRunningTimeTest.php
@@ -0,0 +1,183 @@
+ [
+ 0 => null,
+ 1 => '/^\d{4}\.\d{1,}$/'
+ ],
+ 'nanoseconds' => [
+ 0 => 'ns',
+ 1 => '/^\d{10}$/'
+ ],
+ 'microseconds' => [
+ 0 => 'ys',
+ 1 => '/^\d{7}\.\d{1,}$/'
+ ],
+ 'milliseconds' => [
+ 0 => 'ms',
+ 1 => '/^\d{4}\.\d{1,}$/'
+ ],
+ 'seconds' => [
+ 0 => 's',
+ 1 => '/^\d{1}\.\d{4,}$/'
+ ],
+ 'invalid fallback to ms' => [
+ 0 => 'invalid',
+ 1 => '/^\d{4}\.\d{1,}$/'
+ ]
+ ];
+ }
+
+ public function runningTimeProvider(): array
+ {
+ return [
+ 'run time test' => [
+ 0 => '/^\d{1,}\.\d{1,}$/',
+ 1 => '/^Start: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 0\.\d{8}, $/',
+ 2 => '/^Start: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 0\.\d{8}, '
+ . 'End: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} 0\.\d{8}, '
+ . 'Run: \d{1,}\.\d{1,} s$/'
+ ]
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @cover ::hrRunningTime
+ * @dataProvider hrRunningTimeProvider
+ * @testdox hrRunningTime with $out_time matching $expected [$_dataName]
+ *
+ * @param string|null $out_time
+ * @param string $expected
+ * @return void
+ */
+ public function testHrRunningTime(?string $out_time, string $expected): void
+ {
+ // reset for each run
+ \CoreLibs\Debug\RunningTime::hrRunningTimeReset();
+ $start = \CoreLibs\Debug\RunningTime::hrRunningTime();
+ $this->assertEquals(
+ 0,
+ $start,
+ 'assert first run 0'
+ );
+ time_nanosleep(1, 500);
+ if ($out_time === null) {
+ $second = \CoreLibs\Debug\RunningTime::hrRunningTime();
+ } else {
+ $second = \CoreLibs\Debug\RunningTime::hrRunningTime($out_time);
+ }
+ // print "E: " . $end . "\n";
+ $this->assertMatchesRegularExpression(
+ $expected,
+ (string)$second,
+ 'assert second run regex'
+ );
+ if ($out_time === null) {
+ $end_second = \CoreLibs\Debug\RunningTime::hrRunningTimeFromStart();
+ } else {
+ $end_second = \CoreLibs\Debug\RunningTime::hrRunningTimeFromStart($out_time);
+ }
+ $this->assertEquals(
+ $end_second,
+ $second,
+ 'assert end is equal second'
+ );
+ // sleep again, second messurement
+ time_nanosleep(1, 500);
+ if ($out_time === null) {
+ $third = \CoreLibs\Debug\RunningTime::hrRunningTime();
+ } else {
+ $third = \CoreLibs\Debug\RunningTime::hrRunningTime($out_time);
+ }
+ // third call is not null
+ $this->assertNotEquals(
+ 0,
+ $third,
+ 'assert third call not null'
+ );
+ // third call is bigger than end
+ $this->assertNotEquals(
+ $second,
+ $third,
+ 'assert third different second'
+ );
+ // last messurement, must match start - end + last
+ if ($out_time === null) {
+ $end = \CoreLibs\Debug\RunningTime::hrRunningTimeFromStart();
+ } else {
+ $end = \CoreLibs\Debug\RunningTime::hrRunningTimeFromStart($out_time);
+ }
+ $this->assertGreaterThan(
+ $third,
+ $end,
+ 'assert end greater third'
+ );
+ // new start
+ \CoreLibs\Debug\RunningTime::hrRunningTimeReset();
+ $new_start = \CoreLibs\Debug\RunningTime::hrRunningTime();
+ $this->assertEquals(
+ 0,
+ $new_start,
+ 'assert new run 0'
+ );
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @dataProvider runningTimeProvider
+ * @testdox runningTime matching return $expected_number and start $expected_start end $expected_end [$_dataName]
+ *
+ * @param string $expected_number
+ * @param string $expected_start
+ * @param string $expected_end
+ * @return void
+ */
+ public function testRunningTime(string $expected_number, string $expected_start, string $expected_end): void
+ {
+ $start = \CoreLibs\Debug\RunningTime::runningTime(true);
+ // print "Start: " . $start . "\n";
+ $this->assertEquals(
+ 0,
+ $start
+ );
+ // print "STRING: " . \CoreLibs\Debug\RunningTime::runningTimeString() . "\n";
+ $this->assertMatchesRegularExpression(
+ $expected_start,
+ \CoreLibs\Debug\RunningTime::runningTimeString()
+ );
+ time_nanosleep(1, 500);
+ $end = \CoreLibs\Debug\RunningTime::runningTime(true);
+ // print "Start: " . $end . "\n";
+ $this->assertMatchesRegularExpression(
+ $expected_number,
+ (string)$end
+ );
+ // print "STRING: " . \CoreLibs\Debug\RunningTime::runningTimeString() . "\n";
+ $this->assertMatchesRegularExpression(
+ $expected_end,
+ \CoreLibs\Debug\RunningTime::runningTimeString()
+ );
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsDebugSupportTest.php b/test/phpunit/CoreLibsDebugSupportTest.php
new file mode 100644
index 0000000..9bf2fef
--- /dev/null
+++ b/test/phpunit/CoreLibsDebugSupportTest.php
@@ -0,0 +1,475 @@
+ [
+ 0 => null,
+ 1 => "/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{8}$/",
+ ],
+ 'microtime -1' => [
+ 0 => -1,
+ 1 => "/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{8}$/",
+ ],
+ 'microtime 0' => [
+ 0 => 0,
+ 1 => "/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/",
+ ],
+ 'microtime 4' => [
+ 0 => 4,
+ 1 => "/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{4}$/",
+ ],
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function printArrayProvider(): array
+ {
+ return [
+ 'empty array' => [
+ 0 => [],
+ 1 => "
Array\n(\n)\n
"
+ ],
+ 'simple array' => [
+ 0 => ['a', 'b'],
+ 1 => "
Array\n(\n"
+ . " [0] => a\n"
+ . " [1] => b\n"
+ . ")\n
"
+ ],
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function printBoolProvider(): array
+ {
+ return [
+ 'true input default' => [
+ 0 => true,
+ 1 => [],
+ 2 => 'true'
+ ],
+ 'false input default' => [
+ 0 => false,
+ 1 => [],
+ 2 => 'false'
+ ],
+ 'false input param name' => [
+ 0 => false,
+ 1 => [
+ 'name' => 'param test'
+ ],
+ 2 => '
param test: false'
+ ],
+ 'true input param name, true override' => [
+ 0 => true,
+ 1 => [
+ 'name' => 'param test',
+ 'true' => 'ok'
+ ],
+ 2 => '
param test: ok'
+ ],
+ 'false input param name, true override, false override' => [
+ 0 => false,
+ 1 => [
+ 'name' => 'param test',
+ 'true' => 'ok',
+ 'false' => 'not',
+ ],
+ 2 => '
param test: not'
+ ],
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function printToStringProvider(): array
+ {
+ // 0: unput
+ // 1: html flag (only for strings and arry)
+ // 2: expected
+ return [
+ 'null' => [
+ null,
+ null,
+ 'NULL',
+ ],
+ 'string' => [
+ 'a string',
+ null,
+ 'a string',
+ ],
+ 'string with html chars, encode' => [
+ 'a string with <> &',
+ true,
+ 'a string with <> &',
+ ],
+ 'string with html chars' => [
+ 'a string with <> &',
+ null,
+ 'a string with <> &',
+ ],
+ 'a number' => [
+ 1234,
+ null,
+ '1234',
+ ],
+ 'a float number' => [
+ 1234.5678,
+ null,
+ '1234.5678',
+ ],
+ 'bool true' => [
+ true,
+ null,
+ 'TRUE',
+ ],
+ 'bool false' => [
+ false,
+ null,
+ 'FALSE',
+ ],
+ 'an array default' => [
+ ['a', 'b'],
+ null,
+ "
Array\n(\n"
+ . " [0] => a\n"
+ . " [1] => b\n"
+ . ")\n
",
+ ],
+ 'an array, no html' => [
+ ['a', 'b'],
+ true,
+ "##HTMLPRE##"
+ . "Array\n(\n"
+ . " [0] => a\n"
+ . " [1] => b\n"
+ . ")\n"
+ . "##/HTMLPRE##",
+ ],
+ // resource
+ 'a resource' => [
+ tmpfile(),
+ null,
+ '/^Resource id #\d+$/',
+ ],
+ // object
+ 'an object' => [
+ new \CoreLibs\Debug\Support(),
+ null,
+ 'CoreLibs\Debug\Support',
+ ]
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function debugStringProvider(): array
+ {
+ // 0: input string
+ // 1: replace
+ // 2: html flag
+ // 3: expected
+ return [
+ 'null string, default' => [
+ null,
+ null,
+ null,
+ '-'
+ ],
+ 'empty string, ... replace' => [
+ '',
+ '...',
+ null,
+ '...'
+ ],
+ 'filled string' => [
+ 'some string',
+ null,
+ null,
+ 'some string'
+ ],
+ 'string with html chars, encode' => [
+ 'a string with <> &',
+ '-',
+ true,
+ 'a string with <> &',
+ ],
+ 'string with html chars' => [
+ 'a string with <> &',
+ '-',
+ null,
+ 'a string with <> &',
+ ],
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @cover ::printTime
+ * @dataProvider printTimeProvider
+ * @testdox printTime test with $microtime and match to regex [$_dataName]
+ *
+ * @param int|null $mircrotime
+ * @param string $expected
+ * @return void
+ */
+ public function testPrintTime(?int $microtime, string $regex): void
+ {
+ if ($microtime === null) {
+ $this->assertMatchesRegularExpression(
+ $regex,
+ \CoreLibs\Debug\Support::printTime()
+ );
+ } else {
+ $this->assertMatchesRegularExpression(
+ $regex,
+ \CoreLibs\Debug\Support::printTime($microtime)
+ );
+ }
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @cover ::printAr
+ * @cover ::printArray
+ * @dataProvider printArrayProvider
+ * @testdox printAr/printArray $input will be $expected [$_dataName]
+ *
+ * @param array $input
+ * @param string $expected
+ * @return void
+ */
+ public function testPrintAr(array $input, string $expected): void
+ {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Debug\Support::printAr($input),
+ 'assert printAr'
+ );
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Debug\Support::printArray($input),
+ 'assert printArray'
+ );
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @cover ::printBool
+ * @dataProvider printBoolProvider
+ * @testdox printBool $input will be $expected [$_dataName]
+ *
+ * @param bool $input
+ * @param array $params
+ * @param string $expected
+ * @return void
+ */
+ public function testPrintBool(bool $input, array $params, string $expected): void
+ {
+ if (
+ isset($params['name']) &&
+ isset($params['true']) &&
+ isset($params['false'])
+ ) {
+ $string = \CoreLibs\Debug\Support::printBool(
+ $input,
+ $params['name'],
+ $params['true'],
+ $params['false']
+ );
+ } elseif (isset($params['name']) && isset($params['true'])) {
+ $string = \CoreLibs\Debug\Support::printBool(
+ $input,
+ $params['name'],
+ $params['true']
+ );
+ } elseif (isset($params['name'])) {
+ $string = \CoreLibs\Debug\Support::printBool(
+ $input,
+ $params['name']
+ );
+ } else {
+ $string = \CoreLibs\Debug\Support::printBool($input);
+ }
+ $this->assertEquals(
+ $expected,
+ $string,
+ 'assert printBool'
+ );
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @cover ::printToString
+ * @dataProvider printToStringProvider
+ * @testdox printToString $input with $flag will be $expected [$_dataName]
+ *
+ * @param mixed $input anything
+ * @param boolean|null $flag html flag, only for string and array
+ * @param string $expected always string
+ * @return void
+ */
+ public function testPrintToString(mixed $input, ?bool $flag, string $expected): void
+ {
+ if ($flag === null) {
+ // if expected starts with / and ends with / then this is a regex compare
+ if (
+ substr($expected, 0, 1) == '/' &&
+ substr($expected, -1, 1) == '/'
+ ) {
+ $this->assertMatchesRegularExpression(
+ $expected,
+ \CoreLibs\Debug\Support::printToString($input)
+ );
+ } else {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Debug\Support::printToString($input)
+ );
+ }
+ } else {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Debug\Support::printToString($input, $flag)
+ );
+ }
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @cover ::getCallerMethod
+ * @testWith ["testGetCallerMethod"]
+ * @testdox getCallerMethod check if it returns $expected [$_dataName]
+ *
+ * @return void
+ */
+ public function testGetCallerMethod(string $expected): void
+ {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Debug\Support::getCallerMethod()
+ );
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @cover ::getCallerMethodList
+ * @testWith [["main", "run", "run", "run", "run", "run", "run", "runBare", "runTest", "testGetCallerMethodList"],["main", "run", "run", "run", "run", "run", "run", "run", "runBare", "runTest", "testGetCallerMethodList"]]
+ * @testdox getCallerMethodList check if it returns $expected [$_dataName]
+ *
+ * @param array $expected
+ * @return void
+ */
+ public function testGetCallerMethodList(array $expected, array $expected_group): void
+ {
+ $compare = \CoreLibs\Debug\Support::getCallerMethodList();
+ // if we direct call we have 10, if we call as folder we get 11
+ if (count($compare) == 10) {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Debug\Support::getCallerMethodList(),
+ 'assert expected 10'
+ );
+ } else {
+ $this->assertEquals(
+ $expected_group,
+ \CoreLibs\Debug\Support::getCallerMethodList(),
+ 'assert expected group'
+ );
+ }
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @cover ::getCallerClass
+ * @testWith ["PHPUnit\\TextUI\\Command"]
+ * @testdox getCallerClass check if it returns $expected [$_dataName]
+ *
+ * @return void
+ */
+ public function testGetCallerClass(string $expected): void
+ {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Debug\Support::getCallerClass()
+ );
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @cover ::debugString
+ * @dataProvider debugStringProvider
+ * @testdox debugString $input with replace $replace and html $flag will be $expected [$_dataName]
+ *
+ * @param string|null $input
+ * @param string|null $replace
+ * @param bool|null $flag
+ * @param string $expected
+ * @return void
+ */
+ public function testDebugString(?string $input, ?string $replace, ?bool $flag, string $expected): void
+ {
+ if ($replace === null && $flag === null) {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Debug\Support::debugString($input),
+ 'assert all default'
+ );
+ } elseif ($flag === null) {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Debug\Support::debugString($input, $replace),
+ 'assert flag default'
+ );
+ } else {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Debug\Support::debugString($input, $replace, $flag),
+ 'assert all set'
+ );
+ }
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsGetDotEnvTest.php b/test/phpunit/CoreLibsGetDotEnvTest.php
new file mode 100644
index 0000000..285e19e
--- /dev/null
+++ b/test/phpunit/CoreLibsGetDotEnvTest.php
@@ -0,0 +1,162 @@
+ 'A',
+ 'OTHER' => 'B IS B',
+ 'Complex' => 'A B \"D is F',
+ 'HAS_SPACE' => 'ABC',
+ 'HAS_COMMENT_QUOTES_SPACE' => 'Comment at end with quotes and space',
+ 'HAS_COMMENT_QUOTES_NO_SPACE' => 'Comment at end with quotes no space',
+ 'HAS_COMMENT_NO_QUOTES_SPACE' => 'Comment at end no quotes and space',
+ 'HAS_COMMENT_NO_QUOTES_NO_SPACE' => 'Comment at end no quotes no space',
+ 'COMMENT_IN_TEXT_QUOTES' => 'Foo bar # comment in here',
+ 'FAILURE' => 'ABC',
+ 'SIMPLEBOX' => 'A B C',
+ 'TITLE' => '1',
+ 'FOO' => '1.2',
+ 'SOME.TEST' => 'Test Var',
+ 'SOME.LIVE' => 'Live Var',
+ 'A_TEST1' => 'foo',
+ 'A_TEST2' => '${TEST1:-bar}',
+ 'A_TEST3' => '${TEST4:-bar}',
+ 'A_TEST5' => 'null',
+ 'A_TEST6' => '${TEST5-bar}',
+ 'A_TEST7' => '${TEST6:-bar}',
+ 'B_TEST1' => 'foo',
+ 'B_TEST2' => '${TEST1:=bar}',
+ 'B_TEST3' => '${TEST4:=bar}',
+ 'B_TEST5' => 'null',
+ 'B_TEST6' => '${TEST5=bar}',
+ 'B_TEST7' => '${TEST6=bar}',
+ 'Test' => 'A',
+ 'TEST' => 'B',
+ 'LINE' => "ABC\nDEF",
+ 'OTHERLINE' => "ABC\nAF\"ASFASDF\nMORESHIT",
+ 'SUPERLINE' => '',
+ '__FOO_BAR_1' => 'b',
+ '__FOOFOO' => 'f ',
+ 123123 => 'number',
+ 'EMPTY' => '',
+ ];
+ // 0: folder relative to test folder, if unset __DIR__
+ // 1: file, if unset .env
+ // 2: status to be returned
+ // 3: _ENV file content to be set
+ // 4: override chmod as octect in string
+ return [
+ 'default' => [
+ 'folder' => null,
+ 'file' => null,
+ 'status' => 3,
+ 'content' => [],
+ 'chmod' => null,
+ ],
+ 'cannot open file' => [
+ 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
+ 'file' => 'cannot_read.env',
+ 'status' => 2,
+ 'content' => [],
+ 'chmod' => '000',
+ ],
+ 'empty file' => [
+ 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
+ 'file' => 'empty.env',
+ 'status' => 1,
+ 'content' => [],
+ 'chmod' => null,
+ ],
+ 'override all' => [
+ 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
+ 'file' => 'test.env',
+ 'status' => 0,
+ 'content' => $dot_env_content,
+ 'chmod' => null,
+ ],
+ 'override directory' => [
+ 'folder' => __DIR__ . DIRECTORY_SEPARATOR . 'dotenv',
+ 'file' => null,
+ 'status' => 0,
+ 'content' => $dot_env_content,
+ 'chmod' => null,
+ ],
+ ];
+ }
+
+ /**
+ * test read .env file
+ *
+ * @covers ::readEnvFile
+ * @dataProvider envFileProvider
+ * @testdox Read _ENV file from $folder / $file with expected status: $expected_status and chmod $chmod [$_dataName]
+ *
+ * @param string|null $folder
+ * @param string|null $file
+ * @param int $expected_status
+ * @param array $expected_env
+ * @param string|null $chmod
+ * @return void
+ */
+ public function testReadEnvFile(
+ ?string $folder,
+ ?string $file,
+ int $expected_status,
+ array $expected_env,
+ ?string $chmod
+ ): void {
+ // if we have file + chmod set
+ $old_chmod = null;
+ if (
+ is_file($folder . DIRECTORY_SEPARATOR . $file) &&
+ !empty($chmod)
+ ) {
+ // get the old permissions
+ $old_chmod = fileperms($folder . DIRECTORY_SEPARATOR . $file);
+ chmod($folder . DIRECTORY_SEPARATOR . $file, octdec($chmod));
+ }
+ if ($folder !== null && $file !== null) {
+ $status = DotEnv::readEnvFile($folder, $file);
+ } elseif ($folder !== null) {
+ $status = DotEnv::readEnvFile($folder);
+ } else {
+ $status = DotEnv::readEnvFile();
+ }
+ $this->assertEquals(
+ $status,
+ $expected_status,
+ 'Assert returned status equal'
+ );
+ // now assert read data
+ $this->assertEquals(
+ $_ENV,
+ $expected_env,
+ 'Assert _ENV correct'
+ );
+ // if we have file and chmod unset
+ if ($old_chmod !== null) {
+ chmod($folder . DIRECTORY_SEPARATOR . $file, $old_chmod);
+ }
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsGetSystemTest.php b/test/phpunit/CoreLibsGetSystemTest.php
new file mode 100644
index 0000000..26ea383
--- /dev/null
+++ b/test/phpunit/CoreLibsGetSystemTest.php
@@ -0,0 +1,221 @@
+ [
+ 0 => UPLOAD_ERR_INI_SIZE,
+ 1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
+ ],
+ 'upload err from size' => [
+ 0 => UPLOAD_ERR_FORM_SIZE,
+ 1 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'
+ ],
+ 'upload err partial' => [
+ 0 => UPLOAD_ERR_PARTIAL,
+ 1 => 'The uploaded file was only partially uploaded'
+ ],
+ 'upload err no file' => [
+ 0 => UPLOAD_ERR_NO_FILE,
+ 1 => 'No file was uploaded'
+ ],
+ 'upload err no tmp dir' => [
+ 0 => UPLOAD_ERR_NO_TMP_DIR,
+ 1 => 'Missing a temporary folder'
+ ],
+ 'upload err cant write' => [
+ 0 => UPLOAD_ERR_CANT_WRITE,
+ 1 => 'Failed to write file to disk'
+ ],
+ 'upload err extension' => [
+ 0 => UPLOAD_ERR_EXTENSION,
+ 1 => 'File upload stopped by extension'
+ ],
+ 'unkown error' => [
+ 0 => 99999,
+ 1 => 'Unknown upload error'
+ ]
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function getHostNameProvider(): array
+ {
+ return [
+ 'original set' => [
+ 0 => null,
+ 1 => 'NOHOST',
+ 2 => 'NOPORT',
+ ],
+ 'override set no port' => [
+ 0 => 'foo.org',
+ 1 => 'foo.org',
+ 2 => '80'
+ ],
+ 'override set with port' => [
+ 0 => 'foo.org:443',
+ 1 => 'foo.org',
+ 2 => '443'
+ ]
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function getPageNameProvider(): array
+ {
+ return [
+ // 0: input
+ // 1: expected default/WITH_EXTENSION
+ // 2: expected NO_EXTENSION
+ // 3: expected FULL_PATH, if first and last character are / use regex
+ 'original set' => [
+ 0 => null,
+ 1 => 'phpunit',
+ 2 => 'phpunit',
+ // NOTE: this can change, so it is a regex check
+ 3 => "/^(\/?.*\/?)?www\/vendor\/bin\/phpunit$/",
+ ],
+ 'some path with extension' => [
+ 0 => '/some/path/to/file.txt',
+ 1 => 'file.txt',
+ 2 => 'file',
+ 3 => '/some/path/to/file.txt',
+ ]
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @covers ::fileUploadErrorMessage
+ * @dataProvider fileUploadErrorMessageProvider
+ * @testdox fileUploadErrorMessage $input error matches $expected [$_dataName]
+ *
+ * @param integer $input
+ * @param string $expected
+ * @return void
+ */
+ public function testFileUploadErrorMessage(int $input, string $expected): void
+ {
+ $this->assertEquals(
+ $expected,
+ \CoreLibs\Get\System::fileUploadErrorMessage($input)
+ );
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @covers ::getHostName
+ * @dataProvider getHostNameProvider
+ * @testdox getHostName $input must match $expected_host:$expected_port [$_dataName]
+ *
+ * @param string|null $input
+ * @param string $expected_host
+ * @param string $expected_port
+ * @return void
+ */
+ public function testGetHostNanme(?string $input, string $expected_host, string $expected_port): void
+ {
+ // print "HOSTNAME: " . $_SERVER['HTTP_HOST'] . "
";
+ // print "SERVER: " . print_r($_SERVER, true) . "\n";
+ // print "SELF: " . $_SERVER['PHP_SELF'] . "\n";
+ if ($input !== null) {
+ $_SERVER['HTTP_HOST'] = $input;
+ }
+ list ($host, $port) = \CoreLibs\Get\System::getHostName();
+ $this->assertEquals(
+ $expected_host,
+ $host,
+ 'failed expected host assert'
+ );
+ $this->assertEquals(
+ $expected_port,
+ $port,
+ 'faile expected port assert'
+ );
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @covers ::getPageName
+ * @dataProvider getPageNameProvider
+ * @testdox getPageName $input will match 0: $expected_0, 1: $expected_1, 2: $expected_2 [$_dataName]
+ *
+ * @param string|null $input
+ * @param string $expected_0 default with extension
+ * @param string $expected_1 no extension
+ * @param string $expected_2 full path
+ * @return void
+ */
+ public function testGetPageName(?string $input, string $expected_0, string $expected_1, string $expected_2)
+ {
+ if ($input !== null) {
+ $_SERVER['PHP_SELF'] = $input;
+ }
+ // default 0,
+ $this->assertEquals(
+ $expected_0,
+ \CoreLibs\Get\System::getPageName(),
+ 'failed default assert'
+ );
+ $this->assertEquals(
+ $expected_0,
+ \CoreLibs\Get\System::getPageName(\CoreLibs\Get\System::WITH_EXTENSION),
+ 'failed WITH_EXTESION assert'
+ );
+ $this->assertEquals(
+ $expected_1,
+ \CoreLibs\Get\System::getPageName(\CoreLibs\Get\System::NO_EXTENSION),
+ 'failed NO_EXTENSION assert'
+ );
+ // FULL PATH check can be equals or regex
+ $page_name_full_path = \CoreLibs\Get\System::getPageName(\CoreLibs\Get\System::FULL_PATH);
+ if (
+ substr($expected_2, 0, 1) == '/' &&
+ substr($expected_2, -1, 1) == '/'
+ ) {
+ // this is regex
+ $this->assertMatchesRegularExpression(
+ $expected_2,
+ $page_name_full_path,
+ 'failed FULL_PATH assert regex'
+ );
+ } else {
+ $this->assertEquals(
+ $expected_2,
+ $page_name_full_path,
+ 'failed FULL_PATH assert equals'
+ );
+ }
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsLanguageGetLocaleTest.php b/test/phpunit/CoreLibsLanguageGetLocaleTest.php
new file mode 100644
index 0000000..981c3e2
--- /dev/null
+++ b/test/phpunit/CoreLibsLanguageGetLocaleTest.php
@@ -0,0 +1,310 @@
+ [
+ // lang, domain, encoding, path
+ null, null, null, null,
+ // SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
+ null, null,
+ // return array
+ [
+ 'locale' => 'en_US.UTF-8',
+ 'lang' => 'en_US',
+ 'domain' => 'frontend',
+ 'encoding' => 'UTF-8',
+ 'path' => "/^\/(.*\/)?includes\/locale\/$/",
+ ],
+ ],
+ 'no params, session charset and lang' => [
+ // lang, domain, encoding, path
+ null, null, null, null,
+ // SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
+ 'ja_JP', 'UTF-8',
+ // return array
+ [
+ 'locale' => 'ja_JP',
+ 'lang' => 'ja_JP',
+ 'domain' => 'frontend',
+ 'encoding' => 'UTF-8',
+ 'path' => "/^\/(.*\/)?includes\/locale\/$/",
+ ],
+ ],
+ 'no params, session charset and lang short' => [
+ // lang, domain, encoding, path
+ null, null, null, null,
+ // SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
+ 'ja', 'UTF-8',
+ // return array
+ [
+ 'locale' => 'ja',
+ 'lang' => 'ja',
+ 'domain' => 'frontend',
+ 'encoding' => 'UTF-8',
+ 'path' => "/^\/(.*\/)?includes\/locale\/$/",
+ ],
+ ],
+ // param lang (no sessions)
+ 'locale param only, no sessions' => [
+ // lang, domain, encoding, path
+ 'ja.UTF-8', null, null, null,
+ // SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
+ null, null,
+ // return array
+ [
+ 'locale' => 'ja.UTF-8',
+ 'lang' => 'ja',
+ 'domain' => 'frontend',
+ 'encoding' => 'UTF-8',
+ 'path' => "/^\/(.*\/)?includes\/locale\/$/",
+ ],
+ ],
+ // different locale setting
+ 'locale complex param only, no sessions' => [
+ // lang, domain, encoding, path
+ 'ja_JP.SJIS', null, null, null,
+ // SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
+ null, null,
+ // return array
+ [
+ 'locale' => 'ja_JP.SJIS',
+ 'lang' => 'ja_JP',
+ 'domain' => 'frontend',
+ 'encoding' => 'SJIS',
+ 'path' => "/^\/(.*\/)?includes\/locale\/$/",
+ ],
+ ],
+ // param lang and domain (no override)
+ 'locale, domain params, no sessions' => [
+ // lang, domain, encoding, path
+ 'ja.UTF-8', 'admin', null, null,
+ // SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
+ null, null,
+ // return array
+ [
+ 'locale' => 'ja.UTF-8',
+ 'lang' => 'ja',
+ 'domain' => 'admin',
+ 'encoding' => 'UTF-8',
+ 'path' => "/^\/(.*\/)?includes\/locale\/$/",
+ ],
+ ],
+ // param lang and domain (no override)
+ 'locale, domain, encoding params, no sessions' => [
+ // lang, domain, encoding, path
+ 'ja.UTF-8', 'admin', 'UTF-8', null,
+ // SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
+ null, null,
+ // return array
+ [
+ 'locale' => 'ja.UTF-8',
+ 'lang' => 'ja',
+ 'domain' => 'admin',
+ 'encoding' => 'UTF-8',
+ 'path' => "/^\/(.*\/)?includes\/locale\/$/",
+ ],
+ ],
+ // lang, domain, path (no override)
+ 'locale, domain and path, no sessions' => [
+ // lang, domain, encoding, path
+ 'ja.UTF-8', 'admin', '', __DIR__ . '/locale_other/',
+ // SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
+ null, null,
+ // return array
+ [
+ 'locale' => 'ja.UTF-8',
+ 'lang' => 'ja',
+ 'domain' => 'admin',
+ 'encoding' => 'UTF-8',
+ 'path' => "/^\/(.*\/)?locale_other\/$/",
+ ],
+ ],
+ // all params set (no override)
+ 'all parameter, no sessions' => [
+ // lang, domain, encoding, path
+ 'ja', 'admin', 'UTF-8', __DIR__ . '/locale_other/',
+ // SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
+ null, null,
+ // return array
+ [
+ 'locale' => 'ja',
+ 'lang' => 'ja',
+ 'domain' => 'admin',
+ 'encoding' => 'UTF-8',
+ 'path' => "/^\/(.*\/)?locale_other\/$/",
+ ],
+ ],
+ // param lang and domain (no override)
+ 'long locale, domain, encoding params, no sessions' => [
+ // lang, domain, encoding, path
+ 'de_CH.UTF-8@euro', 'admin', 'UTF-8', null,
+ // SESSION DEFAULT_LOCALE, SESSION: DEFAULT_CHARSET
+ null, null,
+ // return array
+ [
+ 'locale' => 'de_CH.UTF-8@euro',
+ 'lang' => 'de_CH',
+ 'domain' => 'admin',
+ 'encoding' => 'UTF-8',
+ 'path' => "/^\/(.*\/)?includes\/locale\/$/",
+ ],
+ ],
+ // TODO invalid params (bad path) (no override)
+ // TODO param calls, but with override set
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @covers ::setLocale
+ * @dataProvider setLocaleProvider
+ * @testdox lang settings lang $language, domain $domain, encoding $encoding, path $path; session lang: $SESSION_DEFAULT_LOCALE, session char: $SESSION_DEFAULT_CHARSET [$_dataName]
+ *
+ * @return void
+ */
+ public function testsetLocale(
+ ?string $language,
+ ?string $domain,
+ ?string $encoding,
+ ?string $path,
+ ?string $SESSION_DEFAULT_LOCALE,
+ ?string $SESSION_DEFAULT_CHARSET,
+ array $expected
+ ): void {
+ $return_lang_settings = [];
+ global $_SESSION;
+ // set override
+ if ($SESSION_DEFAULT_LOCALE !== null) {
+ $_SESSION['DEFAULT_LOCALE'] = $SESSION_DEFAULT_LOCALE;
+ }
+ if ($SESSION_DEFAULT_CHARSET !== null) {
+ $_SESSION['DEFAULT_CHARSET'] = $SESSION_DEFAULT_CHARSET;
+ }
+ // function call
+ if ($language === null && $domain === null && $encoding === null && $path === null) {
+ $return_lang_settings = \CoreLibs\Language\GetLocale::setLocale();
+ } elseif ($language !== null && $domain === null && $encoding === null && $path === null) {
+ $return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
+ $language
+ );
+ } elseif ($language !== null && $domain !== null && $encoding === null && $path === null) {
+ $return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
+ $language,
+ $domain
+ );
+ } elseif ($language !== null && $domain !== null && $encoding !== null && $path === null) {
+ $return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
+ $language,
+ $domain,
+ $encoding
+ );
+ } else {
+ $return_lang_settings = \CoreLibs\Language\GetLocale::setLocale(
+ $language,
+ $domain,
+ $encoding,
+ $path
+ );
+ }
+ // print "RETURN: " . print_r($return_lang_settings, true) . "\n";
+
+ foreach (
+ [
+ 'locale', 'lang', 'domain', 'encoding', 'path'
+ ] as $key
+ ) {
+ $value = $expected[$key];
+ if (strpos($value, "/") === 0) {
+ // this is regex
+ $this->assertMatchesRegularExpression(
+ $value,
+ $return_lang_settings[$key],
+ 'assert regex failed for ' . $key
+ );
+ } else {
+ // assert equal
+ $this->assertEquals(
+ $value,
+ $return_lang_settings[$key],
+ 'assert equal failed for ' . $key
+ );
+ }
+ }
+ // unset all vars
+ $_SESSION = [];
+ unset($GLOBALS['OVERRIDE_LANG']);
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsLanguageL10nTest.php b/test/phpunit/CoreLibsLanguageL10nTest.php
new file mode 100644
index 0000000..ee5bf0f
--- /dev/null
+++ b/test/phpunit/CoreLibsLanguageL10nTest.php
@@ -0,0 +1,1046 @@
+assertIsObject(
+ $l10n_obj
+ );
+ $this->assertInstanceOf(
+ '\CoreLibs\Language\L10n',
+ $l10n_obj
+ );
+ }
+
+ /**
+ * get current translator class type check
+ *
+ * @covers ::getTranslatorClass
+ * @testdox check that getTranslatorClass() returns valid instance
+ *
+ * @return void
+ */
+ public function testGetTranslatorClass(): void
+ {
+ $l10n = new \CoreLibs\Language\L10n();
+ $translator = $l10n->getTranslatorClass();
+ $this->assertIsObject(
+ $translator
+ );
+ $this->assertInstanceOf(
+ '\CoreLibs\Language\Core\GetTextReader',
+ $translator
+ );
+ }
+
+ /**
+ * provider for class load parameters
+ *
+ * @return array
+ */
+ public function l10nObjectProvider(): array
+ {
+ return [
+ // 0: locale
+ // 1: domain
+ // 2: encoding
+ // 3: path
+ // 4: locale expected
+ // 5: locale set expected
+ // 6: domain exepcted
+ // 7: context (null for none)
+ // 8: test string in
+ // 9: test translated
+ // new style load
+ 'gettext load en' => [
+ 'en_US.UTF-8',
+ 'frontend',
+ __DIR__ . 'includes/locale/',
+ //
+ 'en_US.UTF-8',
+ 'en_US',
+ 'frontend',
+ null,
+ 'Original',
+ 'Translated frontend en_US',
+ ],
+ 'gettext load en' => [
+ 'en_US.UTF-8',
+ 'frontend',
+ __DIR__ . 'includes/locale/',
+ //
+ 'en_US.UTF-8',
+ 'en_US',
+ 'frontend',
+ 'context',
+ 'Original',
+ 'Original context frontend en_US',
+ ],
+ 'gettext load ja' => [
+ 'ja_JP.UTF-8',
+ 'admin',
+ __DIR__ . 'includes/locale/',
+ //
+ 'ja_JP.UTF-8',
+ 'ja_JP',
+ 'admin',
+ null,
+ 'Original',
+ 'Translated admin ja_JP',
+ ],
+ // mixed path and domain
+ 'mixed path and domain' => [
+ 'en_US.UTF-8',
+ __DIR__ . 'includes/locale/',
+ 'frontend',
+ //
+ 'en_US.UTF-8',
+ 'en_US',
+ 'frontend',
+ 'context',
+ 'Original',
+ 'Original context frontend en_US',
+ ],
+ // null set
+ 'empty load new ' => [
+ '',
+ '',
+ '',
+ //
+ '',
+ '',
+ '',
+ null,
+ 'Original',
+ 'Original',
+ ]
+ ];
+ }
+
+ /**
+ * new class load test (basic test)
+ *
+ * @covers ::__construct
+ * @dataProvider l10nObjectProvider
+ * @testdox check l10n init with Locale $locale, Path $path, Domain $domain, Legacy: $legacy with $context [$_dataName]
+ *
+ * @param string|null $locale
+ * @param string|null $domain
+ * @param string|null $path
+ * @param string $locale_expected
+ * @param string $locale_set_expected
+ * @param string $domain_expected
+ * @param ?string $context
+ * @param string $original
+ * @param string $translated
+ * @return void
+ */
+ public function testL10nObject(
+ ?string $locale,
+ ?string $domain,
+ ?string $path,
+ string $locale_expected,
+ string $locale_set_expected,
+ string $domain_expected,
+ ?string $context,
+ string $original,
+ string $translated
+ ): void {
+ if ($locale === null) {
+ $l10n = new \CoreLibs\Language\L10n();
+ } elseif ($domain === null) {
+ $l10n = new \CoreLibs\Language\L10n($locale);
+ } elseif ($path === null) {
+ $l10n = new \CoreLibs\Language\L10n($locale, $domain);
+ } else {
+ $l10n = new \CoreLibs\Language\L10n($locale, $domain, $path);
+ }
+ // print "LOC: " . $locale . ", " . $l10n->getLocale() . ", " . $locale_expected . "\n";
+ // print "MO: " . $l10n->getMoFile() . "\n";
+ $this->assertEquals(
+ $locale_expected,
+ $l10n->getLocale(),
+ 'Locale assert failed'
+ );
+ $this->assertEquals(
+ $locale_set_expected,
+ $l10n->getLocaleSet(),
+ 'Locale set assert failed'
+ );
+ $this->assertEquals(
+ $domain_expected,
+ $l10n->getDomain(),
+ 'Domain assert failed'
+ );
+ if (empty($context)) {
+ $this->assertEquals(
+ $translated,
+ $l10n->__($original),
+ 'Translated string assert failed'
+ );
+ } else {
+ $this->assertEquals(
+ $translated,
+ $l10n->__p($context, $original),
+ 'Translated string assert failed in context: ' . $context
+ );
+ }
+ }
+
+ // l10nReloadMOfile and getTranslator
+ // null init with loader
+ // loader with reload (change type)
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function getTranslatorProvider(): array
+ {
+ return [
+ // 0: locale
+ // 1: domain
+ // 2: path
+ // 3: load error
+ // 4: input string to translated
+ // 5: expected locale
+ // 6: expected locale set
+ // 7: expected domain
+ // 8: expected translation
+ // 9: change locale
+ // 10: change domain
+ // 11: change path
+ // 12: change load error
+ // 13: expected locale
+ // 14: expected locale set
+ // 15: expected domain
+ // 16: expected translation
+ 'load and change (en->ja)' => [
+ // set 0-2
+ 'en_US.UTF-8',
+ 'frontend',
+ __DIR__ . 'includes/locale/',
+ // status 3
+ false,
+ // to translate 4
+ 'Original',
+ // check setter 5-7
+ 'en_US.UTF-8',
+ 'en_US',
+ 'frontend',
+ 'Translated frontend en_US',
+ // set new 8-10
+ 'ja_JP.UTF-8',
+ 'frontend',
+ __DIR__ . 'includes/locale/',
+ // status new 11
+ false,
+ // check new setter 12-14
+ 'ja_JP.UTF-8',
+ 'ja_JP',
+ 'frontend',
+ 'Translated frontend ja_JP',
+ ],
+ 'empty load and change to en' => [
+ // set 0-2
+ '',
+ '',
+ '',
+ // status 3
+ false,
+ // to translate 4
+ 'Original',
+ // check setter 5-7
+ '',
+ '',
+ '',
+ 'Original',
+ // set new 8-10
+ 'en_US.UTF-8',
+ 'frontend',
+ __DIR__ . 'includes/locale/',
+ // status new 11
+ false,
+ // check new setter 12-14
+ 'en_US.UTF-8',
+ 'en_US',
+ 'frontend',
+ 'Translated frontend en_US',
+ ]
+ ];
+ }
+
+ /**
+ * init check and connected change translation
+ *
+ * @covers ::getTranslator
+ * @covers ::l10nReloadMOfile
+ * @dataProvider getTranslatorProvider
+ * @testdox change locale from $locale and domain $domain to locale $locale_new and domain $domain_new [$_dataName]
+ *
+ * @param string|null $locale
+ * @param string|null $domain
+ * @param string|null $path
+ * @param bool $load_error
+ * @param string $original
+ * @param string $locale_expected_a
+ * @param string $locale_set_expected_a
+ * @param string $domain_expected_a
+ * @param string $translated_a
+ * @param string|null $locale_new
+ * @param string|null $domain_new
+ * @param string|null $path_new
+ * @param bool $load_error_new
+ * @param string $locale_set_expected_b
+ * @param string $locale_expected_b
+ * @param string $domain_expected_b
+ * @param string $translated_b
+ * @return void
+ */
+ public function testGetTranslator(
+ // 0-2
+ ?string $locale,
+ ?string $domain,
+ ?string $path,
+ // 3
+ bool $load_error,
+ // 4
+ string $original,
+ // 5-7
+ string $locale_expected_a,
+ string $locale_set_expected_a,
+ string $domain_expected_a,
+ string $translated_a,
+ // 8-10
+ ?string $locale_new,
+ ?string $domain_new,
+ ?string $path_new,
+ // 11
+ bool $load_error_new,
+ // 12-14
+ string $locale_expected_b,
+ string $locale_set_expected_b,
+ string $domain_expected_b,
+ string $translated_b
+ ): void {
+ if ($locale === null) {
+ $l10n = new \CoreLibs\Language\L10n();
+ } elseif ($domain === null) {
+ $l10n = new \CoreLibs\Language\L10n($locale);
+ } elseif ($path === null) {
+ $l10n = new \CoreLibs\Language\L10n($locale, $domain);
+ } else {
+ $l10n = new \CoreLibs\Language\L10n($locale, $domain, $path);
+ }
+ // print "LOC: " . $locale . ", " . $l10n->getLocale() . ", " . $locale_expected . "\n";
+ // status check
+ $this->assertEquals(
+ $load_error,
+ $l10n->getLoadError(),
+ 'Legacy method load error init check'
+ );
+ $this->assertEquals(
+ $locale_expected_a,
+ $l10n->getLocale(),
+ 'Locale init assert failed'
+ );$this->assertEquals(
+ $locale_set_expected_a,
+ $l10n->getLocaleSet(),
+ 'Locale Set init assert failed'
+ );
+ $this->assertEquals(
+ $domain_expected_a,
+ $l10n->getDomain(),
+ 'Domain init assert failed'
+ );
+ $this->assertEquals(
+ $translated_a,
+ $l10n->__($original),
+ 'Translated string init assert failed'
+ );
+
+ // switch
+ if ($locale_new === null) {
+ $translator = $l10n->getTranslator();
+ } elseif ($domain_new === null) {
+ $translator = $l10n->getTranslator($locale_new);
+ } elseif ($path_new === null) {
+ $translator = $l10n->getTranslator($locale_new, $domain_new);
+ } else {
+ $translator = $l10n->getTranslator($locale_new, $domain_new, $path_new);
+ }
+ // status check
+ $this->assertEquals(
+ $load_error_new,
+ $l10n->getLoadError(),
+ 'Translate method load error change check'
+ );
+ // check that returned is class GetTextReader and object
+ $this->assertIsObject(
+ $translator,
+ 'translater class is object assert failed'
+ );
+ $this->assertInstanceOf(
+ '\CoreLibs\Language\Core\GetTextReader',
+ $translator,
+ 'translator class is correct instance assert failed'
+ );
+
+ // translator class
+ $this->assertEquals(
+ $translated_b,
+ $translator->gettext($original),
+ 'Translated string change assert failed from returned class'
+ );
+ // new set check
+ $this->assertEquals(
+ $locale_expected_b,
+ $l10n->getLocale(),
+ 'Locale change assert failed'
+ );
+ $this->assertEquals(
+ $locale_set_expected_b,
+ $l10n->getLocaleSet(),
+ 'Locale Set change assert failed'
+ );
+ $this->assertEquals(
+ $domain_expected_b,
+ $l10n->getDomain(),
+ 'Domain change assert failed'
+ );
+ $this->assertEquals(
+ $translated_b,
+ $l10n->__($original),
+ 'Translated string change assert failed'
+ );
+ }
+
+ // TODO: domain based
+ // ->dgettext
+ // ->dngettext
+ // ->dpgettext
+ // ->dpngettext
+
+ /**
+ * for plural and plural context
+ *
+ * @return array
+ */
+ public function ngettextProvider(): array
+ {
+ return [
+ // 0: locale
+ // 1: path
+ // 2: domain
+ // 3: context (null for none)
+ // 4: single string
+ // 5: plural string
+ // 6: array for each n value expected string
+ 'plural text en' => [
+ 'en_US',
+ __DIR__ . 'includes/locale/',
+ 'admin',
+ // context
+ null,
+ // text single/multi in
+ 'single',
+ 'multi',
+ // confirm translation, pos in array equal n
+ [
+ 0 => 'Multi admin en_US 1',
+ 1 => 'Multi admin en_US 0',
+ 2 => 'Multi admin en_US 1',
+ ]
+ ],
+ 'plural text context en' => [
+ 'en_US',
+ __DIR__ . 'includes/locale/',
+ 'admin',
+ // context
+ 'context',
+ // text single/multi in
+ 'single',
+ 'multi',
+ // confirm translation, pos in array equal n
+ [
+ 0 => 'Multi context admin en_US 1',
+ 1 => 'Multi context admin en_US 0',
+ 2 => 'Multi context admin en_US 1',
+ ]
+ ],
+ ];
+ }
+
+ /**
+ * plural and plural context
+ *
+ * @covers ::__n
+ * @covers ::__pn
+ * @dataProvider ngettextProvider
+ * @testdox plural string test for locale $locale and domain $domain with $context [$_dataName]
+ *
+ * @param string $locale
+ * @param string $path
+ * @param string $domain
+ * @param ?string $context
+ * @param string $original_single
+ * @param string $original_plural
+ * @param array $expected_strings
+ * @return void
+ */
+ public function testNgettext(
+ // config 0-3
+ string $locale,
+ string $path,
+ string $domain,
+ // context string
+ ?string $context,
+ // input strings
+ string $original_single,
+ string $original_plural,
+ // expected
+ array $expected_strings
+ ): void {
+ $l10n = new \CoreLibs\Language\L10n($locale, $path, $domain, false);
+
+ foreach ($expected_strings as $n => $expected) {
+ if (empty($context)) {
+ $this->assertEquals(
+ $expected,
+ $l10n->__n($original_single, $original_plural, $n),
+ 'assert failed for plural: ' . $n
+ );
+ } else {
+ $this->assertEquals(
+ $expected,
+ $l10n->__np($context, $original_single, $original_plural, $n),
+ 'assert failed for plural: ' . $n . ' in context: ' . $context
+ );
+ }
+ }
+ }
+
+ /**
+ * locales list for testing locale folder lookup
+ *
+ * @return array
+ */
+ public function localesProvider(): array
+ {
+ return [
+ // 0: locale
+ // 1: return array
+ 'en' => [
+ 'en',
+ [
+ 'en',
+ ],
+ [
+ 'lang' => 'en',
+ 'country' => null,
+ 'charset' => null,
+ 'modifier' => null,
+ ],
+ ],
+ 'en.UTF-8' => [
+ 'en.UTF-8',
+ [
+ 'en.UTF-8',
+ 'en',
+ ],
+ [
+ 'lang' => 'en',
+ 'country' => null,
+ 'charset' => 'UTF-8',
+ 'modifier' => null,
+ ],
+ ],
+ 'en_US' => [
+ 'en_US',
+ [
+ 'en_US',
+ 'en',
+ ],
+ [
+ 'lang' => 'en',
+ 'country' => 'US',
+ 'charset' => null,
+ 'modifier' => null,
+ ],
+ ],
+ 'en_US.UTF-8' => [
+ 'en_US.UTF-8',
+ [
+ 'en_US.UTF-8',
+ 'en_US',
+ 'en',
+ ],
+ [
+ 'lang' => 'en',
+ 'country' => 'US',
+ 'charset' => 'UTF-8',
+ 'modifier' => null,
+ ],
+ ],
+ 'en_US@subtext' => [
+ 'en_US@subtext',
+ [
+ 'en_US@subtext',
+ 'en@subtext',
+ 'en_US',
+ 'en',
+ ],
+ [
+ 'lang' => 'en',
+ 'country' => 'US',
+ 'charset' => null,
+ 'modifier' => 'subtext',
+ ],
+ ],
+ 'en_US.UTF-8@subtext' => [
+ 'en_US.UTF-8@subtext',
+ [
+ 'en_US.UTF-8@subtext',
+ 'en_US@subtext',
+ 'en@subtext',
+ 'en_US.UTF-8',
+ 'en_US',
+ 'en',
+ ],
+ [
+ 'lang' => 'en',
+ 'country' => 'US',
+ 'charset' => 'UTF-8',
+ 'modifier' => 'subtext',
+ ],
+ ]
+ ];
+ }
+
+ /**
+ * test locales array return
+ *
+ * @covers ::listLocales
+ * @dataProvider localesProvider
+ * @testdox check $locale [$_dataName]
+ *
+ * @param string $locale
+ * @param array $expected_list
+ * @param array $expected_detail
+ * @return void
+ */
+ public function testListLocales(string $locale, array $expected_list, array $expected_detail): void
+ {
+ $locale_detail = \CoreLibs\Language\L10n::parseLocale($locale);
+ $this->assertEquals(
+ $expected_detail,
+ $locale_detail,
+ 'Parse local assert failed'
+ );
+ $locale_list = \CoreLibs\Language\L10n::listLocales($locale);
+ // print "LOCALES: " . print_r($locale_list, true) . "\n";
+ $this->assertEquals(
+ $expected_list,
+ $locale_list,
+ 'List locale assert failed'
+ );
+ }
+
+ // @covers ::detectLocale
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function detectLocaleProvider(): array
+ {
+ return [
+ // 0: type: global | env
+ // 1: global variable name or enviroment var
+ // 2: value to set
+ // 3: value to expect back
+ 'global locale' => [
+ 'global',
+ 'LOCALE',
+ 'ja_JP.UTF-8',
+ 'ja_JP.UTF-8',
+ ],
+ 'env LC_ALL' => [
+ 'env',
+ 'LC_ALL',
+ 'ja_JP.UTF-8',
+ 'ja_JP.UTF-8',
+ ],
+ 'env LANG' => [
+ 'env',
+ 'LANG',
+ 'ja_JP.UTF-8',
+ 'ja_JP.UTF-8',
+ ],
+ 'default return' => [
+ 'env',
+ 'LC_ALL',
+ '',
+ 'en',
+ ]
+ ];
+ }
+
+ /**
+ * Undocumented function
+ * @covers ::detectLocale
+ * @dataProvider detectLocaleProvider
+ * @testdox check detectLocale for $type with $var and $value is $expected [$_dataName]
+ *
+ * @return void
+ */
+ public function testDetectLocale(
+ string $type,
+ string $var,
+ string $value,
+ string $expected
+ ): void {
+ switch ($type) {
+ case 'global':
+ $GLOBALS[$var] = $value;
+ break;
+ case 'env':
+ $old_value = getenv("$var");
+ putenv("$var=$value");
+ // unset all other env vars
+ foreach (['LC_ALL', 'LC_MESSAGES', 'LANG'] as $env) {
+ if ($env != $var) {
+ putenv("$env=");
+ }
+ }
+ break;
+ }
+ $locale = \CoreLibs\Language\L10n::detectLocale();
+ $this->assertEquals(
+ $expected,
+ $locale
+ );
+ // reset post run
+ switch ($type) {
+ case 'global':
+ unset($GLOBALS[$var]);
+ break;
+ case 'env':
+ putenv("$var=$old_value");
+ break;
+ }
+ }
+
+ // set/get text domain, domain, locale
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function textDomainProvider(): array
+ {
+ return [
+ // 0: set domain
+ // 1: set path
+ // 2: get domain
+ // 3: expected path
+ 'valid set and get' => [
+ 'foo',
+ 'foo/bar',
+ 'foo',
+ 'foo/bar',
+ ],
+ 'invalid set and get' => [
+ 'foo',
+ 'foo/bar',
+ 'iamnotset',
+ false
+ ]
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @covers ::setTextDomain
+ * @covers ::getTextDomain
+ * @dataProvider textDomainProvider
+ * @testdox set $domain with $path and get $get_domain and expect $expected [$_dataName]
+ *
+ * @param string $domain
+ * @param string $path
+ * @param string $get_domain
+ * @param string|bool $expected
+ * @return void
+ */
+ public function testSetGetTextDomain(string $domain, string $path, string $get_domain, $expected): void
+ {
+ $l10n = new \CoreLibs\Language\L10n();
+ $l10n->setTextDomain($domain, $path);
+ $this->assertEquals(
+ $expected,
+ $l10n->getTextDomain($get_domain)
+ );
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function domainProvider(): array
+ {
+ return [
+ // 0: set domain
+ // 1: expected domain from get
+ 'valid domain' => [
+ 'foo',
+ 'foo',
+ ],
+ 'empty domain' => [
+ '',
+ '',
+ ]
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @covers ::setDomain
+ * @covers ::getDomain
+ * @dataProvider domainProvider
+ * @testdox set $domain and expect $expected [$_dataName]
+ *
+ * @param string $domain
+ * @param string $expected
+ * @return void
+ */
+ public function testSetGetDomain(string $domain, string $expected): void
+ {
+ $l10n = new \CoreLibs\Language\L10n();
+ $l10n->setDomain($domain);
+ $this->assertEquals(
+ $expected,
+ $l10n->getDomain()
+ );
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function localeProvider(): array
+ {
+ return [
+ // 0: set locale
+ // 1: pre set if not null or not empty
+ // 2: expected return from set
+ // 3: expected from get
+ 'valid locale' => [
+ 'foo',
+ null,
+ 'foo',
+ 'foo',
+ ],
+ 'empty locale' => [
+ '',
+ null,
+ '',
+ '',
+ ],
+ 'empty locale, pre set' => [
+ '',
+ 'foo',
+ 'foo',
+ 'foo',
+ ],
+ ];
+ }
+
+ /**
+ * Undocumented function
+ *
+ * @covers ::setLocale
+ * @covers ::getLocale
+ * @dataProvider localeProvider
+ * @testdox set $locale with $expected_return and expect $expected [$_dataName]
+ *
+ * @param string $locale
+ * @param string $pre_locale
+ * @param string $expected_return
+ * @param string $expected
+ * @return void
+ */
+ public function testSetGetLocale(
+ string $locale,
+ ?string $pre_locale,
+ string $expected_return,
+ string $expected
+ ): void {
+ $l10n = new \CoreLibs\Language\L10n();
+ if (!empty($pre_locale)) {
+ $l10n->setLocale($pre_locale);
+ }
+ $returned = $l10n->setLocale($locale);
+ $this->assertEquals(
+ $expected_return,
+ $returned,
+ 'Set locale return assert failed'
+ );
+ $this->assertEquals(
+ $expected,
+ $l10n->getLocale(),
+ 'Get locale aszert failed'
+ );
+ }
+
+ // static load
+
+ /**
+ * Undocumented function
+ *
+ * @return array
+ */
+ public function functionsProvider(): array
+ {
+ return [
+ // 0: lang/locale
+ // 1: domain
+ // 2: path
+ // 3: encoding
+ // 4: string
+ // 5: translated string
+ 'standard en' => [
+ 'en_US.UTF-8',
+ 'frontend',
+ __DIR__ . 'includes/locale/',
+ 'UTF-8',
+ 'Original',
+ 'Translated frontend en_US',
+ ],
+ 'standard ja' => [
+ 'ja_JP.UTF-8',
+ 'admin',
+ __DIR__ . 'includes/locale/',
+ 'UTF-8',
+ 'Original',
+ 'Translated admin ja_JP',
+ ]
+ ];
+ }
+
+ /**
+ * fuctions check
+ * TODO: others d/dn/dp/dnp gettext functions
+ *
+ * @covers __setlocale
+ * @covers __bindtextdomain
+ * @covers __bind_textdomain_codeset
+ * @covers __textdomain
+ * @covers __gettext
+ * @covers __
+ * @dataProvider functionsProvider
+ * @testdox check functions with locale $locale and domain $domain [$_dataName]
+ * @param string $locale
+ * @param string $domain
+ * @param string $path
+ * @param string $encoding
+ * @param string $original
+ * @param string $translated
+ * @return void
+ */
+ public function testFunctions(
+ string $locale,
+ string $domain,
+ string $path,
+ string $encoding,
+ string $original,
+ string $translated
+ ): void {
+ \CoreLibs\Language\L10n::loadFunctions();
+ _setlocale(LC_MESSAGES, $locale);
+ _textdomain($domain);
+ _bindtextdomain($domain, $path);
+ _bind_textdomain_codeset($domain, $encoding);
+ $this->assertEquals(
+ $translated,
+ __($original),
+ 'function __ assert failed'
+ );
+ $this->assertEquals(
+ $translated,
+ _gettext($original),
+ 'function gettext assert failed'
+ );
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsOutputFormElementsTest.php b/test/phpunit/CoreLibsOutputFormElementsTest.php
new file mode 100644
index 0000000..f228bfa
--- /dev/null
+++ b/test/phpunit/CoreLibsOutputFormElementsTest.php
@@ -0,0 +1,33 @@
+assertTrue(true, 'Output Form Elements Tests not implemented');
+ $this->markTestIncomplete(
+ 'Output\Form\Elements Tests have not yet been implemented'
+ );
+ // $this->markTestSkipped('No implementation for Output\Form\Elements at the moment');
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsOutputFormGenerateTest.php b/test/phpunit/CoreLibsOutputFormGenerateTest.php
new file mode 100644
index 0000000..40ba194
--- /dev/null
+++ b/test/phpunit/CoreLibsOutputFormGenerateTest.php
@@ -0,0 +1,33 @@
+assertTrue(true, 'Output Form Generate Tests not implemented');
+ $this->markTestIncomplete(
+ 'Output\Form\Generate Tests have not yet been implemented'
+ ); */
+ $this->markTestSkipped('No implementation for Output\Form\Generate at the moment');
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsOutputFormTokenTest.php b/test/phpunit/CoreLibsOutputFormTokenTest.php
new file mode 100644
index 0000000..1e3418b
--- /dev/null
+++ b/test/phpunit/CoreLibsOutputFormTokenTest.php
@@ -0,0 +1,33 @@
+assertTrue(true, 'Output Form Token Tests not implemented');
+ $this->markTestIncomplete(
+ 'Output\Form\Token Tests have not yet been implemented'
+ );
+ // $this->markTestSkipped('No implementation for Output\Form\Token at the moment');
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsOutputImageTest.php b/test/phpunit/CoreLibsOutputImageTest.php
new file mode 100644
index 0000000..5384093
--- /dev/null
+++ b/test/phpunit/CoreLibsOutputImageTest.php
@@ -0,0 +1,33 @@
+assertTrue(true, 'Output Image Tests not implemented');
+ $this->markTestIncomplete(
+ 'Output\Image Tests have not yet been implemented'
+ );
+ // $this->markTestSkipped('No implementation for Output\Image at the moment');
+ }
+}
+
+// __END__
diff --git a/test/phpunit/CoreLibsOutputProgressbarTest.php b/test/phpunit/CoreLibsOutputProgressbarTest.php
new file mode 100644
index 0000000..a31e122
--- /dev/null
+++ b/test/phpunit/CoreLibsOutputProgressbarTest.php
@@ -0,0 +1,33 @@
+assertTrue(true, 'Output Progressbar Tests not implemented');
+ // $this->markTestIncomplete(
+ // 'Output\Progressbar Tests have not yet been implemented'
+ // );
+ $this->markTestSkipped('No implementation for Output\Progressbar at the moment');
+ }
+}
+
+// __END__
diff --git a/test/phpunit/database/CoreLibsACLLogin_database_create_data.sql b/test/phpunit/database/CoreLibsACLLogin_database_create_data.sql
new file mode 100644
index 0000000..af0a5f6
--- /dev/null
+++ b/test/phpunit/database/CoreLibsACLLogin_database_create_data.sql
@@ -0,0 +1,1031 @@
+-- START: function/random_string.sql
+-- create random string with length X
+
+CREATE FUNCTION random_string(randomLength int)
+RETURNS text AS
+$$
+SELECT array_to_string(
+ ARRAY(
+ SELECT substring(
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
+ trunc(random() * 62)::int + 1,
+ 1
+ )
+ FROM generate_series(1, randomLength) AS gs(x)
+ ),
+ ''
+)
+$$
+LANGUAGE SQL
+RETURNS NULL ON NULL INPUT
+VOLATILE; -- LEAKPROOF;
+-- END: function/random_string.sql
+-- START: function/set_edit_generic.sql
+-- adds the created or updated date tags
+
+CREATE OR REPLACE FUNCTION set_edit_generic()
+RETURNS TRIGGER AS
+$$
+DECLARE
+ random_length INT = 12; -- that should be long enough
+BEGIN
+ IF TG_OP = 'INSERT' THEN
+ NEW.date_created := 'now';
+ NEW.cuid := random_string(random_length);
+ ELSIF TG_OP = 'UPDATE' THEN
+ NEW.date_updated := 'now';
+ END IF;
+ RETURN NEW;
+END;
+$$
+LANGUAGE 'plpgsql';
+-- END: function/set_edit_generic.sql
+-- START: function/edit_access_set_uid.sql
+-- add uid add for edit_access table
+
+CREATE OR REPLACE FUNCTION set_edit_access_uid() RETURNS TRIGGER AS
+$$
+DECLARE
+ myrec RECORD;
+ v_uid VARCHAR;
+BEGIN
+ -- skip if NEW.name is not set
+ IF NEW.name IS NOT NULL AND NEW.name <> '' THEN
+ -- use NEW.name as base, remove all spaces
+ -- name data is already unique, so we do not need to worry about this here
+ v_uid := REPLACE(NEW.name, ' ', '');
+ IF TG_OP = 'INSERT' THEN
+ -- always set
+ NEW.uid := v_uid;
+ ELSIF TG_OP = 'UPDATE' THEN
+ -- check if not set, then set
+ SELECT INTO myrec t.* FROM edit_access t WHERE edit_access_id = NEW.edit_access_id;
+ IF FOUND THEN
+ NEW.uid := v_uid;
+ END IF;
+ END IF;
+ END IF;
+ RETURN NEW;
+END;
+$$
+ LANGUAGE 'plpgsql';
+-- END: function/edit_access_set_uid.sql
+-- START: function/edit_group_set_uid.sql
+-- add uid add for edit_group table
+
+CREATE OR REPLACE FUNCTION set_edit_group_uid() RETURNS TRIGGER AS
+$$
+DECLARE
+ myrec RECORD;
+ v_uid VARCHAR;
+BEGIN
+ -- skip if NEW.name is not set
+ IF NEW.name IS NOT NULL AND NEW.name <> '' THEN
+ -- use NEW.name as base, remove all spaces
+ -- name data is already unique, so we do not need to worry about this here
+ v_uid := REPLACE(NEW.name, ' ', '');
+ IF TG_OP = 'INSERT' THEN
+ -- always set
+ NEW.uid := v_uid;
+ ELSIF TG_OP = 'UPDATE' THEN
+ -- check if not set, then set
+ SELECT INTO myrec t.* FROM edit_group t WHERE edit_group_id = NEW.edit_group_id;
+ IF FOUND THEN
+ NEW.uid := v_uid;
+ END IF;
+ END IF;
+ END IF;
+ RETURN NEW;
+END;
+$$
+ LANGUAGE 'plpgsql';
+-- END: function/edit_group_set_uid.sql
+-- START: function/edit_log_partition_insert.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2018-07-17
+-- DESCRIPTION:
+-- partition the edit_log table by year
+-- auto creates table if missing, if failure writes to overflow table
+-- HISTORY:
+
+CREATE OR REPLACE FUNCTION edit_log_insert_trigger ()
+RETURNS TRIGGER AS
+$$
+DECLARE
+ start_date DATE := '2010-01-01';
+ end_date DATE;
+ timeformat TEXT := 'YYYY';
+ selector TEXT := 'year';
+ base_table TEXT := 'edit_log';
+ _interval INTERVAL := '1 ' || selector;
+ _interval_next INTERVAL := '2 ' || selector;
+ table_name TEXT;
+ -- compare date column
+ compare_date DATE := NEW.event_date;
+ compare_date_name TEXT := 'event_date';
+ -- the create commands
+ command_create_table TEXT := 'CREATE TABLE IF NOT EXISTS {TABLE_NAME} (CHECK({COMPARE_DATE_NAME} >= {START_DATE} AND {COMPARE_DATE_NAME} < {END_DATE})) INHERITS ({BASE_NAME})';
+ command_create_primary_key TEXT := 'ALTER TABLE {TABLE_NAME} ADD PRIMARY KEY ({BASE_TABLE}_id)';
+ command_create_foreign_key_1 TEXT := 'ALTER TABLE {TABLE_NAME} ADD CONSTRAINT {TABLE_NAME}_euid_fkey FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL';
+ command_create_trigger_1 TEXT = 'CREATE TRIGGER trg_{TABLE_NAME} BEFORE INSERT OR UPDATE ON {TABLE_NAME} FOR EACH ROW EXECUTE PROCEDURE set_edit_generic()';
+BEGIN
+ -- we are in valid start time area
+ IF (NEW.event_date >= start_date) THEN
+ -- current table name
+ table_name := base_table || '_' || to_char(NEW.event_date, timeformat);
+ BEGIN
+ EXECUTE 'INSERT INTO ' || quote_ident(table_name) || ' SELECT ($1).*' USING NEW;
+ -- if insert failed because of missing table, create new below
+ EXCEPTION
+ WHEN undefined_table THEN
+ -- another block, so in case the creation fails here too
+ BEGIN
+ -- create new table here + all indexes
+ start_date := date_trunc(selector, NEW.event_date);
+ end_date := date_trunc(selector, NEW.event_date + _interval);
+ -- creat table
+ EXECUTE format(REPLACE( -- end date
+ REPLACE( -- start date
+ REPLACE( -- compare date name
+ REPLACE( -- base name (inherit)
+ REPLACE( -- table name
+ command_create_table,
+ '{TABLE_NAME}',
+ table_name
+ ),
+ '{BASE_NAME}',
+ base_table
+ ),
+ '{COMPARE_DATE_NAME}',
+ compare_date_name
+ ),
+ '{START_DATE}',
+ quote_literal(start_date)
+ ),
+ '{END_DATE}',
+ quote_literal(end_date)
+ ));
+ -- create all indexes and triggers
+ EXECUTE format(REPLACE(
+ REPLACE(
+ command_create_primary_key,
+ '{TABLE_NAME}',
+ table_name
+ ),
+ '{BASE_TABLE}',
+ base_table
+ ));
+ -- FK constraints
+ EXECUTE format(REPLACE(command_create_foreign_key_1, '{TABLE_NAME}', table_name));
+ -- generic trigger
+ EXECUTE format(REPLACE(command_create_trigger_1, '{TABLE_NAME}', table_name));
+
+ -- insert try again
+ EXECUTE 'INSERT INTO ' || quote_ident(table_name) || ' SELECT ($1).*' USING NEW;
+ EXCEPTION
+ WHEN OTHERS THEN
+ -- if this faled, throw it into the overflow table (so we don't loose anything)
+ INSERT INTO edit_log_overflow VALUES (NEW.*);
+ END;
+ -- other errors, insert into overlow
+ WHEN OTHERS THEN
+ -- if this faled, throw it into the overflow table (so we don't loose anything)
+ INSERT INTO edit_log_overflow VALUES (NEW.*);
+ END;
+ -- main insert run done, check if we have to create next months table
+ BEGIN
+ -- check if next month table exists
+ table_name := base_table || '_' || to_char((SELECT NEW.event_date + _interval)::DATE, timeformat);
+ -- RAISE NOTICE 'SEARCH NEXT: %', table_name;
+ IF (SELECT to_regclass(table_name)) IS NULL THEN
+ -- move inner interval same
+ start_date := date_trunc(selector, NEW.event_date + _interval);
+ end_date := date_trunc(selector, NEW.event_date + _interval_next);
+ -- RAISE NOTICE 'CREATE NEXT: %', table_name;
+ -- create table
+ EXECUTE format(REPLACE( -- end date
+ REPLACE( -- start date
+ REPLACE( -- compare date name
+ REPLACE( -- base name (inherit)
+ REPLACE( -- table name
+ command_create_table,
+ '{TABLE_NAME}',
+ table_name
+ ),
+ '{BASE_NAME}',
+ base_table
+ ),
+ '{COMPARE_DATE_NAME}',
+ compare_date_name
+ ),
+ '{START_DATE}',
+ quote_literal(start_date)
+ ),
+ '{END_DATE}',
+ quote_literal(end_date)
+ ));
+ -- create all indexes and triggers
+ EXECUTE format(REPLACE(
+ REPLACE(
+ command_create_primary_key,
+ '{TABLE_NAME}',
+ table_name
+ ),
+ '{BASE_TABLE}',
+ base_table
+ ));
+ -- FK constraints
+ EXECUTE format(REPLACE(command_create_foreign_key_1, '{TABLE_NAME}', table_name));
+ -- generic trigger
+ EXECUTE format(REPLACE(command_create_trigger_1, '{TABLE_NAME}', table_name));
+ END IF;
+ EXCEPTION
+ WHEN OTHERS THEN
+ RAISE NOTICE 'Failed to create next table: %', table_name;
+ END;
+ ELSE
+ -- if outside valid date, insert into overflow
+ INSERT INTO edit_log_overflow VALUES (NEW.*);
+ END IF;
+ RETURN NULL;
+END
+$$
+LANGUAGE 'plpgsql';
+-- END: function/edit_log_partition_insert.sql
+-- START: function/edit_user_set_login_user_id_set_date.sql
+-- set edit user login_user_id_set_date if login_user_id is set
+-- NOW() if not empty
+
+CREATE OR REPLACE FUNCTION set_login_user_id_set_date()
+RETURNS TRIGGER AS
+$$
+BEGIN
+ -- if new is not null/empty
+ -- and old one is null or old one different new one
+ -- set NOW()
+ -- if new one is NULL
+ -- set NULL
+ IF
+ NEW.login_user_id IS NOT NULL AND NEW.login_user_id <> '' AND
+ (OLD.login_user_id IS NULL OR NEW.login_user_id <> OLD.login_user_id)
+ THEN
+ NEW.login_user_id_set_date = NOW();
+ NEW.login_user_id_last_revalidate = NOW();
+ ELSIF NEW.login_user_id IS NULL OR NEW.login_user_id = '' THEN
+ NEW.login_user_id_set_date = NULL;
+ NEW.login_user_id_last_revalidate = NULL;
+ END IF;
+ RETURN NEW;
+END;
+$$
+LANGUAGE 'plpgsql';
+-- END: function/edit_user_set_login_user_id_set_date.sql
+-- START: table/edit_temp_files.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/08
+-- DESCRIPTION:
+-- edit interface temporary files, list of all files in edit (admin) directory
+-- TABLE: temp_files
+-- HISTORY:
+
+-- DROP TABLE temp_files;
+CREATE TABLE temp_files (
+ filename VARCHAR,
+ folder VARCHAR
+);
+-- END: table/edit_temp_files.sql
+-- START: table/edit_generic.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- edit tables, this is the generic table, inheriteded by most edit tables
+-- TABLE: edit_generic
+-- HISTORY:
+
+-- DROP TABLE edit_generic;
+CREATE TABLE edit_generic (
+ cuid VARCHAR,
+ date_created TIMESTAMP WITHOUT TIME ZONE DEFAULT clock_timestamp(),
+ date_updated TIMESTAMP WITHOUT TIME ZONE
+);
+-- END: table/edit_generic.sql
+-- START: table/edit_visible_group.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- edit tables, postgres SQL statements for the mysql definitions
+-- TABLE: edit_visible_group
+-- HISTORY
+
+-- DROP TABLE edit_visible_group;
+CREATE TABLE edit_visible_group (
+ edit_visible_group_id SERIAL PRIMARY KEY,
+ name VARCHAR,
+ flag VARCHAR
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_visible_group.sql
+-- START: table/edit_menu_group.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- edit tables, groupings for menu
+-- TABLE: edit_menu_group
+-- HISTORY
+
+-- DROP TABLE edit_menu_group;
+CREATE TABLE edit_menu_group (
+ edit_menu_group_id SERIAL PRIMARY KEY,
+ name VARCHAR,
+ flag VARCHAR,
+ order_number INT NOT NULL
+) INHERITS (edit_generic) WITHOUT OIDS;
+
+
+-- END: table/edit_menu_group.sql
+-- START: table/edit_page.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- edit tables, this table contains all pages in the edit interface and allocates rights + values to it
+-- TABLE: edit_page
+-- HISTORY:
+
+-- DROP TABLE edit_page;
+CREATE TABLE edit_page (
+ edit_page_id SERIAL PRIMARY KEY,
+ content_alias_edit_page_id INT, -- alias for page content, if the page content is defined on a different page, ege for ajax backend pages
+ FOREIGN KEY (content_alias_edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE RESTRICT ON UPDATE CASCADE,
+ filename VARCHAR,
+ name VARCHAR UNIQUE,
+ order_number INT NOT NULL,
+ online SMALLINT NOT NULL DEFAULT 0,
+ menu SMALLINT NOT NULL DEFAULT 0,
+ popup SMALLINT NOT NULL DEFAULT 0,
+ popup_x SMALLINT,
+ popup_y SMALLINT,
+ hostname VARCHAR
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_page.sql
+-- START: table/edit_query_string.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- edit tables
+-- TABLE: edit_query_string
+-- HISTORY:
+
+-- DROP TABLE edit_query_string;
+CREATE TABLE edit_query_string (
+ edit_query_string_id SERIAL PRIMARY KEY,
+ edit_page_id INT NOT NULL,
+ FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ enabled SMALLINT NOT NULL DEFAULT 0,
+ name VARCHAR,
+ value VARCHAR,
+ dynamic SMALLINT NOT NULL DEFAULT 0
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_query_string.sql
+-- START: table/edit_page_visible_group.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- reference table between visible groups and pages
+-- TABLE: edit_page_visible_group
+-- HISTORY:
+
+-- DROP TABLE edit_page_visible_group;
+CREATE TABLE edit_page_visible_group (
+ edit_page_id INT NOT NULL,
+ FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_visible_group_id INT NOT NULL,
+ FOREIGN KEY (edit_visible_group_id) REFERENCES edit_visible_group (edit_visible_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE
+);
+-- END: table/edit_page_visible_group.sql
+-- START: table/edit_page_menu_group.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- reference table between menu groups and pages
+-- TABLE: edit_page_menu_group
+-- HISTORY:
+
+-- DROP TABLE edit_page_menu_group;
+CREATE TABLE edit_page_menu_group (
+ edit_page_id INT NOT NULL,
+ FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_menu_group_id INT NOT NULL,
+ FOREIGN KEY (edit_menu_group_id) REFERENCES edit_menu_group (edit_menu_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE
+);
+-- END: table/edit_page_menu_group.sql
+-- START: table/edit_access_right.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- holds all access right levels for the edit interface and other access areas
+-- this table is fixed, prefilled and not changable
+-- TABLE: edit_access_right
+-- HISTORY:
+
+-- DROP TABLE edit_access_right;
+CREATE TABLE edit_access_right (
+ edit_access_right_id SERIAL PRIMARY KEY,
+ name VARCHAR,
+ level SMALLINT,
+ type VARCHAR,
+ UNIQUE (level,type)
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_access_right.sql
+-- START: table/edit_scheme.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- holds backend template schemes
+-- TABLE: edit_scheme
+-- HISTORY:
+
+-- DROP TABLE edit_scheme;
+CREATE TABLE edit_scheme (
+ edit_scheme_id SERIAL PRIMARY KEY,
+ enabled SMALLINT NOT NULL DEFAULT 0,
+ name VARCHAR,
+ header_color VARCHAR,
+ css_file VARCHAR,
+ template VARCHAR
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_scheme.sql
+-- START: table/edit_language.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- languages for the backend, this not used for the encoding, but only for having different language strings
+-- the backend encoding is all UTF-8 (not changeable)
+-- TABLE: edit_language
+-- HISTORY:
+
+-- DROP TABLE edit_language;
+CREATE TABLE edit_language (
+ edit_language_id SERIAL PRIMARY KEY,
+ enabled SMALLINT NOT NULL DEFAULT 0,
+ lang_default SMALLINT NOT NULL DEFAULT 0,
+ long_name VARCHAR,
+ short_name VARCHAR, -- en_US, en or en_US@latin without encoding
+ iso_name VARCHAR, -- should actually be encoding
+ order_number INT
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_language.sql
+-- START: table/edit_group.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- list of pages the user can access, with a generic access level, one group per user
+-- TABLE: edit_group
+-- HISTORY:
+
+-- DROP TABLE edit_group;
+CREATE TABLE edit_group (
+ edit_group_id SERIAL PRIMARY KEY,
+ edit_scheme_id INT,
+ FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_access_right_id INT NOT NULL,
+ FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ enabled SMALLINT NOT NULL DEFAULT 0,
+ deleted SMALLINT DEFAULT 0,
+ uid VARCHAR,
+ name VARCHAR,
+ additional_acl JSONB
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_group.sql
+-- START: table/edit_page_access.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- groups pages together to one page group to which the user is then subscribed
+-- TABLE: edit_page_access
+-- HISTORY:
+
+-- DROP TABLE edit_page_access;
+CREATE TABLE edit_page_access (
+ edit_page_access_id SERIAL PRIMARY KEY,
+ edit_group_id INT NOT NULL,
+ FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_page_id INT NOT NULL,
+ FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_access_right_id INT NOT NULL,
+ FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ enabled SMALLINT NOT NULL DEFAULT 0
+) INHERITS (edit_generic) WITHOUT OIDS;
+
+
+-- END: table/edit_page_access.sql
+-- START: table/edit_page_content.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2019/9/9
+-- DESCRIPTION:
+-- sub content to one page with additional edit access right set
+-- can be eg JS content groups on one page
+-- TABLE: edit_page_content
+-- HISTORY:
+
+-- DROP TABLE edit_page_content;
+CREATE TABLE edit_page_content (
+ edit_page_content_id SERIAL PRIMARY KEY,
+ edit_page_id INT NOT NULL,
+ FOREIGN KEY (edit_page_id) REFERENCES edit_page (edit_page_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_access_right_id INT NOT NULL,
+ FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ uid VARCHAR UNIQUE,
+ name VARCHAR,
+ order_number INT NOT NULL,
+ online SMALLINT NOT NULL DEFAULT 0
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_page_content.sql
+-- START: table/edit_user.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/06
+-- DESCRIPTION:
+-- holds the user that can login + group, scheme, lang and a default access right
+-- TABLE: edit_user
+-- HISTORY:
+
+-- DROP TABLE edit_user;
+CREATE TABLE edit_user (
+ edit_user_id SERIAL PRIMARY KEY,
+ connect_edit_user_id INT, -- possible reference to other user
+ FOREIGN KEY (connect_edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_language_id INT NOT NULL,
+ FOREIGN KEY (edit_language_id) REFERENCES edit_language (edit_language_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_group_id INT NOT NULL,
+ FOREIGN KEY (edit_group_id) REFERENCES edit_group (edit_group_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_scheme_id INT,
+ FOREIGN KEY (edit_scheme_id) REFERENCES edit_scheme (edit_scheme_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_access_right_id INT NOT NULL,
+ FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ -- username/password
+ username VARCHAR UNIQUE,
+ password VARCHAR,
+ -- name block
+ first_name VARCHAR,
+ last_name VARCHAR,
+ first_name_furigana VARCHAR,
+ last_name_furigana VARCHAR,
+ -- email
+ email VARCHAR,
+ -- eanbled/deleted flag
+ enabled SMALLINT NOT NULL DEFAULT 0,
+ deleted SMALLINT NOT NULL DEFAULT 0,
+ -- general flags
+ strict SMALLINT DEFAULT 0,
+ locked SMALLINT DEFAULT 0,
+ protected SMALLINT NOT NULL DEFAULT 0,
+ -- legacy, debug flags
+ debug SMALLINT NOT NULL DEFAULT 0,
+ db_debug SMALLINT NOT NULL DEFAULT 0,
+ -- is admin user
+ admin SMALLINT NOT NULL DEFAULT 0,
+ -- last login log
+ last_login TIMESTAMP WITHOUT TIME ZONE,
+ -- login error
+ login_error_count INT DEFAULT 0,
+ login_error_date_last TIMESTAMP WITHOUT TIME ZONE,
+ login_error_date_first TIMESTAMP WITHOUT TIME ZONE,
+ -- time locked
+ lock_until TIMESTAMP WITHOUT TIME ZONE,
+ lock_after TIMESTAMP WITHOUT TIME ZONE,
+ -- password change
+ password_change_date TIMESTAMP WITHOUT TIME ZONE, -- only when password is first set or changed
+ password_change_interval INTERVAL, -- null if no change is needed, or d/m/y time interval
+ password_reset_time TIMESTAMP WITHOUT TIME ZONE, -- when the password reset was requested
+ password_reset_uid VARCHAR, -- the uid to access the password reset page
+ -- _GET login id for direct login
+ login_user_id VARCHAR UNIQUE, -- the loginUserId, at least 32 chars
+ login_user_id_set_date TIMESTAMP WITHOUT TIME ZONE, -- when above uid was set
+ login_user_id_last_revalidate TIMESTAMP WITHOUT TIME ZONE, -- when the last login was done with user name and password
+ login_user_id_valid_from TIMESTAMP WITHOUT TIME ZONE, -- if set, from when the above uid is valid
+ login_user_id_valid_until TIMESTAMP WITHOUT TIME ZONE, -- if set, until when the above uid is valid
+ login_user_id_revalidate_after INTERVAL, -- user must login to revalidated loginUserId after set days, 0 for forever
+ login_user_id_locked SMALLINT DEFAULT 0, -- lock for loginUserId, but still allow normal login
+ -- additional ACL json block
+ additional_acl JSONB -- additional ACL as JSON string (can be set by other pages)
+) INHERITS (edit_generic) WITHOUT OIDS;
+
+-- create unique index
+-- CREATE UNIQUE INDEX edit_user_login_user_id_key ON edit_user (login_user_id) WHERE login_user_id IS NOT NULL;
+
+COMMENT ON COLUMN edit_user.username IS 'Login username, must set';
+COMMENT ON COLUMN edit_user.password IS 'Login password, must set';
+COMMENT ON COLUMN edit_user.enabled IS 'Login is enabled (master switch)';
+COMMENT ON COLUMN edit_user.deleted IS 'Login is deleted (master switch), overrides all other';
+COMMENT ON COLUMN edit_user.strict IS 'If too many failed logins user will be locked, default off';
+COMMENT ON COLUMN edit_user.locked IS 'Locked from too many wrong password logins';
+COMMENT ON COLUMN edit_user.protected IS 'User can only be chnaged by admin user';
+COMMENT ON COLUMN edit_user.debug IS 'Turn debug flag on (legacy)';
+COMMENT ON COLUMN edit_user.db_debug IS 'Turn DB debug flag on (legacy)';
+COMMENT ON COLUMN edit_user.admin IS 'If set, this user is SUPER admin';
+COMMENT ON COLUMN edit_user.last_login IS 'Last succesfull login tiemstamp';
+COMMENT ON COLUMN edit_user.login_error_count IS 'Number of failed logins, reset on successful login';
+COMMENT ON COLUMN edit_user.login_error_date_last IS 'Last login error date';
+COMMENT ON COLUMN edit_user.login_error_date_first IS 'First login error date, reset on successfull login';
+COMMENT ON COLUMN edit_user.lock_until IS 'Account is locked until this date, <';
+COMMENT ON COLUMN edit_user.lock_after IS 'Account is locked after this date, >';
+COMMENT ON COLUMN edit_user.password_change_date IS 'Password was changed on';
+COMMENT ON COLUMN edit_user.password_change_interval IS 'After how many days the password has to be changed';
+COMMENT ON COLUMN edit_user.password_reset_time IS 'When the password reset was requested. For reset page uid valid check';
+COMMENT ON COLUMN edit_user.password_reset_uid IS 'Password reset page uid, one time, invalid after reset successful or time out';
+COMMENT ON COLUMN edit_user.login_user_id IS 'Min 32 character UID to be used to login without password. Via GET/POST parameter';
+COMMENT ON COLUMN edit_user.login_user_id_set_date IS 'loginUserId was set at what date';
+COMMENT ON COLUMN edit_user.login_user_id_last_revalidate IS 'set when username/password login is done and loginUserId is set';
+COMMENT ON COLUMN edit_user.login_user_id_valid_from IS 'loginUserId is valid from this date, >=';
+COMMENT ON COLUMN edit_user.login_user_id_valid_until IS 'loginUserId is valid until this date, <=';
+COMMENT ON COLUMN edit_user.login_user_id_revalidate_after IS 'If set to a number greater 0 then user must login after given amount of days to revalidate the loginUserId, set to 0 for valid forver';
+COMMENT ON COLUMN edit_user.login_user_id_locked IS 'A separte lock flag for loginUserId, user can still login normal';
+COMMENT ON COLUMN edit_user.additional_acl IS 'Additional Access Control List stored in JSON format';
+-- END: table/edit_user.sql
+-- START: table/edit_log.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- log data for backend interface, logs all user activities
+-- TABLE: edit_log
+-- HISTORY:
+
+-- DROP TABLE edit_log;
+CREATE TABLE edit_log (
+ edit_log_id SERIAL PRIMARY KEY,
+ euid INT, -- this is a foreign key, but I don't nedd to reference to it
+ FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL,
+ username VARCHAR,
+ password VARCHAR,
+ event_date TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP,
+ ip VARCHAR,
+ error TEXT,
+ event TEXT,
+ data_binary BYTEA,
+ data TEXT,
+ page VARCHAR,
+ action VARCHAR,
+ action_id VARCHAR,
+ action_yes VARCHAR,
+ action_flag VARCHAR,
+ action_menu VARCHAR,
+ action_loaded VARCHAR,
+ action_value VARCHAR,
+ action_type VARCHAR,
+ action_error VARCHAR,
+ user_agent VARCHAR,
+ referer VARCHAR,
+ script_name VARCHAR,
+ query_string VARCHAR,
+ server_name VARCHAR,
+ http_host VARCHAR,
+ http_accept VARCHAR,
+ http_accept_charset VARCHAR,
+ http_accept_encoding VARCHAR,
+ session_id VARCHAR
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_log.sql
+-- START: table/edit_log_overflow.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2020/1/28
+-- DESCRIPTION:
+-- edit log overflow table
+-- this is the overflow table for partition
+-- TABLE: edit_log_overflow
+-- HISTORY:
+
+-- DROP TABLE edit_log_overflow;
+CREATE TABLE IF NOT EXISTS edit_log_overflow () INHERITS (edit_log);
+ALTER TABLE edit_log_overflow ADD PRIMARY KEY (edit_log_id);
+ALTER TABLE edit_log_overflow ADD CONSTRAINT edit_log_overflow_euid_fkey FOREIGN KEY (euid) REFERENCES edit_user (edit_user_id) MATCH FULL ON UPDATE CASCADE ON DELETE SET NULL;
+-- END: table/edit_log_overflow.sql
+-- START: table/edit_access.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- is a "group" for the outside, a user can have serveral groups with different rights so he can access several parts from the outside
+-- TABLE: edit_access
+-- HISTORY:
+
+-- DROP TABLE edit_access;
+CREATE TABLE edit_access (
+ edit_access_id SERIAL PRIMARY KEY,
+ enabled SMALLINT NOT NULL DEFAULT 0,
+ protected SMALLINT DEFAULT 0,
+ deleted SMALLINT DEFAULT 0,
+ uid VARCHAR,
+ name VARCHAR UNIQUE,
+ description VARCHAR,
+ color VARCHAR,
+ additional_acl JSONB
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_access.sql
+-- START: table/edit_access_user.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2005/07/05
+-- DESCRIPTION:
+-- groupings which user has rights to which access groups (incl ACL)
+-- TABLE: edit_access_user
+-- HISTORY:
+
+-- DROP TABLE edit_access_user;
+CREATE TABLE edit_access_user (
+ edit_access_user_id SERIAL PRIMARY KEY,
+ edit_access_id INT NOT NULL,
+ FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_user_id INT NOT NULL,
+ FOREIGN KEY (edit_user_id) REFERENCES edit_user (edit_user_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_access_right_id INT NOT NULL,
+ FOREIGN KEY (edit_access_right_id) REFERENCES edit_access_right (edit_access_right_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ edit_default SMALLINT DEFAULT 0,
+ enabled SMALLINT NOT NULL DEFAULT 0
+) INHERITS (edit_generic) WITHOUT OIDS;
+-- END: table/edit_access_user.sql
+-- START: table/edit_access_data.sql
+-- AUTHOR: Clemens Schwaighofer
+-- DATE: 2016/7/15
+-- DESCRIPTION:
+-- sub table to edit access, holds additional data for access group
+-- TABLE: edit_access_data
+-- HISTORY:
+
+-- DROP TABLE edit_access_data;
+CREATE TABLE edit_access_data (
+ edit_access_data_id SERIAL PRIMARY KEY,
+ edit_access_id INT NOT NULL,
+ FOREIGN KEY (edit_access_id) REFERENCES edit_access (edit_access_id) MATCH FULL ON DELETE CASCADE ON UPDATE CASCADE,
+ enabled SMALLINT NOT NULL DEFAULT 0,
+ name VARCHAR,
+ value VARCHAR
+) INHERITS (edit_generic) WITHOUT OIDS;
+
+-- create a unique index for each attached data block for each edit access can
+-- only have ONE value;
+CREATE UNIQUE INDEX edit_access_data_edit_access_id_name_ukey ON edit_access_data (edit_access_id, name);
+-- END: table/edit_access_data.sql
+-- START: trigger/trg_edit_access_right.sql
+-- DROP TRIGGER IF EXISTS trg_edit_access_right ON edit_access_right;
+CREATE TRIGGER trg_edit_access_right
+BEFORE INSERT OR UPDATE ON edit_access_right
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_access_right.sql
+-- START: trigger/trg_edit_access.sql
+-- DROP TRIGGER IF EXISTS trg_edit_access ON edit_access;
+CREATE TRIGGER trg_edit_access
+BEFORE INSERT OR UPDATE ON edit_access
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+
+-- DROP TRIGGER IF EXISTS trg_set_edit_access_uid ON edit_access;
+CREATE TRIGGER trg_set_edit_access_uid
+BEFORE INSERT OR UPDATE ON edit_access
+FOR EACH ROW EXECUTE PROCEDURE set_edit_access_uid();
+-- END: trigger/trg_edit_access.sql
+-- START: trigger/trg_edit_access_data.sql
+-- DROP TRIGGER IF EXISTS trg_edit_access_data ON edit_access_data;
+CREATE TRIGGER trg_edit_access_data
+BEFORE INSERT OR UPDATE ON edit_access_data
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_access_data.sql
+-- START: trigger/trg_edit_access_user.sql
+-- DROP TRIGGER IF EXISTS trg_edit_access_user ON edit_access_user;
+CREATE TRIGGER trg_edit_access_user
+BEFORE INSERT OR UPDATE ON edit_access_user
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_access_user.sql
+-- START: trigger/trg_edit_group.sql
+-- DROP TRIGGER IF EXISTS trg_edit_group ON edit_group;
+CREATE TRIGGER trg_edit_group
+BEFORE INSERT OR UPDATE ON edit_group
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+
+-- DROP TRIGGER IF EXISTS trg_set_edit_group_uid ON edit_group;
+CREATE TRIGGER trg_set_edit_group_uid
+BEFORE INSERT OR UPDATE ON edit_group
+FOR EACH ROW EXECUTE PROCEDURE set_edit_group_uid();
+-- END: trigger/trg_edit_group.sql
+-- START: trigger/trg_edit_language.sql
+-- DROP TRIGGER IF EXISTS trg_edit_language ON edit_language;
+CREATE TRIGGER trg_edit_language
+BEFORE INSERT OR UPDATE ON edit_language
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_language.sql
+-- START: trigger/trg_edit_log_overflow.sql
+-- DROP TRIGGER IF EXISTS trg_edit_log_overflow ON edit_log_overflow;
+CREATE TRIGGER trg_edit_log_overflow
+BEFORE INSERT OR UPDATE ON edit_log_overflow
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_log_overflow.sql
+-- START: trigger/trg_edit_log.sql
+-- DROP TRIGGER IF EXISTS trg_edit_log ON edit_log;
+CREATE TRIGGER trg_edit_log
+BEFORE INSERT OR UPDATE ON edit_log
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+
+-- DROP TRIGGER IF EXISTS trg_edit_log_insert_partition ON edit_log;
+CREATE TRIGGER trg_edit_log_insert_partition
+BEFORE INSERT OR UPDATE ON edit_log
+FOR EACH ROW EXECUTE PROCEDURE edit_log_insert_trigger();
+-- END: trigger/trg_edit_log.sql
+-- START: trigger/trg_edit_page_access.sql
+-- DROP TRIGGER IF EXISTS trg_edit_page_access ON edit_page_access;
+CREATE TRIGGER trg_edit_page_access
+BEFORE INSERT OR UPDATE ON edit_page_access
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_page_access.sql
+-- START: trigger/trg_edit_page_content.sql
+-- DROP TRIGGER IF EXISTS trg_edit_page_content ON edit_page_content;
+CREATE TRIGGER trg_edit_page_content
+BEFORE INSERT OR UPDATE ON edit_page_content
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_page_content.sql
+-- START: trigger/trg_edit_page.sql
+-- DROP TRIGGER IF EXISTS trg_edit_page ON edit_page;
+CREATE TRIGGER trg_edit_page
+BEFORE INSERT OR UPDATE ON edit_page
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_page.sql
+-- START: trigger/trg_edit_query_string.sql
+-- DROP TRIGGER IF EXISTS trg_edit_query_string ON edit_query_string;
+CREATE TRIGGER trg_edit_query_string
+BEFORE INSERT OR UPDATE ON edit_query_string
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_query_string.sql
+-- START: trigger/trg_edit_scheme.sql
+-- DROP TRIGGER IF EXISTS trg_edit_scheme ON edit_scheme;
+CREATE TRIGGER trg_edit_scheme
+BEFORE INSERT OR UPDATE ON edit_scheme
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_scheme.sql
+-- START: trigger/trg_edit_user.sql
+-- DROP TRIGGER IF EXISTS trg_edit_user ON edit_user;
+CREATE TRIGGER trg_edit_user
+BEFORE INSERT OR UPDATE ON edit_user
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+
+-- DROP TRIGGER IF EXISTS trg_edit_user_set_login_user_id_set_date ON edit_user;
+CREATE TRIGGER trg_edit_user_set_login_user_id_set_date
+BEFORE INSERT OR UPDATE ON edit_user
+FOR EACH ROW EXECUTE PROCEDURE set_login_user_id_set_date();
+-- END: trigger/trg_edit_user.sql
+-- START: trigger/trg_edit_visible_group.sql
+-- DROP TRIGGER IF EXISTS trg_edit_visible_group ON edit_visible_group;
+CREATE TRIGGER trg_edit_visible_group
+BEFORE INSERT OR UPDATE ON edit_visible_group
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_visible_group.sql
+-- START: trigger/trg_edit_menu_group.sql
+-- DROP TRIGGER IF EXISTS trg_edit_menu_group ON edit_menu_group;
+CREATE TRIGGER trg_edit_menu_group
+BEFORE INSERT OR UPDATE ON edit_menu_group
+FOR EACH ROW EXECUTE PROCEDURE set_edit_generic();
+-- END: trigger/trg_edit_menu_group.sql
+-- START: data/edit_tables.sql
+-- edit tables insert data in order
+
+-- edit visible group
+DELETE FROM edit_visible_group;
+INSERT INTO edit_visible_group (name, flag) VALUES ('Main Menu', 'main');
+INSERT INTO edit_visible_group (name, flag) VALUES ('Data popup Menu', 'datapopup');
+
+-- edit menu group
+DELETE FROM edit_menu_group;
+INSERT INTO edit_menu_group (name, flag, order_number) VALUES ('Admin Menu', 'admin', 1);
+INSERT INTO edit_menu_group (name, flag, order_number) VALUES ('Admin Data Popup Menu', 'AdminDataPopup', 2);
+
+-- edit page
+DELETE FROM edit_page;
+INSERT INTO edit_page (filename, name, order_number, online, menu) VALUES ('edit_pages.php', 'Edit Pages', 1, 1, 1);
+INSERT INTO edit_page (filename, name, order_number, online, menu) VALUES ('edit_users.php', 'Edit Users', 2, 1, 1);
+INSERT INTO edit_page (filename, name, order_number, online, menu) VALUES ('edit_languages.php', 'Edit Languages', 3, 1, 1);
+INSERT INTO edit_page (filename, name, order_number, online, menu) VALUES ('edit_schemes.php', 'Edit Schemes', 4, 1, 1);
+INSERT INTO edit_page (filename, name, order_number, online, menu) VALUES ('edit_groups.php', 'Edit Groups', 5, 1, 1);
+INSERT INTO edit_page (filename, name, order_number, online, menu) VALUES ('edit_visible_group.php', 'Edit Visible Groups', 6, 1, 1);
+INSERT INTO edit_page (filename, name, order_number, online, menu) VALUES ('edit_menu_group.php', 'Edit Menu Groups', 7, 1, 1);
+INSERT INTO edit_page (filename, name, order_number, online, menu) VALUES ('edit_access.php', 'Edit Access', 8, 1, 1);
+INSERT INTO edit_page (filename, name, order_number, online, menu) VALUES ('edit_order.php', 'Edit Order', 9, 1, 0);
+
+-- edit visible group
+DELETE FROM edit_page_visible_group;
+INSERT INTO edit_page_visible_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Pages'), (SELECT edit_visible_group_id FROM edit_visible_group WHERE flag = 'main'));
+INSERT INTO edit_page_visible_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Users'), (SELECT edit_visible_group_id FROM edit_visible_group WHERE flag = 'main'));
+INSERT INTO edit_page_visible_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Languages'), (SELECT edit_visible_group_id FROM edit_visible_group WHERE flag = 'main'));
+INSERT INTO edit_page_visible_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Schemes'), (SELECT edit_visible_group_id FROM edit_visible_group WHERE flag = 'main'));
+INSERT INTO edit_page_visible_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Groups'), (SELECT edit_visible_group_id FROM edit_visible_group WHERE flag = 'main'));
+INSERT INTO edit_page_visible_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Visible Groups'), (SELECT edit_visible_group_id FROM edit_visible_group WHERE flag = 'main'));
+INSERT INTO edit_page_visible_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Menu Groups'), (SELECT edit_visible_group_id FROM edit_visible_group WHERE flag = 'main'));
+INSERT INTO edit_page_visible_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Access'), (SELECT edit_visible_group_id FROM edit_visible_group WHERE flag = 'main'));
+-- INSERT INTO edit_page_visible_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Order'), (SELECT edit_visible_group_id FROM edit_visible_group WHERE flag = 'main'));
+
+-- edit page menu group
+DELETE FROM edit_page_menu_group;
+INSERT INTO edit_page_menu_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Pages'), (SELECT edit_menu_group_id FROM edit_menu_group WHERE flag = 'admin'));
+INSERT INTO edit_page_menu_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Users'), (SELECT edit_menu_group_id FROM edit_menu_group WHERE flag = 'admin'));
+INSERT INTO edit_page_menu_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Languages'), (SELECT edit_menu_group_id FROM edit_menu_group WHERE flag = 'admin'));
+INSERT INTO edit_page_menu_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Schemes'), (SELECT edit_menu_group_id FROM edit_menu_group WHERE flag = 'admin'));
+INSERT INTO edit_page_menu_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Groups'), (SELECT edit_menu_group_id FROM edit_menu_group WHERE flag = 'admin'));
+INSERT INTO edit_page_menu_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Visible Groups'), (SELECT edit_menu_group_id FROM edit_menu_group WHERE flag = 'admin'));
+INSERT INTO edit_page_menu_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Menu Groups'), (SELECT edit_menu_group_id FROM edit_menu_group WHERE flag = 'admin'));
+INSERT INTO edit_page_menu_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Access'), (SELECT edit_menu_group_id FROM edit_menu_group WHERE flag = 'admin'));
+-- INSERT INTO edit_page_menu_group VALUES ((SELECT edit_page_id FROM edit_page WHERE name = 'Edit Order'), (SELECT edit_menu_group_id FROM edit_menu_group WHERE flag = 'admin'));
+
+
+-- edit access right
+DELETE FROM edit_access_right;
+INSERT INTO edit_access_right (name, level, type) VALUES ('Default', -1, 'default');
+INSERT INTO edit_access_right (name, level, type) VALUES ('No Access', 0, 'none');
+INSERT INTO edit_access_right (name, level, type) VALUES ('List', 10, 'list');
+INSERT INTO edit_access_right (name, level, type) VALUES ('Read', 20, 'read');
+INSERT INTO edit_access_right (name, level, type) VALUES ('Translator', 30, 'mod_trans');
+INSERT INTO edit_access_right (name, level, type) VALUES ('Modify', 40, 'mod');
+INSERT INTO edit_access_right (name, level, type) VALUES ('Create/Write', 60, 'write');
+INSERT INTO edit_access_right (name, level, type) VALUES ('Delete', 80, 'del');
+INSERT INTO edit_access_right (name, level, type) VALUES ('Site Admin', 90, 'siteadmin');
+INSERT INTO edit_access_right (name, level, type) VALUES ('Admin', 100, 'admin');
+
+-- edit scheme
+DELETE FROM edit_scheme;
+INSERT INTO edit_scheme (name, header_color, enabled) VALUES ('Default Scheme', 'E0E2FF', 1);
+INSERT INTO edit_scheme (name, header_color, enabled) VALUES ('Admin', 'CC7E7E', 1);
+INSERT INTO edit_scheme (name, header_color, enabled) VALUES ('Visitor', 'B0C4B3', 1);
+INSERT INTO edit_scheme (name, header_color, enabled) VALUES ('User', '1E789E', 1);
+
+-- edit language
+-- short_name = locale without encoding
+-- iso_name = encoding
+DELETE FROM edit_language;
+INSERT INTO edit_language (long_name, short_name, iso_name, order_number, enabled, lang_default) VALUES ('English', 'en_US', 'UTF-8', 1, 1, 1);
+INSERT INTO edit_language (long_name, short_name, iso_name, order_number, enabled, lang_default) VALUES ('Japanese', 'ja_JP', 'UTF-8', 2, 1, 0);
+
+-- edit group
+DELETE FROM edit_group;
+INSERT INTO edit_group (name, enabled, edit_scheme_id, edit_access_right_id) VALUES ('Admin', 1, (SELECT edit_scheme_id FROM edit_scheme WHERE name = 'Admin'), (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin'));
+INSERT INTO edit_group (name, enabled, edit_scheme_id, edit_access_right_id) VALUES ('User', 1, (SELECT edit_scheme_id FROM edit_scheme WHERE name = 'User'), (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'write'));
+
+-- edit page access
+DELETE FROM edit_page_access;
+INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_right_id) VALUES (1,
+ (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
+ (SELECT edit_page_id FROM edit_page WHERE name = 'Edit Pages'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_right_id) VALUES (1,
+ (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
+ (SELECT edit_page_id FROM edit_page WHERE name = 'Edit Users'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_right_id) VALUES (1,
+ (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
+ (SELECT edit_page_id FROM edit_page WHERE name = 'Edit Languages'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_right_id) VALUES (1,
+ (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
+ (SELECT edit_page_id FROM edit_page WHERE name = 'Edit Schemes'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_right_id) VALUES (1,
+ (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
+ (SELECT edit_page_id FROM edit_page WHERE name = 'Edit Groups'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_right_id) VALUES (1,
+ (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
+ (SELECT edit_page_id FROM edit_page WHERE name = 'Edit Visible Groups'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_right_id) VALUES (1,
+ (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
+ (SELECT edit_page_id FROM edit_page WHERE name = 'Edit Menu Groups'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_right_id) VALUES (1,
+ (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
+ (SELECT edit_page_id FROM edit_page WHERE name = 'Edit Access'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+INSERT INTO edit_page_access (enabled, edit_group_id, edit_page_id, edit_access_right_id) VALUES (1,
+ (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
+ (SELECT edit_page_id FROM edit_page WHERE name = 'Edit Order'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+
+-- edit user
+-- inserts admin user so basic users can be created
+DELETE FROM edit_user;
+INSERT INTO edit_user (username, password, enabled, debug, db_debug, email, protected, admin, edit_language_id, edit_group_id, edit_scheme_id, edit_access_right_id) VALUES ('admin', 'admin', 1, 1, 1, '', 1, 1,
+ (SELECT edit_language_id FROM edit_language WHERE short_name = 'en_US'),
+ (SELECT edit_group_id FROM edit_group WHERE name = 'Admin'),
+ (SELECT edit_scheme_id FROM edit_scheme WHERE name = 'Admin'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+
+-- edit access
+DELETE FROM edit_access;
+INSERT INTO edit_access (name, enabled, protected) VALUES ('Admin Access', 1, 1);
+
+-- edit access user
+DELETE FROM edit_access_user;
+INSERT INTO edit_access_user (edit_default, enabled, edit_access_id, edit_user_id, edit_access_right_id) VALUES (1, 1,
+ (SELECT edit_access_id FROM edit_access WHERE uid = 'AdminAccess'),
+ (SELECT edit_user_id FROM edit_user WHERE username = 'admin'),
+ (SELECT edit_access_right_id FROM edit_access_right WHERE type = 'admin')
+);
+-- END: data/edit_tables.sql
diff --git a/test/phpunit/dotenv/cannot_read.env b/test/phpunit/dotenv/cannot_read.env
new file mode 100644
index 0000000..e69de29
diff --git a/test/phpunit/dotenv/empty.env b/test/phpunit/dotenv/empty.env
new file mode 100644
index 0000000..e69de29
diff --git a/test/phpunit/dotenv/test.env b/test/phpunit/dotenv/test.env
new file mode 100644
index 0000000..46d3456
--- /dev/null
+++ b/test/phpunit/dotenv/test.env
@@ -0,0 +1,49 @@
+# enviroment file
+SOMETHING=A
+OTHER="B IS B"
+Complex="A B \"D is F"
+# COMMENT
+HAS_SPACE= "ABC";
+# COMMENT AT END
+HAS_COMMENT_QUOTES_SPACE="Comment at end with quotes and space" # Comment QE
+HAS_COMMENT_QUOTES_NO_SPACE="Comment at end with quotes no space"# Comment QES
+HAS_COMMENT_NO_QUOTES_SPACE=Comment at end no quotes and space # Comment NQE
+HAS_COMMENT_NO_QUOTES_NO_SPACE=Comment at end no quotes no space# Comment NQES
+COMMENT_IN_TEXT_QUOTES="Foo bar # comment in here"
+FAILURE = ABC
+SIMPLEBOX= A B C
+TITLE=1
+FOO=1.2
+SOME.TEST=Test Var
+SOME.LIVE=Live Var
+# VAR TESTS -
+A_TEST1 = foo
+A_TEST2 = ${TEST1:-bar} # TEST1 is set so the value of TEST2 = foo
+A_TEST3 = ${TEST4:-bar} # TEST4 is not set so the value of TEST3 = bar
+A_TEST5 = null
+A_TEST6 = ${TEST5-bar} # TEST5 is set but empty so the value of TEST6 = null
+A_TEST7 = ${TEST6:-bar} # TEST5 is set and empty so the value of TEST7 = bar
+# VAR TESTS =
+B_TEST1 = foo
+B_TEST2 = ${TEST1:=bar} # TEST1 is set so the value of TEST2 = foo
+B_TEST3 = ${TEST4:=bar} # TEST4 is not set so the value of TEST3 = bar and TEST4 = bar
+B_TEST5 = null
+B_TEST6 = ${TEST5=bar} # TEST5 is set but emtpy so the value of TEST6 = null
+B_TEST7 = ${TEST6=bar} # TEST5 is set and empty so the value of TEST7 = bar and TEST5 = bar
+# VAR TEST END
+Test="A"
+TEST="B"
+TEST="D"
+LINE="ABC
+DEF"
+OTHERLINE="ABC
+AF\"ASFASDF
+MORESHIT"
+SUPERLINE=
+"asfasfasf"
+ __FOO_BAR_1 = b
+ __FOOFOO = f
+123123=number
+EMPTY=
+= flase
+asfasdf
diff --git a/test/phpunit/includes/create_po.sh b/test/phpunit/includes/create_po.sh
new file mode 100755
index 0000000..0162152
--- /dev/null
+++ b/test/phpunit/includes/create_po.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+# if we don't have one base file we are in the wrong folder
+if [ ! -f "locale/en_US/LC_MESSAGES/admin.mo" ]; then
+ echo "Locale file is missing, wrong base folder?"
+ echo "Should be: 4dev/tests/includes/"
+ exit;
+fi;
+
+for file in $(ls -1 locale/*.po); do
+ echo $file;
+ file=$(basename $file .po);
+ locale=$(echo "${file}" | cut -d "-" -f 1);
+ domain=$(echo "${file}" | cut -d "-" -f 2);
+ msgfmt -o locale/${locale}/LC_MESSAGES/${domain}.mo locale/${locale}-${domain}.po;
+done;
diff --git a/test/phpunit/includes/locale/en_US-admin.po b/test/phpunit/includes/locale/en_US-admin.po
new file mode 100644
index 0000000..c35caf2
--- /dev/null
+++ b/test/phpunit/includes/locale/en_US-admin.po
@@ -0,0 +1,34 @@
+
+msgid ""
+msgstr ""
+"Project-Id-Version: en_US.UTF-8 LC_MESSAGES admin\n"
+"Report-Msgid-Bugs-To: clemens.schwaighofer@egplusww.com\n"
+"POT-Creation-Date: 2018-03-28 10:40+0900\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: clemens.schwaighofer@egplusww.co\n"
+"Language-Team: E-GRAPHICS COMMUNICATIONS Japan
\n"
+"Language: en_US\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+msgid "Original"
+msgstr "Translated admin en_US"
+
+msgid "single"
+msgid_plural "multi"
+msgstr[0] "Multi admin en_US 0"
+msgstr[1] "Multi admin en_US 1"
+msgstr[2] "Multi admin en_US 2"
+
+msgctxt "context"
+msgid "Original"
+msgstr "Original context admin en_US"
+
+msgctxt "context"
+msgid "single"
+msgid_plural "multi"
+msgstr[0] "Multi context admin en_US 0"
+msgstr[1] "Multi context admin en_US 1"
+msgstr[2] "Multi context admin en_US 2"
diff --git a/test/phpunit/includes/locale/en_US-frontend.po b/test/phpunit/includes/locale/en_US-frontend.po
new file mode 100644
index 0000000..a2f58ed
--- /dev/null
+++ b/test/phpunit/includes/locale/en_US-frontend.po
@@ -0,0 +1,34 @@
+
+msgid ""
+msgstr ""
+"Project-Id-Version: en_US.UTF-8 LC_MESSAGES frontend\n"
+"Report-Msgid-Bugs-To: clemens.schwaighofer@egplusww.com\n"
+"POT-Creation-Date: 2018-03-28 10:40+0900\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: clemens.schwaighofer@egplusww.co\n"
+"Language-Team: E-GRAPHICS COMMUNICATIONS Japan \n"
+"Language: en_US\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+
+msgid "Original"
+msgstr "Translated frontend en_US"
+
+msgid "single"
+msgid_plural "multi"
+msgstr[0] "Multi frontend en_US 0"
+msgstr[1] "Multi frontend en_US 1"
+msgstr[2] "Multi frontend en_US 2"
+
+msgctxt "context"
+msgid "Original"
+msgstr "Original context frontend en_US"
+
+msgctxt "context"
+msgid "single"
+msgid_plural "multi"
+msgstr[0] "Multi context frontend en_US 0"
+msgstr[1] "Multi context frontend en_US 1"
+msgstr[2] "Multi context frontend en_US 2"
diff --git a/test/phpunit/includes/locale/en_US/LC_MESSAGES/admin.mo b/test/phpunit/includes/locale/en_US/LC_MESSAGES/admin.mo
new file mode 100644
index 0000000..c681b0c
Binary files /dev/null and b/test/phpunit/includes/locale/en_US/LC_MESSAGES/admin.mo differ
diff --git a/test/phpunit/includes/locale/en_US/LC_MESSAGES/frontend.mo b/test/phpunit/includes/locale/en_US/LC_MESSAGES/frontend.mo
new file mode 100644
index 0000000..b63ffe7
Binary files /dev/null and b/test/phpunit/includes/locale/en_US/LC_MESSAGES/frontend.mo differ
diff --git a/test/phpunit/includes/locale/ja_JP-admin.po b/test/phpunit/includes/locale/ja_JP-admin.po
new file mode 100644
index 0000000..e2a8795
--- /dev/null
+++ b/test/phpunit/includes/locale/ja_JP-admin.po
@@ -0,0 +1,35 @@
+
+msgid ""
+msgstr ""
+"Project-Id-Version: ja_JP.UTF-8 LC_MESSAGES admin\n"
+"Report-Msgid-Bugs-To: clemens.schwaighofer@egplusww.com\n"
+"POT-Creation-Date: 2018-03-28 10:40+0900\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: clemens.schwaighofer@egplusww.co\n"
+"Language-Team: E-GRAPHICS COMMUNICATIONS Japan \n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
+# 0, 1, 2 plural
+
+msgid "Original"
+msgstr "Translated admin ja_JP"
+
+msgid "single"
+msgid_plural "multi"
+msgstr[0] "Multi admin ja_JP 0"
+msgstr[1] "Multi admin ja_JP 1"
+msgstr[2] "Multi admin ja_JP 2"
+
+msgctxt "context"
+msgid "Original"
+msgstr "Original context admin ja_JP"
+
+msgctxt "context"
+msgid "single"
+msgid_plural "multi"
+msgstr[0] "Multi context admin ja_JP 0"
+msgstr[1] "Multi context admin ja_JP 1"
+msgstr[2] "Multi context admin ja_JP 2"
diff --git a/test/phpunit/includes/locale/ja_JP-frontend.po b/test/phpunit/includes/locale/ja_JP-frontend.po
new file mode 100644
index 0000000..ccdba7a
--- /dev/null
+++ b/test/phpunit/includes/locale/ja_JP-frontend.po
@@ -0,0 +1,35 @@
+
+msgid ""
+msgstr ""
+"Project-Id-Version: ja_JP.UTF-8 LC_MESSAGES frontend\n"
+"Report-Msgid-Bugs-To: clemens.schwaighofer@egplusww.com\n"
+"POT-Creation-Date: 2018-03-28 10:40+0900\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: clemens.schwaighofer@egplusww.co\n"
+"Language-Team: E-GRAPHICS COMMUNICATIONS Japan \n"
+"Language: ja\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;\n"
+# 0, 1, 2 plural
+
+msgid "Original"
+msgstr "Translated frontend ja_JP"
+
+msgid "single"
+msgid_plural "multi"
+msgstr[0] "Multi frontend ja_JP 0"
+msgstr[1] "Multi frontend ja_JP 1"
+msgstr[2] "Multi frontend ja_JP 2"
+
+msgctxt "context"
+msgid "Original"
+msgstr "Original context frontend ja_JP"
+
+msgctxt "context"
+msgid "single"
+msgid_plural "multi"
+msgstr[0] "Multi context frontend ja_JP 0"
+msgstr[1] "Multi context frontend ja_JP 1"
+msgstr[2] "Multi context frontend ja_JP 2"
diff --git a/test/phpunit/includes/locale/ja_JP/LC_MESSAGES/admin.mo b/test/phpunit/includes/locale/ja_JP/LC_MESSAGES/admin.mo
new file mode 100644
index 0000000..df8d579
Binary files /dev/null and b/test/phpunit/includes/locale/ja_JP/LC_MESSAGES/admin.mo differ
diff --git a/test/phpunit/includes/locale/ja_JP/LC_MESSAGES/frontend.mo b/test/phpunit/includes/locale/ja_JP/LC_MESSAGES/frontend.mo
new file mode 100644
index 0000000..c8b8414
Binary files /dev/null and b/test/phpunit/includes/locale/ja_JP/LC_MESSAGES/frontend.mo differ
diff --git a/test/phpunit/log/.gitignore b/test/phpunit/log/.gitignore
new file mode 100644
index 0000000..2a50fc0
--- /dev/null
+++ b/test/phpunit/log/.gitignore
@@ -0,0 +1,3 @@
+*log
+*LOG
+!.gitignore