diff options
-rw-r--r-- | config.example.toml | 1 | ||||
-rw-r--r-- | public/index.php | 1 | ||||
-rw-r--r-- | src/App.php | 333 | ||||
-rw-r--r-- | tests/Test.php | 205 |
4 files changed, 268 insertions, 272 deletions
diff --git a/config.example.toml b/config.example.toml index a943c4f..64ecc4a 100644 --- a/config.example.toml +++ b/config.example.toml @@ -1,3 +1,2 @@ [app] contentFolderPath = './content' - diff --git a/public/index.php b/public/index.php index 3d82924..b943298 100644 --- a/public/index.php +++ b/public/index.php @@ -34,4 +34,3 @@ foreach ($config as $key => $value) { } new App(); - diff --git a/src/App.php b/src/App.php index bad2412..92ceb4a 100644 --- a/src/App.php +++ b/src/App.php @@ -33,152 +33,152 @@ class App // check api key $apiKey = $_GET['key'] ?? $_POST['key'] ?? null; if (empty($apiKey)) { - $response->setStatusCode(Response::HTTP_BAD_REQUEST); - throw new \Exception('API key missing'); + $response->setStatusCode(Response::HTTP_BAD_REQUEST); + throw new \Exception('API key missing'); } if (! in_array($apiKey, $config['api']['keys'])) { - $response->setStatusCode(Response::HTTP_UNAUTHORIZED); - throw new \Exception('API key does not match'); + $response->setStatusCode(Response::HTTP_UNAUTHORIZED); + throw new \Exception('API key does not match'); } // GET if ($method == 'GET') { - if (str_ends_with($path, '/fields')) { - $this->formPath = $formPath = $contentRoot . str_replace('/fields', '', $path); - - $fields = $this->buildFields($formPath, $_GET['page'] ?? null); - - // flatten paged form - if ($this->isPagedFieldSet($fields) && isset($_GET['flat'])) { - $fields = array_merge(...array_values($fields)); - } - - $content['data'] = $fields; - } - - else if (str_ends_with($path, '/entries')) { - if (! isset($_GET['dateFrom'])) { - $response->setStatusCode(Response::HTTP_BAD_REQUEST); - throw new \Exception('dateFrom parameter missing'); - } - - $this->formPath = $formPath = $contentRoot . str_replace('/entries', '', $path); - - $entries = []; - - $dateFrom = new \DateTime($_GET['dateFrom']); - $dateTo = new \DateTime($_GET['dateTo'] ?? 'now'); - - $dateRangeYears = range($dateFrom->format('Y'), $dateTo->format('Y')); - $dateRangeYearsCount = count($dateRangeYears); - foreach ($dateRangeYears as $dateRangeYearIdx => $dateRangeYear) { - $yearPath = "$formPath/entries/$dateRangeYear"; - if (! is_dir($yearPath)) { - continue; - } - - if ($dateRangeYearsCount === 1) { - $dateRangeMonths = range($dateFrom->format('m'), $dateTo->format('m')); - } - else if ($dateRangeYearIdx === 0) { - $dateRangeMonths = range($dateFrom->format('m'), 12); - } - else if ($dateRangeYearIdx === $dateRangeYearsCount - 1) { - $dateRangeMonths = range(1, $dateTo->format('m')); - } - else { - $dateRangeMonths = range(1, 12); - } - - $dateRangeMonthsCount = count($dateRangeMonths); - foreach ($dateRangeMonths as $dateRangeMonthIdx => $dateRangeMonth) { - $monthPath = "$yearPath/" . sprintf('%02d', $dateRangeMonth); - if (! is_dir($monthPath)) { - continue; - } - - if ($dateRangeMonthsCount === 1) { - $dateRangeDays = range($dateFrom->format('d'), $dateTo->format('d')); - } - else if ($dateRangeYearIdx === 0 && $dateRangeMonthIdx === 0) { - $dateRangeDays = range($dateFrom->format('d'), 31); - } - else if ($dateRangeYearIdx === $dateRangeYearsCount - 1 && $dateRangeMonthIdx === $dateRangeMonthsCount - 1) { - $dateRangeDays = range(1, $dateTo->format('d')); - } - else { - $dateRangeDays = range(1, 31); - } - - foreach ($dateRangeDays as $dateRangeDay) { - $dayPath = "$monthPath/" . sprintf('%02d', $dateRangeDay); - if (! is_dir($dayPath)) { - continue; - } - - $entriesForDay = $this->scandir($dayPath); - foreach ($entriesForDay as $entryForDay) { - $entry = Toml::parseFile("$dayPath/$entryForDay"); - if (isset($_GET['flat'])) { - $entries[] = $entry; - } else { - $entries[$dateRangeYear][$dateRangeMonth][$dateRangeDay][] = $entry; - } - } - } - - } - } - - $content['data'] = $entries; - } - - else { - $content['data'] = Toml::parseFile($contentRoot . $path . '.toml'); - } + if (str_ends_with($path, '/fields')) { + $this->formPath = $formPath = $contentRoot . str_replace('/fields', '', $path); + + $fields = $this->buildFields($formPath, $_GET['page'] ?? null); + + // flatten paged form + if ($this->isPagedFieldSet($fields) && isset($_GET['flat'])) { + $fields = array_merge(...array_values($fields)); + } + + $content['data'] = $fields; + } + + else if (str_ends_with($path, '/entries')) { + if (! isset($_GET['dateFrom'])) { + $response->setStatusCode(Response::HTTP_BAD_REQUEST); + throw new \Exception('dateFrom parameter missing'); + } + + $this->formPath = $formPath = $contentRoot . str_replace('/entries', '', $path); + + $entries = []; + + $dateFrom = new \DateTime($_GET['dateFrom']); + $dateTo = new \DateTime($_GET['dateTo'] ?? 'now'); + + $dateRangeYears = range($dateFrom->format('Y'), $dateTo->format('Y')); + $dateRangeYearsCount = count($dateRangeYears); + foreach ($dateRangeYears as $dateRangeYearIdx => $dateRangeYear) { + $yearPath = "$formPath/entries/$dateRangeYear"; + if (! is_dir($yearPath)) { + continue; + } + + if ($dateRangeYearsCount === 1) { + $dateRangeMonths = range($dateFrom->format('m'), $dateTo->format('m')); + } + else if ($dateRangeYearIdx === 0) { + $dateRangeMonths = range($dateFrom->format('m'), 12); + } + else if ($dateRangeYearIdx === $dateRangeYearsCount - 1) { + $dateRangeMonths = range(1, $dateTo->format('m')); + } + else { + $dateRangeMonths = range(1, 12); + } + + $dateRangeMonthsCount = count($dateRangeMonths); + foreach ($dateRangeMonths as $dateRangeMonthIdx => $dateRangeMonth) { + $monthPath = "$yearPath/" . sprintf('%02d', $dateRangeMonth); + if (! is_dir($monthPath)) { + continue; + } + + if ($dateRangeMonthsCount === 1) { + $dateRangeDays = range($dateFrom->format('d'), $dateTo->format('d')); + } + else if ($dateRangeYearIdx === 0 && $dateRangeMonthIdx === 0) { + $dateRangeDays = range($dateFrom->format('d'), 31); + } + else if ($dateRangeYearIdx === $dateRangeYearsCount - 1 && $dateRangeMonthIdx === $dateRangeMonthsCount - 1) { + $dateRangeDays = range(1, $dateTo->format('d')); + } + else { + $dateRangeDays = range(1, 31); + } + + foreach ($dateRangeDays as $dateRangeDay) { + $dayPath = "$monthPath/" . sprintf('%02d', $dateRangeDay); + if (! is_dir($dayPath)) { + continue; + } + + $entriesForDay = $this->scandir($dayPath); + foreach ($entriesForDay as $entryForDay) { + $entry = Toml::parseFile("$dayPath/$entryForDay"); + if (isset($_GET['flat'])) { + $entries[] = $entry; + } else { + $entries[$dateRangeYear][$dateRangeMonth][$dateRangeDay][] = $entry; + } + } + } + + } + } + + $content['data'] = $entries; + } + + else { + $content['data'] = Toml::parseFile($contentRoot . $path . '.toml'); + } } // POST else if ($method == 'POST') { - if (str_ends_with($path, '/validate')) { - $this->formPath = $formPath = $contentRoot . str_replace('/validate', '', $path); - - $fields = $this->buildFields($formPath, $_GET['page'] ?? null); - - $content = $this->validateRequest($fields); - } - - else if (str_ends_with($path, '/submit')) { - $this->formPath = $formPath = $contentRoot . str_replace('/submit', '', $path); - - $fields = $this->buildFields($formPath); - - $content = $this->validateRequest($fields); - - // if there were no validation errors then add entry - if (empty($content['error'])) { - $date = new \Datetime(); - $entry = [ - 'fields' => $_POST, - 'date' => $date->format('c'), - ]; - - $builder = new TomlBuilder(); - $builder->addValue('date', $entry['date']); - $builder->addTable('fields'); - foreach ($entry['fields'] as $entryKey => $entryValue) { - $builder->addValue($entryKey, $entryValue); - } - - $entryDirectory = $formPath . '/entries/' . $date->format('Y/m/d'); - @mkdir($entryDirectory, 0774, true); - $entryFilename = $date->format('Ymd_Hi_') . hash('adler32', serialize($entry)) . '.toml'; - file_put_contents( - $entryDirectory . '/' . $entryFilename, - $builder->getTomlString() - ); - } - } + if (str_ends_with($path, '/validate')) { + $this->formPath = $formPath = $contentRoot . str_replace('/validate', '', $path); + + $fields = $this->buildFields($formPath, $_GET['page'] ?? null); + + $content = $this->validateRequest($fields); + } + + else if (str_ends_with($path, '/submit')) { + $this->formPath = $formPath = $contentRoot . str_replace('/submit', '', $path); + + $fields = $this->buildFields($formPath); + + $content = $this->validateRequest($fields); + + // if there were no validation errors then add entry + if (empty($content['error'])) { + $date = new \Datetime(); + $entry = [ + 'fields' => $_POST, + 'date' => $date->format('c'), + ]; + + $builder = new TomlBuilder(); + $builder->addValue('date', $entry['date']); + $builder->addTable('fields'); + foreach ($entry['fields'] as $entryKey => $entryValue) { + $builder->addValue($entryKey, $entryValue); + } + + $entryDirectory = $formPath . '/entries/' . $date->format('Y/m/d'); + @mkdir($entryDirectory, 0774, true); + $entryFilename = $date->format('Ymd_Hi_') . hash('adler32', serialize($entry)) . '.toml'; + file_put_contents( + $entryDirectory . '/' . $entryFilename, + $builder->getTomlString() + ); + } + } } } catch (\Exception $exception) { $content['error'] = basename(get_class($exception)) . ': ' . $exception->getMessage(); @@ -202,11 +202,11 @@ class App if ($this->isPagedFieldSet($fields)) { // remove surplus field values from response $fields = array_map(function ($page) { - return array_map(function ($field) { - return array_intersect_key($field, array_flip([ - 'is_valid', - ])); - }, $page); + return array_map(function ($field) { + return array_intersect_key($field, array_flip([ + 'is_valid', + ])); + }, $page); }, $fields); $flattened = array_merge(...array_values($fields)); @@ -214,9 +214,9 @@ class App } else { // remove surplus field values from response $fields = array_map(function ($field) { - return array_intersect_key($field, array_flip([ - 'is_valid', - ])); + return array_intersect_key($field, array_flip([ + 'is_valid', + ])); }, $fields); $hasInvalidFields = in_array(false, array_column($fields, 'is_valid')); @@ -243,11 +243,11 @@ class App // if a page is requested if ($page) { if (! isset($parsed['page'])) { - throw new \Exception('Form has no pages'); + throw new \Exception('Form has no pages'); } if (! isset($parsed['page'][$page])) { - throw new \Exception('Form has no page ' . $page); + throw new \Exception('Form has no page ' . $page); } $fields = $this->buildSinglePageFields($parsed['page'][$page], $formPath); @@ -257,17 +257,17 @@ class App else { // if form is paged if (isset($parsed['page'])) { - $pages = $parsed['page']; - foreach ($pages as $pageKey => $pageFields) { - $fields[$pageKey] = $this->buildSinglePageFields($pageFields, $formPath); - } + $pages = $parsed['page']; + foreach ($pages as $pageKey => $pageFields) { + $fields[$pageKey] = $this->buildSinglePageFields($pageFields, $formPath); + } } // if form is not paged else { - foreach ($parsed['field'] as $key => $field) { - $fields[$key] = $this->buildSingleField($formPath, $key, $field); - } + foreach ($parsed['field'] as $key => $field) { + $fields[$key] = $this->buildSingleField($formPath, $key, $field); + } } } @@ -318,13 +318,13 @@ class App { if ($this->isPagedFieldSet($fields)) { foreach ($fields as $pageKey => &$pageFields) { - foreach ($pageFields as $key => &$field) { - $field = $this->validateSingleField($field); - } + foreach ($pageFields as $key => &$field) { + $field = $this->validateSingleField($field); + } } } else { foreach ($fields as $key => &$field) { - $field = $this->validateSingleField($field); + $field = $this->validateSingleField($field); } } @@ -365,21 +365,21 @@ class App while (true) { $configFile = $currentDirectory . '/config/config.toml'; if (file_exists($configFile)) { - $parsedConfig = Toml::parseFile($configFile); + $parsedConfig = Toml::parseFile($configFile); - $apiKeys = array_merge($parsedConfig['api']['keys'] ?? [], $config['api']['keys'] ?? []); - $config = array_replace_recursive($parsedConfig, $config); - $config['api']['keys'] = $apiKeys; + $apiKeys = array_merge($parsedConfig['api']['keys'] ?? [], $config['api']['keys'] ?? []); + $config = array_replace_recursive($parsedConfig, $config); + $config['api']['keys'] = $apiKeys; } // include custom functions $functionsFile = $currentDirectory . '/config/functions.php'; if (file_exists($functionsFile)) { - include_once $functionsFile; + include_once $functionsFile; } if (str_ends_with($currentDirectory, '/' . basename($_ENV['app']['contentFolderPath'])) || $currentDirectory == '/') { - break; + break; } $currentDirectory = dirname($currentDirectory); @@ -408,9 +408,9 @@ class App $scanned = []; foreach ($paths as $path) { $filtered = array_values( - array_filter( - scandir($path), fn ($item) => ! in_array($item, ['.', '..']) - ) + array_filter( + scandir($path), fn ($item) => ! in_array($item, ['.', '..']) + ) ); array_push($scanned, ...$filtered); } @@ -418,4 +418,3 @@ class App return $scanned; } } - diff --git a/tests/Test.php b/tests/Test.php index 0543c1f..2888d81 100644 --- a/tests/Test.php +++ b/tests/Test.php @@ -27,118 +27,118 @@ class Test extends TestCase # root config file_put_contents($contentRoot . '/config/config.toml', <<<EOF - [app] - title = "Flat-File Forms" + [app] + title = "Flat-File Forms" - [email] - host = "localhost" - user = "root" - password = "123456" + [email] + host = "localhost" + user = "root" + password = "123456" - [api] - keys = ["1234"] + [api] + keys = ["1234"] - [api.cors] - origins = ["http://localhost:8081"] - EOF); + [api.cors] + origins = ["http://localhost:8081"] + EOF); # formname config file_put_contents($contentRoot . '/customername/formname/config/config.toml', <<<EOF - [api] - keys = ["asdfghjklö0987654321", "123"] + [api] + keys = ["asdfghjklö0987654321", "123"] - [email] - host = "gmx" - EOF); + [email] + host = "gmx" + EOF); # formname fields file_put_contents($contentRoot . '/customername/formname/fields/_fields.toml', <<<EOF - [field.name] - file = "name.toml" + [field.name] + file = "name.toml" - [field.email] - file = "email.toml" - required = true + [field.email] + file = "email.toml" + required = true - [field.date] - required = true - EOF); + [field.date] + required = true + EOF); file_put_contents($contentRoot . '/customername/formname/fields/email.toml', <<<EOF - type = "email" - name = "email" - placeholder = "E-Mail" + type = "email" + name = "email" + placeholder = "E-Mail" - [attributes] - data-email = "test@example.org" - EOF); + [attributes] + data-email = "test@example.org" + EOF); file_put_contents($contentRoot . '/customername/formname/fields/name.toml', <<<EOF - type = "text" - name = "name" - - [attributes] - data-value = 123 - EOF); - - file_put_contents($contentRoot . '/customername/formname/config/functions.php', ' - <?php - function validate_formname_name($field, $value) - { - if ($value !== \'Harry\') { - $field[\'is_valid\'] = false; + type = "text" + name = "name" + + [attributes] + data-value = 123 + EOF); + + file_put_contents($contentRoot . '/customername/formname/config/functions.php', <<<EOF + <?php + function validate_formname_name(\$field, \$value) + { + if (\$value !== 'Harry') { + \$field['is_valid'] = false; + } + + return \$field; } - - return $field; - } - '); + EOF); # pagedform fields file_put_contents($contentRoot . '/customername/pagedform/fields/_fields.toml', <<<EOF - [page.one.field.name] - file = "name.toml" + [page.one.field.name] + file = "name.toml" - [page.one.field.email] - file = "email.toml" - required = true + [page.one.field.email] + file = "email.toml" + required = true - [page.second] - file = "second.toml" + [page.second] + file = "second.toml" - [page.second.field.date] - required = true - EOF); + [page.second.field.date] + required = true + EOF); file_put_contents($contentRoot . '/customername/pagedform/fields/second.toml', <<<EOF - [field.text] - placeholder = "Text placeholder" + [field.text] + placeholder = "Text placeholder" - [field.text.validation] - pattern = "\\\d+" + [field.text.validation] + pattern = "\\\d+" - [field.manythings] - type = "select" + [field.manythings] + type = "select" - [field.manythings.options] - first = "First level" - second = "Second level" + [field.manythings.options] + first = "First level" + second = "Second level" - [field.multiplethings] - type = "checkbox" - options = ["thing-1", "thing-2", "thing-3"] - EOF); + [field.multiplethings] + type = "checkbox" + options = ["thing-1", "thing-2", "thing-3"] + EOF); file_put_contents($contentRoot . '/customername/pagedform/fields/email.toml', <<<EOF - type = "email" - name = "email" - placeholder = "E-Mail" + type = "email" + name = "email" + placeholder = "E-Mail" - [attributes] - data-email = "test@example.org" - EOF); + [attributes] + data-email = "test@example.org" + EOF); file_put_contents($contentRoot . '/customername/pagedform/fields/name.toml', <<<EOF - type = "text" - name = "name" + type = "text" + name = "name" - [attributes] - data-value = 123 - EOF); + [attributes] + data-value = 123 + EOF); } public function request($method, $path, $options = []): GuzzleHttpResponse @@ -225,14 +225,14 @@ class Test extends TestCase // valid response $response = $this->request('POST', 'customername/pagedform/submit?key=' . $this->apiKey, [ 'form_params' => [ - 'name' => 'NAME', - 'email' => 'EMAIL', - 'date' => 'DATE', - 'text' => '123', - 'manythings' => 'second', - 'multiplethings' => [ - 'thing-1', 'thing-3', - ], + 'name' => 'NAME', + 'email' => 'EMAIL', + 'date' => 'DATE', + 'text' => '123', + 'manythings' => 'second', + 'multiplethings' => [ + 'thing-1', 'thing-3', + ], ], ]); $body = json_decode((string)$response->getBody(), true); @@ -245,9 +245,9 @@ class Test extends TestCase // invalid response $response = $this->request('POST', 'customername/pagedform/submit?key=' . $this->apiKey, [ 'form_params' => [ - 'name' => 'NAME', - 'date' => 'DATE', - 'text' => 'einszweidrei', + 'name' => 'NAME', + 'date' => 'DATE', + 'text' => 'einszweidrei', ], ]); $body = json_decode((string)$response->getBody(), true); @@ -260,8 +260,8 @@ class Test extends TestCase // valid response "one" page $response = $this->request('POST', 'customername/pagedform/validate?page=one&key=' . $this->apiKey, [ 'form_params' => [ - 'name' => 'NAME', - 'email' => 'EMAIL', + 'name' => 'NAME', + 'email' => 'EMAIL', ], ]); $body = json_decode((string)$response->getBody(), true); @@ -275,8 +275,8 @@ class Test extends TestCase // valid response "second" page $response = $this->request('POST', 'customername/pagedform/validate?page=second&key=' . $this->apiKey, [ 'form_params' => [ - 'date' => 'DATE', - 'text' => '123', + 'date' => 'DATE', + 'text' => '123', ], ]); $body = json_decode((string)$response->getBody(), true); @@ -290,9 +290,9 @@ class Test extends TestCase // valid response validation_function $response = $this->request('POST', 'customername/formname/submit?key=' . $this->apiKey, [ 'form_params' => [ - 'name' => 'Harry', - 'email' => 'EMAIL', - 'date' => 'DATE', + 'name' => 'Harry', + 'email' => 'EMAIL', + 'date' => 'DATE', ], ]); $body = json_decode((string)$response->getBody(), true); @@ -305,9 +305,9 @@ class Test extends TestCase // invalid response validation_function $response = $this->request('POST', 'customername/formname/submit?key=' . $this->apiKey, [ 'form_params' => [ - 'name' => 'NAME', - 'email' => 'EMAIL', - 'date' => 'DATE', + 'name' => 'NAME', + 'email' => 'EMAIL', + 'date' => 'DATE', ], ]); $body = json_decode((string)$response->getBody(), true); @@ -318,4 +318,3 @@ class Test extends TestCase $this->assertEquals(true, $body['date']['is_valid']); } } - |