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 { } };