Skip to content

Commit

Permalink
Merge branch '2023-09'
Browse files Browse the repository at this point in the history
New features:
- text input can be cloned in frontend
- better error handling
- custom error HTML
- fix crash when uploads exceeded server limits
- new display options for HR and labels
- CSS fixes
  • Loading branch information
annda committed Sep 21, 2023
2 parents 885833d + c54cff0 commit 8657022
Show file tree
Hide file tree
Showing 33 changed files with 437 additions and 105 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ data/*
!data/EXAMPLE/config.yaml
!data/EXAMPLE/logo.png
!data/EXAMPLE/i_will_be_moved.txt
!data/404.html
!data/500.html

# logs
/logs
Expand All @@ -18,4 +20,4 @@ data/*
/conf/*.local.yaml

# export directory
/export
/export
4 changes: 3 additions & 1 deletion conf/language.de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ label_signature_replace: Unterschrift erneuern
email_text_attachments: Die Anhänge ansehen
button_save : Speichern
button_send : Senden
button_clone: Feld hinzufügen
button_upload: Datei hochladen
button_upload_replace: Datei ersetzen
uploaded_file: Hochgeladene Dateien
upload_info: Die ausgewählte Datei wird beim Speichern oder Senden hochgeladen und geprüft
upload_info: Die ausgewählte Datei wird beim Speichern oder Senden hochgeladen und geprüft
upload_error: Dia Dateien sind zu groß!
4 changes: 3 additions & 1 deletion conf/language.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ label_signature_replace: Replace signature
email_text_attachments: Look into the attachments
button_save : Save
button_send : Send
button_clone: Copy field
button_upload: Upload file
button_upload_replace: Replace uploaded file
uploaded_file: Saved file
upload_info: The selected file will be uploaded and checked after saving or sending the form
upload_info: The selected file will be uploaded and checked after saving or sending the form
upload_error: Upload is too large!
6 changes: 5 additions & 1 deletion conf/settings.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,8 @@ settings:
username: ~
password: ~
fileExporter:
dir: export
dir: export
errorMessageNotFound: 'Form not found!'
errorMessageGeneral: 'Unknown error!'
errorPageNotFound: 'data/404.html'
errorPageGeneral: 'data/500.html'
6 changes: 6 additions & 0 deletions data/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<html>
<head></head>
<body>
<h1>Page not found!</h1>
</body>
</html>
6 changes: 6 additions & 0 deletions data/500.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<html>
<head></head>
<body>
<h1>Unknown error!</h1>
</body>
</html>
8 changes: 8 additions & 0 deletions doc/app.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@ All settings and message strings are defined in `conf/`

You can override the defaults by copying `settings.default.yaml` and/or `language.default.yaml` to `*.local.yaml` and adjusting the values. Also see [Language Settings](meta.md#Translations)

## Error messages

If a form cannot be found, a customized message can be shown to the user. There are two global app settings to define the message:

1. `errorPageNotFound`: path to static HTML file. The default value is `data/404.html` and a very minimalist file is provided in this location. Override this with path to your own file. `data` is the recommended location, but you can also put it in `public`. Leave empty not to use the HTML page.
2. `errorMessageNotFound`: simple text message, displayed when the error page is not found.

In case of other global errors, you can customize the HTML or the text message as well by adjusting `errorPageGeneral` and `errorMessageGeneral`.
7 changes: 5 additions & 2 deletions doc/formelements.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ Form elements can be grouped visually and/or logically in [fieldsets](#fieldsets
The element definition must contain at least the type of the form element.
Options:
* `label` _(optional)_ - the label of the form element (excluded: [hidden](#hidden), [markdown](#markdown))
* `column` _(optional)_ - [bulma column sizes](https://bulma.io/documentation/columns/sizes/) defining the width of the form element (excluded: [hidden](#hidden))
* `labelsmall` _(optional)_ - if set to true, the label will be rendered in regular font instead of default bold
* `column` _(optional)_ - [bulma column sizes](https://bulma.io/documentation/columns/sizes/) defining the width of the form element (excluded: [hidden](#hidden)). You can use [offset](https://bulma.io/documentation/columns/sizes/#offset) to position the columns, for example `is-half is-offset-one-quarter` to center a half-width column.
```yaml
<id>:
type: <type>
Expand Down Expand Up @@ -175,6 +176,8 @@ Simple text input.
required: false
```

* `clone` _(optional)_ - Adds a clone button to the field. That way you can repeat the same input to create a list of variable length.

### Numberinput

Simple number (integer) input.
Expand Down Expand Up @@ -291,7 +294,7 @@ Options:
* `choices` _(required)_ - defines available options. Markdown ist supported.
* `empty_label` _(optional)_ - a placeholder text shown if no value was chosen (e.g. "Please select"). **Note:** this is not a real option and has no value that could be saved.
* `multiselect` _(optional)_ - enables selecting multiple options
* `size` _(optional)_ - if multiselect is turned on this defines the number of rows shown
* `size` _(optional)_ - if multiselect is turned on this defines the number of rows shown, otherwise ignored
* `default` _(optional)_ : Preselects a choice. This is just triggered if the form was never saved before. **Preselect in toggles are not supported yet.** **BREAKING CHANGE until version 1.0.4 this parameter was used for empty_label**

```yaml
Expand Down
2 changes: 1 addition & 1 deletion doc/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## App configuration

[App settings](app.md) offer a few options to adjust the defaults, including mail server settings and the messages displayed to the user.
[App settings](app.md) offer a few options to adjust the defaults, including mail server settings and the messages displayed to the user, including a general error message.

## Form configuration

Expand Down
46 changes: 32 additions & 14 deletions public/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,23 @@ input.checkbox-input {
display: none;
}

label.label-smaller {
font-weight: normal;
}

/** Tooltips */
.label {
display: inline-block;
}

.tooltip-button {
border: 0;
margin-left: 0.2rem;
}

button.clone-field {
float: right;
margin-top: 0.5rem;
}

/** Required so fieldsets don't destroy Bulma's column system */
Expand Down Expand Up @@ -64,10 +74,6 @@ fieldset .subtitle {


@media screen and (min-width: 769px), print {
.fieldset-label {
min-height: 3.75rem;
}

.is-full .fieldset-label {
min-height: 1rem;
}
Expand All @@ -91,6 +97,10 @@ fieldset .subtitle {
height: 10em;
}

/**
* .is-left-label marks fieldsets with "tablestyle" attribute
*/

/** + + + + + spacer in table design + + + + + */

.is-left-label .field.is-spacer {
Expand All @@ -106,10 +116,7 @@ fieldset .subtitle {
display: inline-block;
}


/** + + + + + label once + + + + + */

/* + + + width of cols + + + */
/* + + + width of cols in table design + + + */

.is-left-label .column.is-half {
width: 33.3333%;
Expand All @@ -135,7 +142,7 @@ fieldset .subtitle {
width: 40%;
}

/* + + + background of cols + + + */
/* + + + background of cols in table design + + + */

.is-left-label fieldset:nth-of-type(even) .column .is-multiline {
background-color: #F3F3F3;
Expand Down Expand Up @@ -169,23 +176,38 @@ fieldset .subtitle {
position: relative;
}

.is-left-label fieldset:first-of-type.label-with-tooltip {
display: flex;
flex-direction: row;
width: 50%;
}

/* labels will be invisible in tablestyle "columns" */
.is-left-label label {
position: absolute;
left: -1000rem;
top: -1000rem;
}

/* but visibly move first label to "left header column" */
.is-left-label fieldset:first-of-type label {
position: relative;
left: 0;
top: 0;
width: 50%;
}

.is-left-label fieldset:first-of-type .control {
width: 47%;
margin-right: 0;
margin-left: auto;
}

/* radio inputs are wrapped in labels so skip table magic */
.is-left-label fieldset .control .grouped-form-elements label {
position: relative;
left: 0;
top: 0;
}
}

.is-scrollable {
Expand All @@ -205,7 +227,3 @@ fieldset .subtitle {
border: 3px solid #fff;
background-color: rgba(0, 0, 0, .3);
}

canvas {
width: 100%;
}
42 changes: 42 additions & 0 deletions public/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use CosmoCode\Formserver\ResponseEmitter\ResponseEmitter;
use DI\ContainerBuilder;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Factory\AppFactory;
use Slim\Factory\ServerRequestCreatorFactory;

Expand Down Expand Up @@ -45,6 +46,47 @@
// Add Routing Middleware
$app->addRoutingMiddleware();

// define minimal custom error handler
$customErrorHandler = function (
ServerRequestInterface $request,
Throwable $exception,
bool $displayErrorDetails,
bool $logErrors,
bool $logErrorDetails
) use ($app) {
$response = $app->getResponseFactory()->createResponse();

$settings = $app->getContainer()->get('settings');

if ($exception instanceof \Slim\Exception\HttpNotFoundException) {
// custom error page
$errorPage = __DIR__ . '/../' . $settings['errorPageNotFound'];
if (is_file($errorPage)) {
$errorMessage = file_get_contents($errorPage);
} else {
$errorMessage = '<h1>' . $settings['errorMessageNotFound'] . '</h1>';
}
$response->getBody()->write($errorMessage);
$response = $response->withStatus(404);
} else {
// custom error page
$errorPage = __DIR__ . '/../' . $settings['errorPageGeneral'];
if (is_file($errorPage)) {
$errorMessage = file_get_contents($errorPage);
} else {
$errorMessage = '<h1>' . $settings['errorMessageGeneral'] . '</h1>';
}
$response->getBody()->write($errorMessage);
$response = $response->withStatus(500);
}

return $response;
};

// This middleware should be added last.
$errorMiddleware = $app->addErrorMiddleware(false, false, false);
$errorMiddleware->setDefaultErrorHandler($customErrorHandler);

// Run App & Emit Response
$response = $app->handle($request);
$responseEmitter = new ResponseEmitter();
Expand Down
60 changes: 57 additions & 3 deletions public/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,32 @@ Array.from(fieldsetsWithToggle).forEach(function(fieldset) {
// Init upload feedback text
Array.from(document.querySelectorAll('.form-input.file-input')).forEach(function(fileUpload) {
fileUpload.addEventListener('input', function (e) {
infoContainerId = e.target.getAttribute('data-info-container-id');
infoContainer = document.getElementById(infoContainerId);
infoContainer.classList.remove('hidden');
const infoContainerId = e.target.getAttribute('data-info-container-id');
const infoContainer = document.getElementById(infoContainerId);
const errorContainerId = e.target.getAttribute('data-error-container-id');
const errorContainer = document.getElementById(errorContainerId);

const max = e.target.getAttribute('data-max');
const curFiles = this.files;

let uploadSize = 0;

for (const file of curFiles) {
uploadSize += file.size;
}

const ok = uploadSize < max;

if (!ok) {
this.value = ''
errorContainer.classList.remove('hidden');
infoContainer.classList.add('hidden');
} else {
errorContainer.classList.add('hidden');
infoContainer.classList.remove('hidden');

}

})
});

Expand Down Expand Up @@ -196,3 +219,34 @@ Array.from(document.querySelectorAll('.is-left-label .next-is-double')).forEach(
colFifth.classList.add('is-two-fifths');
}
});


/*
* Clone field
*/
function cloneHandler(e) {
if (e.target.matches('button.clone-field')) {

const elem = e.target;
const cloned = elem.parentNode.parentNode.cloneNode(true);
const input = cloned.querySelector('input');

cloned.id = cloned.id.replace(/(\d+$)/, function (match, number) {
return (parseInt(number, 10) + 1);
});

const newId = input.id.replace(/(\d+$)/, function(match, number) {
return (parseInt(number, 10) + 1);
});
input.value = '';
input.setAttribute('value', '');
input.id = newId;
const label = cloned.querySelector('label');
label.setAttribute('for', newId);

elem.parentNode.parentNode.after(cloned);
elem.parentNode.removeChild(elem);
}
}

form.addEventListener('click', cloneHandler);
6 changes: 5 additions & 1 deletion src/Actions/AbstractAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

namespace CosmoCode\Formserver\Actions;

use CosmoCode\Formserver\Exceptions\FormException;
use CosmoCode\Formserver\Exceptions\YamlException;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Exception\HttpBadRequestException;
Expand Down Expand Up @@ -45,8 +47,10 @@ public function __invoke(Request $request, Response $response, $args): Response

try {
return $this->action();
} catch (\Exception $e) {
} catch (YamlException $e) {
throw new HttpNotFoundException($this->request, $e->getMessage());
} catch (\Exception $e) {
throw new FormException($this->request, $e->getMessage());
}
}

Expand Down
Loading

0 comments on commit 8657022

Please sign in to comment.