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