/** * Trigger a valid ajax action * @param $strGroup * @param $strAction * @param $objContext */ public static function runActiveAction($strGroup, $strAction, $objContext) { if (Request::getInstance()->isXmlHttpRequest()) { /** @var AjaxAction */ $objAction = Ajax::getActiveAction($strGroup, $strAction); if ($objAction === AJAX_ERROR_INVALID_GROUP) { header('HTTP/1.1 400 Bad Request'); die('Invalid ajax group.'); } else { if ($objAction === AJAX_ERROR_NO_AVAILABLE_ACTIONS) { header('HTTP/1.1 400 Bad Request'); die('No available ajax actions within given group.'); } else { if ($objAction === AJAX_ERROR_INVALID_ACTION) { header('HTTP/1.1 400 Bad Request'); die('Invalid ajax act.'); } else { if ($objAction !== null) { $objResponse = $objAction->call($objContext); /** @var Response */ if ($objResponse instanceof Response) { $objResponse->output(); } } } } } } }
public function __construct($arrAttributes = null) { // check against arrAttributes, as 'onsubmit_callback' => 'multifileupload_moveFiles' does not provide valid attributes if ($arrAttributes !== null && !$arrAttributes['uploadFolder']) { throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['noUploadFolderDeclared'], $this->name)); } $arrAttributes['uploadAction'] = static::$uploadAction; if (TL_MODE == 'FE') { $arrAttributes['uploadActionParams'] = http_build_query(AjaxAction::getParams(MultiFileUpload::NAME, static::$uploadAction)); } $arrAttributes['addRemoveLinks'] = isset($arrAttributes['addRemoveLinks']) ? $arrAttributes['addRemoveLinks'] : true; if (!is_array($arrAttributes['value']) && !Validator::isBinaryUuid($arrAttributes['value'])) { $arrAttributes['value'] = json_decode($arrAttributes['value']); } // bin to string -> never pass binary to the widget!! if ($arrAttributes['value']) { if (is_array($arrAttributes['value'])) { $arrAttributes['value'] = array_map(function ($val) { return \Validator::isBinaryUuid($val) ? \StringUtil::binToUuid($val) : $val; }, $arrAttributes['value']); } else { $arrAttributes['value'] = array(\Validator::isBinaryUuid($arrAttributes['value']) ? \StringUtil::binToUuid($arrAttributes['value']) : $arrAttributes['value']); } } parent::__construct($arrAttributes); $this->objUploader = new MultiFileUpload($arrAttributes); // add onsubmit_callback: move files after form submission $GLOBALS['TL_DCA'][$this->strTable]['config']['onsubmit_callback']['multifileupload_moveFiles'] = array('HeimrichHannot\\MultiFileUpload\\FormMultiFileUpload', 'moveFiles'); Ajax::runActiveAction(MultiFileUpload::NAME, MultiFileUpload::ACTION_UPLOAD, $this); }
protected function generateField($strName, $arrData, $skipValidation = false) { $strClass = $GLOBALS['TL_FFL'][$arrData['inputType']]; // overwrite the widget in readonly mode if ($this->viewMode == FORMHYBRID_VIEW_MODE_READONLY || $this->viewMode == FORMHYBRID_VIEW_MODE_DEFAULT && $this->addReadOnly && in_array($strName, $this->arrReadOnly)) { $strClass = 'HeimrichHannot\\FormHybrid\\FormReadonlyField'; $skipValidation = true; } $strInputMethod = $this->strInputMethod; // Continue if the class is not defined if (!class_exists($strClass)) { return false; } $arrWidgetErrors = array(); // contains the load_callback! $varDefault = $this->getDefaultFieldValue($strName, $arrData); $varValue = $varDefault; if ($this->isSubmitted && !$skipValidation) { $varValue = \Input::$strInputMethod($strName) !== null ? \Input::$strInputMethod($strName) : $varValue; $varValue = FormSubmission::prepareSpecialValueForSave($varValue, $arrData, $this->strTable, $this->intId, $varDefault, $arrWidgetErrors); } // overwrite required fields if ($this->overwriteRequired) { // set mandatory to false $arrData['eval']['mandatory'] = false; // overwrite mandatory by config if (!$arrData['eval']['mandatory'] && in_array($strName, $this->arrRequired)) { $arrData['eval']['mandatory'] = true; } } // prevent name for GET and submit widget, otherwise url will have submit name in if ($this->strMethod == FORMHYBRID_METHOD_GET && $arrData['inputType'] == 'submit') { $strName = ''; } $arrData['eval']['tagTable'] = $this->strTable; // always disable validation for filter form if ($this->isFilterForm) { $arrData['eval']['mandatory'] = false; } // to make captcha form related, add the form id without entity id if ($arrData['inputType'] == 'captcha') { $strName .= '_' . $this->getFormId(false); } $this->strField = $strName; $this->strInputName = $strName; $this->varValue = is_array($varValue) ? $varValue : \Controller::replaceInsertTags($varValue); $arrWidget = \Widget::getAttributesFromDca($arrData, $strName, is_array($varValue) ? $varValue : \Controller::replaceInsertTags($varValue), $strName, $this->strTable, $this); $this->updateWidget($arrWidget, $arrData); list($blnActive, $strSubPalette, $arrFields, $arrSubPaletteFields, $blnAutoSubmit, $blnToggleSubpalette) = $this->retrieveSubpaletteWithState($strName, array_keys($this->arrFields)); // support submitOnChange as form submission if ($arrData['eval']['submitOnChange'] && $blnToggleSubpalette) { if ($blnAutoSubmit) { $arrWidget['onchange'] = $this->async ? 'FormhybridAjaxRequest.asyncSubmit(this.form);' : "this.form.submit();"; } else { $strEvent = 'onclick'; switch ($arrData['inputType']) { case 'select': $strEvent = 'onchange'; break; } $arrWidget[$strEvent] = "FormhybridAjaxRequest.toggleSubpalette(this, 'sub_" . $strName . "', '" . $strName . "', '" . AjaxAction::generateUrl(Form::FORMHYBRID_NAME, 'toggleSubpalette') . "')"; unset($arrWidget['submitOnChange']); } } else { if ($arrWidget['submitOnChange']) { $strEvent = null; if ($arrWidget['onchange']) { $strEvent = 'onchange'; } else { if ($arrWidget['onclick']) { $strEvent = 'onclick'; } } if ($strEvent !== null) { $arrWidget[$strEvent] = "FormhybridAjaxRequest.reload('" . $this->getFormId() . "', '" . AjaxAction::generateUrl(Form::FORMHYBRID_NAME, 'reload') . "')"; unset($arrWidget['submitOnChange']); } } } $objWidget = new $strClass($arrWidget); if (isset($arrData['formHybridOptions'])) { $arrFormHybridOptions = $arrData['formHybridOptions']; $this->import($arrFormHybridOptions[0]); $objWidget->options = $this->{$arrFormHybridOptions}[0]->{$arrFormHybridOptions}[1](); } if ($objWidget instanceof \uploadable) { $this->hasUpload = true; } // always xss clean the user input (also if filter, non-model submission, ...) -> done another time // FrontendWidget::validateGetAndPost() in $objWidget->value = FormHelper::xssClean($objWidget->value, $arrData['eval']['allowHtml']); if ($this->isSubmitted) { // add filter class if filter is active if ($objWidget->value && $this->isFilterForm) { $objWidget->class = 'filtered'; } // do not validate fields if not submitted or skipvalidation issset // do not submit if ajax request and group is not formhybrid, for example multifileupload (otherwise captcha fields will be validated does not match visible one) if (!($this->isSkipValidation() || $skipValidation) && Ajax::isRelated(Form::FORMHYBRID_NAME) !== false) { FrontendWidget::validateGetAndPost($objWidget, $this->strMethod, $this->getFormId(), $arrData); if (is_array($arrWidgetErrors)) { foreach ($arrWidgetErrors as $strError) { $objWidget->addError($strError); } } // Make sure unique fields are unique if ($arrData['eval']['unique'] && $varValue != '' && !\Database::getInstance()->isUniqueValue($this->strTable, $strName, $varValue, $this->intId > 0 ? $this->intId : null)) { $objWidget->addError(sprintf($GLOBALS['TL_LANG']['ERR']['unique'], $arrData['label'][0] ?: $strName)); } // trigger save_callbacks before assertion of the new value to objActiveRecord if (is_array($arrData['save_callback'])) { foreach ($arrData['save_callback'] as $callback) { if (is_array($callback)) { $this->import($callback[0]); $varValue = $this->{$callback}[0]->{$callback}[1]($varValue, $this); } elseif (is_callable($callback)) { $varValue = $callback($varValue, $this); } } } if ($objWidget->hasErrors()) { $this->doNotSubmit = true; $this->arrInvalidFields[] = $strName; } elseif ($arrData['inputType'] == 'tag' && in_array('tags_plus', \ModuleLoader::getActive())) { $varValue = deserialize($objWidget->value); if (!is_array($varValue)) { $varValue = array($varValue); } if ($this->intId) { \HeimrichHannot\TagsPlus\TagsPlus::saveTags($this->strTable, $this->intId, array_map('urldecode', $varValue)); } } elseif ($objWidget->submitInput()) { // save non escaped to database if (is_array($objWidget->value)) { $this->objActiveRecord->{$strName} = array_map(function ($varVal) use($arrData) { $varVal = FormSubmission::prepareSpecialValueForSave($varVal, $arrData, $this->strTable, $this->intId); if (is_array($varVal)) { foreach ($varVal as $key => $val) { $varVal[$key] = html_entity_decode($val); } } else { $varVal = html_entity_decode($varVal); } return $varVal; }, $objWidget->value); } else { $this->objActiveRecord->{$strName} = html_entity_decode(FormSubmission::prepareSpecialValueForSave($objWidget->value, $arrData, $this->strTable, $this->intId)); } } elseif ($objWidget instanceof \uploadable && $arrData['inputType'] == 'multifileupload') { $strMethod = strtolower($this->strMethod); if (\Input::$strMethod($strName)) { $arrValue = json_decode(\Input::$strMethod($strName)); if (!empty($arrValue)) { $arrValue = array_map(function ($val) { return \String::uuidToBin($val); }, $arrValue); $this->objActiveRecord->{$strName} = serialize($arrValue); } else { $this->objActiveRecord->{$strName} = serialize($arrValue); } } // delete the files scheduled for deletion $objWidget->deleteScheduledFiles(json_decode(\Input::$strMethod('deleted_' . $strName))); } elseif ($objWidget instanceof \uploadable && isset($_SESSION['FILES'][$strName]) && \Validator::isUuid($_SESSION['FILES'][$strName]['uuid'])) { $this->objActiveRecord->{$strName} = $_SESSION['FILES'][$strName]['uuid']; } } } return $objWidget; }
/** * Get config value from transformed arrData and add logic to modify the value here * @param $strKey * * @return mixed|string */ public function __get($strKey) { $varValue = $this->arrData[$strKey]; switch ($strKey) { case 'strAction': if ($varValue && ($objActionPage = \PageModel::findWithDetails($varValue)) !== null) { $varValue = \Controller::generateFrontendUrl($objActionPage->row(), null, null, true); } else { $varValue = Url::removeQueryString(array('file'), \Environment::get('uri')); // remove all query parameters within ajax request if (Ajax::isRelated(Form::FORMHYBRID_NAME) !== false) { $varValue = AjaxAction::removeAjaxParametersFromUrl($varValue); } } // async form if ($this->async) { $varValue = AjaxAction::generateUrl(Form::FORMHYBRID_NAME, 'asyncFormSubmit'); } // add hash if ($this->addHashToAction) { $varValue .= '#' . ($this->customHash ?: $this->strFormId); } break; case 'arrDefaultValues': $varValue = FormHelper::getAssocMultiColumnWizardList($varValue, 'field'); break; } return $varValue; }
protected function compile() { $this->Template->headline = $this->headline; $this->Template->hl = $this->hl; $this->Template->wrapperClass = $this->strWrapperClass; $this->Template->wrapperId = $this->strWrapperId; $this->strFormId = $this->formHybridDataContainer . '_' . $this->id; $strAction = $this->defaultAction ?: \Input::get('act'); $this->arrEditable = deserialize($this->formHybridEditable, true); $this->strToken = $this->strToken ?: \Input::get('token'); // Do not change this order (see #6191) $this->Template->style = !empty($this->arrStyle) ? implode(' ', $this->arrStyle) : ''; $this->Template->class = trim('mod_' . $this->type . ' ' . $this->cssID[1]); $this->Template->cssID = $this->cssID[0] != '' ? ' id="' . $this->cssID[0] . '"' : ''; $this->Template->inColumn = $this->strColumn; if ($this->Template->headline == '') { $this->Template->headline = $this->headline; } if ($this->Template->hl == '') { $this->Template->hl = $this->hl; } if (!empty($this->classes) && is_array($this->classes)) { $this->Template->class .= ' ' . implode(' ', $this->classes); } $this->addDefaultArchive(); // at first check for the correct request token to be set if (!$this->deactivateTokens && !\RequestToken::validate($this->strToken)) { if (!$this->blnSilentMode) { StatusMessage::addError(sprintf($GLOBALS['TL_LANG']['frontendedit']['requestTokenExpired'], Url::replaceParameterInUri(Url::getUrl(), 'token', \RequestToken::get())), $this->id, 'requestTokenExpired'); } return; } if ($this->formHybridAllowIdAsGetParameter) { $intId = \Input::get($this->formHybridIdGetParameter); if (is_numeric($intId)) { $this->intId = $intId; } } $strItemClass = \Model::getClassFromTable($this->formHybridDataContainer); // get id from share if ($strShare = \Input::get('share')) { if (($objItem = $strItemClass::findByShareToken($strShare)) !== null && !FormHybridList::shareTokenExpiredOrEmpty($objItem, time())) { $this->intId = $objItem->id; } } if (!$this->intId) { if (isset($GLOBALS['TL_HOOKS']['frontendEditAddNoIdBehavior']) && is_array($GLOBALS['TL_HOOKS']['frontendEditAddNoIdBehavior'])) { foreach ($GLOBALS['TL_HOOKS']['frontendEditAddNoIdBehavior'] as $arrCallback) { $this->import($arrCallback[0]); if ($this->{$arrCallback}[0]->{$arrCallback}[1]($this) === false) { return; } } } if ($this->noIdBehavior == 'error') { if (!$this->blnSilentMode) { StatusMessage::addError($GLOBALS['TL_LANG']['frontendedit']['noIdFound'], $this->id, 'noidfound'); } return; } elseif ($this->noIdBehavior == 'redirect' || $this->noIdBehavior == 'create_until') { $arrConditions = deserialize($this->existanceConditions, true); if ($this->existanceConditions && !empty($arrConditions)) { $arrColumns = array(); $arrValues = array(); foreach ($arrConditions as $arrCondition) { if (!$arrCondition['field']) { continue; } $arrColumns[] = $arrCondition['field'] . '=?'; $arrValues[] = $this->replaceInsertTags($arrCondition['value']); } if (!empty($arrColumns) && ($objItem = $strItemClass::findOneBy($arrColumns, $arrValues)) !== null) { $this->intId = $objItem->id; } } } if (!$this->intId) { if ($this->noIdBehavior == 'redirect') { if (!$this->blnSilentMode) { StatusMessage::addError($GLOBALS['TL_LANG']['frontendedit']['noIdFound'], $this->id, 'noidfound'); } return; } else { $strFormId = FormHelper::getFormId($this->formHybridDataContainer, $this->id); // get id from FormSession if ($_POST) { if ($intId = FormSession::getSubmissionId($strFormId)) { $this->intId = $intId; } } if (!$this->intId) { // if no id is given a new instance is initiated $objConfiguration = new FormConfiguration($this->arrData); // ajax handling, required in this manor, as we have no real ajax controller in contao and ajax request not related to this module // might trigger this module beforhand and new submission will be created after the submission was transfered to the user and id wont match any more if (Ajax::isRelated(Form::FORMHYBRID_NAME) !== null) { if ($intId = FormSession::getSubmissionId($strFormId)) { $this->intId = $intId; } else { $objConfiguration->forceCreate = true; } } $this->objForm = new $this->strFormClass($objConfiguration, $this->arrSubmitCallbacks, $this->intId ?: 0, $this); if ($intId = $this->objForm->getId()) { $this->intId = $intId; } } } } } // intId is set at this point! if (!$this->checkEntityExists($this->intId)) { if (!$this->blnSilentMode) { StatusMessage::addError($GLOBALS['TL_LANG']['formhybrid_list']['noPermission'], $this->id, 'nopermission'); } if (Ajax::isRelated(Form::FORMHYBRID_NAME)) { $objResponse = new ResponseError(); $objResponse->setResult(StatusMessage::generate($this->id)); $objResponse->output(); } return; } // page title if ($this->setPageTitle) { global $objPage; if (($objItem = General::getModelInstance($this->formHybridDataContainer, $this->intId)) !== null) { $objPage->pageTitle = $objItem->{$this->pageTitleField}; } } if ($strAction == FRONTENDEDIT_ACT_DELETE) { if ($this->checkDeletePermission($this->intId)) { $blnResult = $this->deleteItem($this->intId); if (\Environment::get('isAjaxRequest')) { die($blnResult); } // return to the list \Controller::redirect(Url::removeQueryString(array('act', 'id', 'token'), Url::getUrl())); } else { if (!$this->blnSilentMode) { StatusMessage::addError($GLOBALS['TL_LANG']['formhybrid_list']['noPermission'], $this->id, 'nopermission'); } return; } } else { if ($this->checkUpdatePermission($this->intId)) { // create a new lock if necessary if (in_array('entity_lock', \ModuleLoader::getActive()) && $this->addEntityLock) { if (\HeimrichHannot\EntityLock\EntityLockModel::isLocked($this->formHybridDataContainer, $this->intId, $this)) { $objLock = \HeimrichHannot\EntityLock\EntityLockModel::findActiveLock($this->formHybridDataContainer, $this->intId, $this); $objItem = General::getModelInstance($this->formHybridDataContainer, $this->intId); if (!$this->blnSilentMode) { $strMessage = \HeimrichHannot\EntityLock\EntityLock::generateErrorMessage($this->formHybridDataContainer, $this->intId, $this); if ($this->allowLockDeletion) { $strUnlockForm = $this->generateUnlockForm($objItem, $objLock); $strMessage .= $strUnlockForm; } StatusMessage::addError($strMessage, $this->id, 'locked'); } if ($this->readOnlyOnLocked) { $this->formHybridViewMode = FORMHYBRID_VIEW_MODE_READONLY; $this->formHybridReadonlyTemplate = 'formhybridreadonly_default'; } else { return; } } else { \HeimrichHannot\EntityLock\EntityLockModel::create($this->formHybridDataContainer, $this->intId, $this); } } if ($this->objForm === null) { $this->objForm = new $this->strFormClass(new FormConfiguration($this->arrData), $this->arrSubmitCallbacks, $this->intId, $this); } $this->Template->form = $this->objForm->generate(); $this->Template->item = $this->objForm->activeRecord; if (\Environment::get('isAjaxRequest') && \Input::get('scope') == 'modal') { $objItem = General::getModelInstance($this->formHybridDataContainer, $this->intId); $objModalWrapper = new \FrontendTemplate($this->modalTpl ?: 'formhybrid_reader_modal_bootstrap'); if ($objItem !== null) { $objModalWrapper->setData($objItem->row()); } $objModalWrapper->module = Arrays::arrayToObject($this->arrData); $objModalWrapper->item = $this->replaceInsertTags($this->Template->parse()); die($objModalWrapper->parse()); } } else { if (!$this->blnSilentMode) { StatusMessage::addError($GLOBALS['TL_LANG']['formhybrid_list']['noPermission'], $this->id, 'nopermission'); } return; } } }
/** * Hook upon PageRegular to register modal and render them * * @param $objPage * @param $objLayout * @param $objPageRegular * * @return void */ public function generatePageWithModal($objPage, $objLayout, &$objPageRegular) { // Do not handle the page if no modal item has been specified or page is no regular if (!\Input::get('modals') || $objPage->type != 'regular') { return; } $objModel = ModalModel::findPublishedByIdOrAliasWithoutLinkedPage(\Input::get('modals')); if ($objModel === null && $objPage->linkModal) { $objModel = ModalModel::findPublishedByIdOrAlias($objPage->modal); } $blnCheck = true; if ($objModel === null) { $blnCheck = false; } $objModel = $objModel->current(); $arrConfig = static::getModalConfig($objModel, $objLayout, $objPage); Ajax::runActiveAction(Modal::MODAL_NAME, 'redirect', new ModalAjax($objModel->current(), $arrConfig)); Ajax::runActiveAction(Modal::MODAL_NAME, 'show', new ModalAjax($objModel->current(), $arrConfig)); if (empty($arrConfig)) { $blnCheck = false; } if (!$blnCheck) { /** @var \PageError404 $objHandler */ $objHandler = new $GLOBALS['TL_PTY']['error_404'](); $objHandler->generate($objPage->id); } $back = \Controller::generateFrontendUrl($objPage->row(), null, null, true); $objModal = new Modal($objModel, $arrConfig); $objModal->setBackLink($back); // render modal within main, as it is the most commonly used region and enabled within contao by default $strBuffer = $objModal->generate(); $objPageRegular->Template->main .= $strBuffer; }