setPayload(); } protected function setPayload() { $this->formModel = $this->getModel('Visforms', 'Site'); } public function captcha() { require_once JPATH_ROOT ."/components/com_visforms/captcha/securimage.php"; $model = $this->formModel; $config = Factory::getApplication()->getConfig(); $options = array( 'captchaId' => (!empty($this->input->get('tid'))) ? $this->input->get('tid') : Session::getFormToken(), 'no_session' => true, 'use_database' => true, 'database_table' => $config->get('dbprefix') . 'viscaptchacodes' ); // only try to set options if we have an id parameter in query else we use the captcha default options $formid = $this->input->get('id', null); if (!empty($formid)) { $visform = $model->getForm(); foreach ($visform->viscaptchaoptions as $name => $value) { // make names shorter and set all captchaoptions as properties of form object $options[$name] = $value; } // $model->getForm() sets the form in the user state, we have to remove it $this->app->setUserState('com_visforms.' . $visform->context, null); } $img = new \Securimage($options); $img->namespace = 'form' . $this->input->getInt('id', 0); $img->ttf_file = JPATH_ROOT . "/components/com_visforms/captcha/elephant.ttf"; $img->show(); } public function sendVerficationMail() { if (!$this->checkToken()) { header('HTTP/1.1 403 Forbidden'); echo Text::_('JINVALID_TOKEN'); $this->app->close(); } $verification = new \Visolutions\Component\Visforms\Site\Model\Helper\Mail\Verification(); // clear buffer $buffer = ob_get_contents(); ob_clean(); echo $verification->sendVerificationMail(); $this->app->close(); } public function checkVerificationCode() { if (!Session::checkToken()) { echo '0'; $this->app->close(); } $verificationMail = $this->input->post->get('verificationAddr', '', 'STRING'); $code = $this->input->post->get('code', '', 'STRING'); $validation = new VerificationCodeValidation(array('value' => $code, 'verificationAddr' => $verificationMail)); $valid = $validation->validate(); // clear buffer $buffer = ob_get_contents(); ob_clean(); echo (empty($valid)) ? '0' : '1'; $this->app->close(); } public function reloadOptionList() { $this->prepareReload(); $context = $this->formModel->getContext(); try { $field = $this->getReloadFieldDefinition(); } catch (\RuntimeException $e) { header('HTTP/1.1 400 Bad Request'); echo $e->getMessage(); $this->app->close(); } if ($field) { $defaultValues = VisformsHelper::registryArrayFromString($field->defaultvalue); $name = 'f_' .$field->typefield . '_sql'; $sql = $defaultValues[$name]; $options = HTMLHelper::_('visformsselect.getOptionsFromSQL', $sql, $context); $return = array(); if (!empty($defaultValues['f_selectsql_render_as_datalist'])) { foreach ($options as $option) { $return[] = ''; $return[] = ''; $return[] = $option['label']; $return[] = ''; $return[] = ''; } } else { $chooseValueText = (empty($defaultValues['f_selectsql_customselectvaluetext'])) ? Text::_('CHOOSE_A_VALUE') : $defaultValues['f_selectsql_customselectvaluetext']; if ((empty($defaultValues['f_selectsql_attribute_multiple'])) && (empty($defaultValues['f_selectsql_attribute_size']))) { $return[] = ''; } if (!empty($options)) { $usedOptsValues = array(); if (!empty($defaultValues['f_selectsql_uniquevaluesonly'])) { $fid = $this->input->post->get('postid', 0, 'cmd'); $cid = $this->input->get('cid', 0, 'cmd'); $usedOpts = HTMLHelper::_('visformsselect.getStoredUserInputs', $field->id, $fid, $cid); // technicly radiofields can only have one value stored in db // but for sake of less if then else code we treat them equally to selects and multicheckboxes, which can have multiselction if (!empty($usedOpts)) { foreach ($usedOpts as $usedOpt) { $usedOptValues = HTMLHelper::_('visformsselect.explodeMsDbValue', $usedOpt); foreach ($usedOptValues as $usedOptValue) { $usedOptsValues[] = $usedOptValue; } } } } foreach ($options as $option) { $disabled = (in_array($option['value'], $usedOptsValues)) ? 'disabled' : ''; $return[] =''; } } } } else { header('HTTP/1.1 400 Bad Request'); echo Text::_('COM_VISFORMS_RELOAD_OPTION_LIST_FIELD_NOT_FOUND'); $this->app->close(); } // clear buffer $buffer = ob_get_contents(); ob_clean(); echo implode('', $return); $this->app->close(); } public function reloadValue() { $this->prepareReload(); $context = $this->formModel->getContext(); try { $field = $this->getReloadFieldDefinition(); } catch (\RuntimeException $e) { header('HTTP/1.1 400 Bad Request'); echo $e->getMessage(); $this->app->close(); } if ($field) { $defaultValues = VisformsHelper::registryArrayFromString($field->defaultvalue); $name = 'f_' .$field->typefield . '_sql'; $sql = $defaultValues[$name]; try { $sqlHelper = new SqlHelper($sql, $context); $value = $sqlHelper->getItemsFromSQL('loadResult'); } catch (\Exception $e) { $value = ''; } } else { header('HTTP/1.1 400 Bad Request'); echo Text::_('COM_VISFORMS_RELOAD_OPTION_LIST_FIELD_NOT_FOUND'); $this->app->close(); } // clear buffer $buffer = ob_get_contents(); ob_clean(); echo $value; $this->app->close(); } public function getUserInputs() { if (!$this->checkAjaxSessionToken()) { header('HTTP/1.1 403 Forbidden'); echo Text::_('JINVALID_TOKEN'); $this->app->close(); } $context = $this->formModel->getContext(); $userInputs = $this->app->getUserState('com_visforms.userinputs.' . $context, null); $buffer = ob_get_contents(); ob_clean(); if ($userInputs) { $this->app->setUserState('com_visforms.userinputs.' . $context, null); echo $userInputs; $this->app->close(); } else { echo json_encode(array(), JSON_FORCE_OBJECT); $this->app->close(); } } protected function checkAjaxSessionToken() { $token = Session::getFormToken(); $dataToken = $this->input->get($token, null, 'cmd'); if ((is_null($dataToken)) || !((int) $dataToken === 1)) { return false; } return true; } protected function prepareReload() { // disable ajax request if we are in Yootheme Builder // form has hidden input previewOnly with value 1 $previewOnly = $this->input->get('previewOnly', 0, 'int'); if ($previewOnly) { $this->app->close(); } if (!$this->checkAjaxSessionToken()) { header('HTTP/1.1 403 Forbidden'); echo Text::_('JINVALID_TOKEN'); $this->app->close(); } } // throws db exception protected function getReloadFieldDefinition() { $reloadFieldId = $this->input->get('reloadId', 0, 'int'); $db = Factory::getContainer()->get(DatabaseInterface::class); $query = $db->createQuery(); $query->select($db->qn(array('id', 'typefield', 'defaultvalue'))) ->from($db->qn('#__visfields')) ->where($db->qn('id') . ' = ' . $reloadFieldId) ->where($db->qn('published') . ' = ' . 1); try { $db->setQuery($query); return $db->loadObject(); } catch (\RuntimeException $e) { throw new \RuntimeException($e->getMessage()); } } public function send() { Session::checkToken() or jexit(Text::_('JINVALID_TOKEN')); $model = $this->formModel; $visform = $model->getForm(); // the display state is use in the field.php function setQueryValue in order to decide if url params from a get request should be stored in the session // url params (from get) are only stored if $displayStateIsNew and not in an edit view task // if we are in the send task, make sure, the display state is set to $displayStateIsRedisplay before any further actions are performed $this->setDisplayState($visform); $app = $this->app; $return = $this->input->post->get('return'); // if we come from module or plugin we remove a potential page cache created by system cache plugin of the page with the form $url = isset($return) ? HTMLHelper::_('visforms.base64_url_decode', $return) : ''; if (!empty($url)) { $cache = Factory::getContainer()->get(CacheControllerFactoryInterface::class)->createCacheController('callback', ['defaultgroup' => 'page']); $folder = Path::clean(JPATH_CACHE . '/page'); // clean page cache, used by system cache plugin if (file_exists($folder)) { $cacheresult = $cache->remove($url, 'page'); } } // Total length of post back data in bytes. $contentLength = $this->input->server->get('CONTENT_LENGTH', 0, 'INT'); // Maximum allowed size of post back data in MB. $postMaxSize = MediaHelper::toBytes(ini_get('post_max_size')); // Maximum allowed size of script execution in MB. $memoryLimit = MediaHelper::toBytes(ini_get('memory_limit')); // ToDo: why here: is set in model->getForm if (!(isset($visform->errors))) { $visform->errors = array(); } // Check for the total size of post back data. if (($postMaxSize > 0 && $contentLength > $postMaxSize) || ($memoryLimit != -1 && $contentLength > $memoryLimit)) { array_push($visform->errors, Text::_('COM_VISFORMS_ERROR_WARNUPLOADTOOLARGE')); $this->setErrorParameter($visform, 'upload'); return $this->setErrorRedirect($url); } $fields = $model->getValidatedFields(); if ((!(count($_POST) > 0)) || (!isset($_POST['postid'])) || ($_POST['postid'] != $visform->id)) { $this->addLogEntry(Text::_('COM_VISFORMS_INVALIDE_POST_ERROR')); array_push($visform->errors, Text::_('COM_VISFORMS_INVALID_POST')); $this->setErrorParameter($visform, 'post'); // Show form again, keep values already typed in if ($url != "") { $this->setRedirect(Route::_($url, false)); return false; } else { $this->display(); return false; } } // include plugin spambotcheck if (isset($visform->spambotcheck) && $visform->spambotcheck == 1) { PluginHelper::importPlugin('visforms'); $spambotCheckEvent = new VisformsSpambotCheckEvent('onVisformsSpambotCheck', [ 'context' => 'com_visforms.form', ]); $results = $this->getDispatcher()->dispatch('onVisformsSpambotCheck', $spambotCheckEvent)->getArgument('result', []); foreach ($results as $result) { if ($result === true) { array_push($visform->errors, Text::_('PLG_VISFORMS_SPAMBOTCHECK_USER_LOGIN_SPAM_TXT')); $this->setErrorParameter($visform, 'spambotcheck'); // Show form again, keep values already typed in return $this->setErrorRedirect($url); } } } // Check that data is ok, in case that javascript may not work properly foreach ($fields as $field) { if (isset($field->isValid) && $field->isValid === false) { $this->setErrorParameter($visform, 'field'); // we have at least one invalid field // Show form again, keep values already typed in return $this->setErrorRedirect($url); } } // timetrap okay? // ToDo: consider: if a form is redisplayed after a php error, the time needed to complete the form is probably much shorter // Maybe keep start value in the timetrap field?? $timeTrap = new TimeTrap($visform); $timeTrap->validate(); if (!$timeTrap->isValid()) { array_push($visform->errors, $timeTrap->getErrorMessage()); $this->setErrorParameter($visform, 'timetrap'); // Show form again, keep values already typed in return $this->setErrorRedirect($url); } // honeypot empty? if (isset($visform->honeypot) && $visform->honeypot == 1) { $honeypotValue = $this->input->post->get($visform->context.'honeypot', '', 'Raw'); if ($honeypotValue !== '') { array_push($visform->errors, Text::_('COM_VISFORMS_HONEYPOT_VALIDATION_FAILED')); $this->setErrorParameter($visform, 'honeypot'); // Show form again, keep values already typed in return $this->setErrorRedirect($url); } } // Captcha ok? if ($visform->captcha == 1) { $config = Factory::getApplication()->getConfig(); $options = array( 'captchaId' => Session::getFormToken(), 'no_session' => true, 'use_database' => true, 'database_table' => $config->get('dbprefix') . 'viscaptchacodes' ); require_once JPATH_ROOT ."/components/com_visforms/captcha/securimage.php"; $responseField = $visform->context . 'viscaptcha_response'; $img = new \Securimage($options); $img->namespace = 'form' . $this->input->getInt('id', 0, 'int'); $valid = $img->check($_POST[$responseField]); if ($valid == false) { array_push($visform->errors, Text::_('COM_VISFORMS_RECAPTCHA_ERROR') . ' ' . Text::_("COM_VISFORMS_CODE_INVALID")); $this->setErrorParameter($visform, 'viscaptcha'); // Show form again, keep values already typed in return $this->setErrorRedirect($url); } } if ($visform->captcha == 3) { $captcha = New Hcaptcha(); if ($captcha->isEnabled()) { $token = $this->input->post->getString('h-captcha-response', ''); $captcha->validate($token); if (!$captcha->isValid()) { $errorMessage = $captcha->getErrorMessage(); array_push($visform->errors, $errorMessage); $this->setErrorParameter($visform, 'hcaptcha'); // Show form again, keep values already typed in return $this->setErrorRedirect($url); } } } if ($visform->captcha == 4 || $visform->captcha == 5) { $captcha = New Recaptcha($visform); if ($captcha->isEnabled()) { $token = $this->input->post->getString('g-recaptcha-response', ''); $captcha->validate($token); if (!$captcha->isValid()) { $errorMessage = $captcha->getErrorMessage(); array_push($visform->errors, $errorMessage); $this->setErrorParameter($visform, 'recaptcha'); // Show form again, keep values already typed in return $this->setErrorRedirect($url); } } } // Set data record overhead properties for placeholder replacement in form $model->setDataRecordOverheadValuesInForm(); // trigger before save event PluginHelper::importPlugin('visforms'); $beforeSaveFormEvent = new VisformsBeforeFormSaveEvent('onVisformsBeforeFormSave', [ 'subject' => $visform, 'context' => 'com_visforms.form', 'fields' => $fields ]); $onBeforeFormSaveResults = $this->getDispatcher()->dispatch('onVisformsBeforeFormSave', $beforeSaveFormEvent)->getArgument('result', []); if ((!empty($onBeforeFormSaveResults)) && is_array($onBeforeFormSaveResults)) { foreach ($onBeforeFormSaveResults as $onBeforeFormSaveResult) { if ($onBeforeFormSaveResult === false) { return $this->setErrorRedirect($url, $visform->id); } } } // process data according to form configuration // upload files // store data in db // send mails // only throws an error if something seriously went wrong // logs error messages if something less serious went wrong try { $model->saveData(); } catch (\RuntimeException $e) { // used in visforms plg_visforms to delete uploaded files, which are invalid due to error in save data $afterFormSaveErrorEvent = new VisformsAfterFormSaveErrorEvent('onVisformsAfterFormSaveError', [ 'subject' => $visform, 'context' => 'com_visforms.form', 'fields' => $fields ]); $this->getDispatcher()->dispatch('onVisformsAfterFormSaveError', $afterFormSaveErrorEvent); $message = $e->getMessage(); // throws an empty message, if unique values validation in database fails if (empty($message)) { $model->reloadFields(); } // we get a custom error message set by visforms array_push($visform->errors, $e->getMessage()); // Show form again, keep values already typed in return $this->setErrorRedirect($url, $visform->id); } // trigger after save event // used in visforms plg_visforms to delete uploaded files if required by form configuration $afterFormSaveEvent = new VisformsAfterFormSaveEvent('onVisformsAfterFormSave', [ 'subject' => $visform, 'context' => 'com_visforms.form', 'fields' => $fields ]); $this->getDispatcher()->dispatch('onVisformsAfterFormSave', $afterFormSaveEvent); // trigger before success action event, allow overriding properties in $visforms $afterBeforeSuccessEvent = new VisformsBeforeSuccessActionEvent('onVisformsBeforeSuccessAction', [ 'subject' => $visform, 'context' => 'com_visforms.form', 'fields' => $fields ]); $this->getDispatcher()->dispatch('onVisformsBeforeSuccessAction', $afterBeforeSuccessEvent); // clear user state $app->setUserState('com_visforms.' . $visform->context, null); $app->setUserState('com_visforms.urlparams.' . $visform->context, null); $app->setUserState('com_visforms.userinputs.' . $visform->context, null); // redirect to specific url no message! // get potential custom redirect urls from post $rawPlgRedirectUrl = $this->input->post->get('redirecturl', null, 'cmd'); $plgRedirectUrl = isset($rawPlgRedirectUrl) ? HTMLHelper::_('visforms.base64_url_decode', $rawPlgRedirectUrl) : ''; if (!empty($visform->allow_content_plugin_custom_redirect) && !empty($plgRedirectUrl)) { $visform->redirecturl = $plgRedirectUrl; } if (!empty($visform->redirecturl)) { // Allow use of placeholder in Redirect URL; Replace them $visform->redirecturl = $model->replacePlaceholder($visform, $visform->redirecturl); $tmpUrl = new Uri($visform->redirecturl); $query = $tmpUrl->getQuery(true); $urlParams = $model->getRedirectParams($fields, $query, $visform->context); if (!empty($urlParams)) { $tmpUrl->setQuery($urlParams); $visform->redirecturl = $tmpUrl->toString(); } $this->setRedirect(Route::_($visform->redirecturl, false)); return true; // no redirect to specific url, a result message is displayed somewhere } else { $msg = $this->createMessageText($visform, $url); $messageTextEvent = new VisformsMessageTextEvent('onVisformsMessageText', [ 'subject' => $visform, 'context' => 'com_visforms.form', 'fields' => $fields, 'msg' => $msg ]); $msg = $this->getDispatcher()->dispatch('onVisformsMessageText', $messageTextEvent)->getArgument('msg', $msg); if (!empty($visform->redirect_to_previous_page)) { if (empty($visform->message_position)) { // Joomla! message does not trigger content plugin. So we do it here, although the result may no be completely ok. // content plugins which need to add custom css or javascript to page cannot be used in this case! $msg = HTMLHelper::_('content.prepare', $msg); $app->enqueueMessage($msg); } else { $app->setUserState('com_visforms.messages.' . $visform->context, $msg); } if (!empty($url)) { $this->setRedirect(Route::_($url, false)); } else { $this->setFallbackRedirect(); // $this->setRedirect(Route::_(Uri::base(), false)); } return true; } else { $correspondingFormMenuItem = $model->checkFormViewMenuItemExists($visform->id); // no text result or no menuitem for form // Redirect to base url; show message in alert. if (empty($visform->textresult) || empty($correspondingFormMenuItem)) { $app->enqueueMessage($msg); $this->setFallbackRedirect(); // this code does not add a potential tmpl parameter to redirect url // $this->setRedirect(Route::_(Uri::base(), false)); return true; } else { // context must be context of visforms view via menu item! $context = 'form' . $visform->id; $app->setUserState('com_visforms.messages.' . $context, $msg); if ($tmpl = $this->input->get('tmpl', null, 'cmd')) { $tmpl = "&tmpl=" . $tmpl; } $this->setRedirect(Route::_('index.php?option=com_visforms&view=visforms&layout=message&id=' . $visform->id . '&Itemid=' . $correspondingFormMenuItem . $tmpl, false)); return true; } } } } protected function setFallbackRedirect() { $redirecturl = $this->getFallbackRedirect(); $this->setRedirect(Route::_($redirecturl, false)); } protected function setErrorRedirect($url = '', $formid = 0) { if ($url != '') { $this->setRedirect(Route::_($url, false)); } else { $this->display(); } return false; } // the display state is use in the field.php function setQueryValue in order to decide if url params from a get request should be stored in the session // url params (from get) are only stored if $displayStateIsNew protected function setDisplayState($visform) { if (isset($visform->displayState) && $visform->displayState === VisformsModelSite::$displayStateIsNew) { $visform->displayState = VisformsModelSite::$displayStateIsRedisplay; $this->app->setUserState('com_visforms.' . $visform->context, $visform); } } protected function createMessageText($visform, $returnUrl) { $returnLink = (empty($visform->redirect_to_previous_page)) ? $this->createReturnLinkHtml($visform, $returnUrl) : ''; $pdfDownloadLink = $this->createPdfDownloadLink($visform); if (empty($visform->textresult)) { return Text::_('COM_VISFORMS_FORM_SEND_SUCCESS') . $pdfDownloadLink . $returnLink; } $message = $this->formModel->replacePlaceholder($visform, $visform->textresult); $message = $this->deleteIfEmpty($message); $message = str_replace('@', '@', $message); return $message . $pdfDownloadLink . $returnLink; } protected function createReturnLinkHtml($visform, $returnUrl) { if (empty($returnUrl)) { return ''; } $showReturnLink = (!empty($visform->textresult_previouspage_link)) ? $visform->textresult_previouspage_link : 0; if (empty($showReturnLink)) { return ''; } $linkText = (!empty($visform->return_link_text)) ? $visform->return_link_text : Text::_('COM_VISFORMS_RETURN_TO_PREVIOUS_PAGE_LINK_TEXT'); return '

' . $linkText . '

'; } protected function createPdfDownloadLink($visform): string { $html = ''; if (!AefHelper::checkAEF()) { return $html; } if (empty($visform->display_pdf_download_link) || empty($visform->pdf_download_link_template)) { return $html; } $this->app->setUserState('visforms'. $visform->id . '.pdf.requestdatas', $visform); foreach ($visform->pdf_download_link_template as $tmplId) { // without subscription there is no Vispdf Table! try { $pdfTable = new \Visolutions\Component\Visforms\Administrator\Table\VispdfTable(Factory::getContainer()->get(DatabaseInterface::class)); } catch (\RuntimeException $e) { return $html; } $loaded = $pdfTable->load((int) $tmplId); if ($loaded === false) { continue; } if (!$pdfTable->published) { continue; } $pdfTable->filenames = VisformsHelper::registryArrayFromString($pdfTable->filenames); $linkText = $this->getPdfName($pdfTable, $visform); $linkText = (!empty($linkText)) ? Text::sprintf('COM_VISFORMS_DOWNLOAD_AS_PDF_TEXT_FOR_NAMED_DOCUMENT', $linkText) : Text::_('COM_VISFORMS_DOWNLOAD_AS_PDF_TEXT'); $pdfLink = htmlspecialchars(((!empty($base = Uri::base()) ? Uri::base() : '') . 'index.php?option=com_visforms&view=visformsdata&layout=data&task=visformsdata.renderPdfFromRequestData&id='.$visform->id . '&tmplid=' . $tmplId . '&' . Session::getFormToken() . '=1')); $html .= '

' . $linkText . '

'; } return $html; } protected function getPdfName($pdfItem, $form) { // no individual file name given if (empty($pdfItem->filenames['output-file-name'])) { return $pdfItem->title; } // individual file Name. Replace placeholder $linkText = trim($this->formModel->replacePlaceholder($form, $pdfItem->filenames['output-file-name'])); if (empty($linkText)) { return $pdfItem->title; } return $linkText; } // remove marked emtyp elements from HTML protected function deleteIfEmpty($text) { $helper = new DeleteIfEmpty($text); return $helper->deleteIfEmpty(); } protected function setErrorParameter($form, $type) { // as visforms always returns immediately if an error is detected, we only allow one value for the data-error-type // must be adapted, if we change the behaviour and do not stop and return immediately after the first error was detected // add data attribute $form->containerDataAttributes['data-error-type'] = 'data-error-type="' . $type . 'error"'; // include script file $form->scripts['phpError'] = true; } }