345 lines
9.7 KiB
PHP
Executable file
345 lines
9.7 KiB
PHP
Executable file
<?php
|
|
|
|
|
|
use Gregwar\Captcha\CaptchaBuilder;
|
|
use Lewp\Resolve;
|
|
|
|
return new class extends Lewp\Module
|
|
{
|
|
|
|
const KEY_CSRF_TOKEN = 'csrf_token';
|
|
const KEY_CAPTCHA_PHRASE = 'captcha_phrase';
|
|
const KEY_POST_EMAIL = 'email';
|
|
const KEY_POST_NAME = 'name';
|
|
const KEY_POST_MESSAGE = 'message';
|
|
const OPTIONS_REDIRECT_URI = 'location_on_success';
|
|
const OPTIONS_MAIL_FROM = 'mail_from';
|
|
const OPTIONS_MAIL_TO = 'mail_to';
|
|
|
|
private function createForm(bool $group_input_and_labels = true)
|
|
{
|
|
$form = $this->createAndSetupElement(
|
|
'form',
|
|
'',
|
|
[
|
|
'method' => 'post',
|
|
'action' => '',
|
|
]
|
|
);
|
|
|
|
// LABEL NAME
|
|
$label_name = $this->createAndSetupElement(
|
|
'label',
|
|
$this->loadTextFile(Resolve::arrayToId([
|
|
$this->getLanguage(),
|
|
'name'
|
|
])),
|
|
[
|
|
'for' => 'name',
|
|
]
|
|
);
|
|
// NAME INPUT
|
|
$name = $this->createAndSetupElement(
|
|
'input',
|
|
'',
|
|
[
|
|
'id' => 'name',
|
|
'name' => 'name',
|
|
'type' => 'text',
|
|
'value' => $_POST[self::KEY_POST_NAME] ?? '',
|
|
'required' => 'required',
|
|
'placeholder' => ' ',
|
|
]
|
|
);
|
|
// LABEL Email
|
|
$label_email = $this->createAndSetupElement(
|
|
'label',
|
|
$this->loadTextFile(Resolve::arrayToId([
|
|
$this->getLanguage(),
|
|
'email'
|
|
])),
|
|
[
|
|
'for' => 'email'
|
|
]
|
|
);
|
|
// EMAIL INPUT
|
|
$email = $this->createAndSetupElement(
|
|
'input',
|
|
'',
|
|
[
|
|
'id' => 'email',
|
|
'name' => 'email',
|
|
'type' => 'email',
|
|
'value' => $_POST[self::KEY_POST_EMAIL] ?? '',
|
|
'required' => 'required',
|
|
'placeholder' => ' ',
|
|
]
|
|
);
|
|
// LABEL Message
|
|
$label_message = $this->createAndSetupElement(
|
|
'label',
|
|
$this->loadTextFile(Resolve::arrayToId([
|
|
$this->getLanguage(),
|
|
'message'
|
|
])),
|
|
[
|
|
'for' => 'message'
|
|
]
|
|
);
|
|
// MESSAGE INPUT
|
|
$message = $this->createAndSetupElement(
|
|
'textarea',
|
|
$_POST[self::KEY_POST_MESSAGE] ?? '',
|
|
[
|
|
'id' => 'message',
|
|
'name' => 'message',
|
|
'required' => 'required',
|
|
'placeholder' => ' ',
|
|
]
|
|
);
|
|
// LABEL Captcha
|
|
$label_captcha = $this->createAndSetupElement(
|
|
'label',
|
|
$this->loadTextFile(Resolve::arrayToId([
|
|
$this->getLanguage(),
|
|
'captcha'
|
|
])),
|
|
[
|
|
'for' => self::KEY_CAPTCHA_PHRASE
|
|
]
|
|
);
|
|
// CAPTCHA INPUT
|
|
$captcha = $this->createAndSetupElement(
|
|
'input',
|
|
'',
|
|
[
|
|
'id' => self::KEY_CAPTCHA_PHRASE,
|
|
'name' => self::KEY_CAPTCHA_PHRASE,
|
|
'type' => 'text',
|
|
'required' => 'required',
|
|
'placeholder' => ' ',
|
|
]
|
|
);
|
|
// SUBMIT BUTTON
|
|
$submit = $this->createAndSetupElement(
|
|
'button',
|
|
$this->loadTextFile(Resolve::arrayToId([
|
|
$this->getLanguage(),
|
|
'submit'
|
|
])),
|
|
[
|
|
'id' => 'submit',
|
|
'type' => 'submit',
|
|
]
|
|
);
|
|
|
|
// APPEND THEM TO DOCUMENT
|
|
if ($group_input_and_labels) {
|
|
$div = $this->createAndSetupElement(
|
|
'div', '', ['class' => 'fieldwrapper']
|
|
);
|
|
$div->appendChild($name);
|
|
$div->appendChild($label_name);
|
|
$form->appendChild($div);
|
|
$div = $this->createAndSetupElement(
|
|
'div', '', ['class' => 'fieldwrapper']
|
|
);
|
|
$div->appendChild($email);
|
|
$div->appendChild($label_email);
|
|
$form->appendChild($div);
|
|
$div = $this->createAndSetupElement(
|
|
'div', '', ['class' => 'fieldwrapper']
|
|
);
|
|
$div->appendChild($message);
|
|
$div->appendChild($label_message);
|
|
$form->appendChild($div);
|
|
} else {
|
|
$form->appendChild($label_name);
|
|
$form->appendChild($name);
|
|
$form->appendChild($label_email);
|
|
$form->appendChild($email);
|
|
$form->appendChild($label_message);
|
|
$form->appendChild($message);
|
|
}
|
|
$form->appendChild($this->generateCaptcha());
|
|
if ($group_input_and_labels) {
|
|
$div = $this->createAndSetupElement(
|
|
'div', '', ['class' => 'fieldwrapper']
|
|
);
|
|
$div->appendChild($captcha);
|
|
$div->appendChild($label_captcha);
|
|
$form->appendChild($div);
|
|
} else {
|
|
$form->appendChild($label_captcha);
|
|
$form->appendChild($captcha);
|
|
}
|
|
|
|
$form->appendChild($submit);
|
|
// ADD CSRF CHECK
|
|
$form->appendChild($this->generateCsrfInput());
|
|
return $form;
|
|
}
|
|
|
|
private function generateCsrfInput()
|
|
{
|
|
// create token and save to session
|
|
$token = bin2hex(random_bytes(6));
|
|
$this->getSession()->set(self::KEY_CSRF_TOKEN, $token);
|
|
return $this->createAndSetupElement(
|
|
'input',
|
|
'',
|
|
[
|
|
'type' => 'hidden',
|
|
'name' => self::KEY_CSRF_TOKEN,
|
|
'value' => $token,
|
|
]
|
|
);
|
|
}
|
|
|
|
private function validateCsrfInput() : bool
|
|
{
|
|
if (!$this->formSubmitted()) {
|
|
return true;
|
|
}
|
|
|
|
$token = $this->getSession()->get(self::KEY_CSRF_TOKEN);
|
|
if (
|
|
is_null($_POST[self::KEY_CSRF_TOKEN])
|
|
|| $_POST[self::KEY_CSRF_TOKEN] !== $token
|
|
) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private function generateCaptcha()
|
|
{
|
|
$builder = new CaptchaBuilder();
|
|
$builder->build();
|
|
$this->getSession()->set(self::KEY_CAPTCHA_PHRASE, $builder->getPhrase());
|
|
$img_captcha = $this->createAndSetupElement(
|
|
'img',
|
|
'',
|
|
[
|
|
'id' => 'captcha',
|
|
'src' => $builder->inline(),
|
|
]
|
|
);
|
|
return $img_captcha;
|
|
}
|
|
|
|
private function validateCaptcha() : bool
|
|
{
|
|
if (!$this->formSubmitted()) {
|
|
return true;
|
|
}
|
|
|
|
$valid_phrase = $this->getSession()->get(self::KEY_CAPTCHA_PHRASE);
|
|
if (
|
|
is_null($_POST[self::KEY_CAPTCHA_PHRASE])
|
|
|| $_POST[self::KEY_CAPTCHA_PHRASE] !== $valid_phrase
|
|
) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private function formSubmitted()
|
|
{
|
|
return (
|
|
!is_null($_POST['name'])
|
|
|| !is_null($_POST['email'])
|
|
|| !is_null($_POST['message'])
|
|
) ? true : false;
|
|
}
|
|
|
|
private function sendMail($payload, $options)
|
|
{
|
|
mail(
|
|
$options[self::OPTIONS_MAIL_TO],
|
|
'['.$this->getTLD().'] '.$payload[self::KEY_POST_NAME],
|
|
$payload[self::KEY_POST_MESSAGE],
|
|
'From: '.$options[self::OPTIONS_MAIL_FROM]
|
|
."\r\n"
|
|
.'Reply-To: '.$payload[self::KEY_POST_EMAIL]
|
|
."\r\n"
|
|
.'MIME-Version: 1.0'
|
|
."\r\n"
|
|
.'Content-type: text/plain; charset=UTF-8'
|
|
."\r\n"
|
|
);
|
|
}
|
|
|
|
public function run(array $options = []) : bool
|
|
{
|
|
|
|
// set default options
|
|
$options += [
|
|
self::OPTIONS_REDIRECT_URI => "/"
|
|
];
|
|
|
|
if (!$this->validateCsrfInput()) {
|
|
$this->getSession()->getFlashBag()->add(
|
|
'error',
|
|
$this->loadTextFile(Resolve::arrayToId([
|
|
$this->getLanguage(),
|
|
'flashmessages',
|
|
'error',
|
|
'csrf-failed'
|
|
]))
|
|
);
|
|
}
|
|
if (!$this->validateCaptcha()) {
|
|
$this->getSession()->getFlashBag()->add(
|
|
'error',
|
|
$this->loadTextFile(Resolve::arrayToId([
|
|
$this->getLanguage(),
|
|
'flashmessages',
|
|
'error',
|
|
'captcha-failed'
|
|
]))
|
|
);
|
|
}
|
|
|
|
// send form again if captcha or csrf not valid, or form not submitted yet
|
|
if (
|
|
!$this->validateCaptcha()
|
|
|| !$this->validateCsrfInput()
|
|
|| !$this->formSubmitted()
|
|
) {
|
|
$form = $this->createForm();
|
|
$this->appendChild($form);
|
|
return true;
|
|
}
|
|
|
|
// send email
|
|
if (
|
|
!is_null($options[self::OPTIONS_MAIL_TO])
|
|
|| !is_null($options[self::OPTIONS_MAIL_FROM])
|
|
) {
|
|
$this->sendMail($_POST, $options);
|
|
}
|
|
// notify user about success
|
|
$this->getSession()->getFlashBag()->add(
|
|
'success',
|
|
$this->loadTextFile(Resolve::arrayToId([
|
|
$this->getLanguage(),
|
|
'flashmessages',
|
|
'success',
|
|
'thanks-for-message'
|
|
]))
|
|
);
|
|
|
|
header('Location: '.$options[self::OPTIONS_REDIRECT_URI]);
|
|
exit; // if not present, session gets not saved
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
public function onWebsocketRequest(
|
|
\Lewp\Websocket\Message $message
|
|
) : \Lewp\Websocket\Message {
|
|
}
|
|
};
|
|
|