public function process(Request $request) { // Ajax-validation is only possible, if the _formID was submitted (automatically done by the FormBuilder). if (\Request::has('_formID')) { // The FormBuilder should have saved the requestObject this form uses inside the session. // We check, if it is there, and can continue only, if it is. $sessionKeyForRequestObject = 'htmlBuilder.formBuilder.requestObjects.' . \Request::input('_formID'); if (Session::has($sessionKeyForRequestObject)) { // Normally we assume a successful submission and return just an empty JSON-array. $returnCode = 200; $return = []; // We instantiate the requestObject. $formRequest = FormBuilderTools::getRequestObject(Session::get($sessionKeyForRequestObject)); // We instantiate a controller with the submitted request-data // and the rules and messages from the requestObject. $validator = Validator::make(\Request::all(), $formRequest->rules(), $formRequest->messages()); // Perform validation, extract error-messages for all fields on failure, put them inside a $return['errors']-array, and return status code 422. if ($validator->fails()) { $errors = []; foreach (array_dot(\Request::all()) as $fieldName => $fieldValue) { $fieldErrors = FormBuilderTools::extractErrorsForField($fieldName, $validator->errors()->getMessages(), \Request::all()); if (count($fieldErrors) > 0) { $errors[FormBuilderTools::convertArrayFieldDotNotation2HtmlName($fieldName)] = $fieldErrors; } } $return['errors'] = $errors; $returnCode = 422; } return new JsonResponse($return, $returnCode); } } }
/** * Add required-symbol to legend. */ protected function task_20_addRequiredSymbolToLegend() { if ($this->hasLegend()) { $fieldRules = app()['formbuilder']->getRulesForField($this->getName()); if (FormBuilderTools::existRules($fieldRules)) { if (is_string($fieldRules)) { $fieldRules = explode('|', $fieldRules); } if (array_search('required', $fieldRules) !== false) { $this->legend($this->getLegend() . '<sup>*</sup>'); } } } }
public function validate($attribute, $value, $parameters, $validator) { $isValid = true; // We only validate, if honeypot-protection is basically enabled in the config. if (config('htmlbuilder.formBuilder.honeypot.enabled')) { $honeypotFieldName = FormBuilderTools::getHoneypotFieldName(); $fullRequest = Request::all(); if (isset($fullRequest[$honeypotFieldName])) { if (strlen($fullRequest[$honeypotFieldName]) > 0) { $isValid = false; } } else { $isValid = false; } } return $isValid; }
/** * Gets the error(s) of field $name from $errors. * Furthermore: If it is the first element of an array (e.g. domainList.0.domainName), * the errors of the array itself are also returned. * For multidimensional arrays this works recursively. * Example: * This server-side request array: * domainList => [ * 0 => [ * domainLabel => 'first-domain', * domainPdo => 'at', * ], * 1 => [ * domainLabel => 'second-domain', * domainPdo => 'at', * ], * * Returns errors in the following structure: * * - Field 'domainList[0][domainLabel]' returns errors for: * - domainList[0][domainLabel] * - domainList[0] * - domainList * - Field 'domainList[0][domainPdo]' returns errors for: * - domainList[0][domainPdo] * - Field 'domainList[1][domainLabel]' returns errors for: * - domainList[1][domainLabel] * - domainList[1] * - Field 'domainList[1][domainPdo]' returns errors for: * - domainList[1][domainPdo] * * @param string $name: Name of the field (array-fields must be in dot-notation (e.g. "domainList.0.domainName")) * @param array $errors: Array of all errors (in dot-notation) * @param array $request: Array of full request (in dot-notation) * @return array */ public static function extractErrorsForField($name = '', $errors = [], $request = []) { $return = []; $isArray = false; // Check, if the name is an array-key (e.g. "domainList.0.domainName"). if (strpos($name, '.') !== false) { $isArray = true; } // If the field is the first element of an array, // we also add errors regarding the array itself. // (e.g. in case of a 'size:5' validation rule on the array itself) if ($isArray) { $arrayName = substr($name, 0, strrpos($name, '.')); $arrayFieldKey = substr($name, strrpos($name, '.') + 1); $addArrayErrors = true; if (array_has($request, $arrayName) && strval(key(array_get($request, $arrayName))) !== $arrayFieldKey) { $addArrayErrors = false; } if ($addArrayErrors) { $return = array_merge(FormBuilderTools::extractErrorsForField($arrayName, $errors, $request), $return); } } // Get the errors (if present) from $this->errors. if (isset($errors[$name])) { $return = array_merge($return, $errors[$name]); } return $return; }
/** * Gets the error(s) of a field currently stored in the FormBuilder-object. * Furthermore: If it is the first element of an array (e.g. domainName[0]), * the errors of the array itself are also returned. * This even works for multidimensional arrays. * Example: * This server-side request array: * domainList => [ * 0 => [ * domainLabel => 'first-domain', * domainPdo => 'at', * ], * 1 => [ * domainLabel => 'second-domain', * domainPdo => 'at', * ], * * Returns errors in the following structure: * * - Field 'domainList[0][domainLabel]' returns errors for: * - domainList[0][domainLabel] * - domainList[0] * - domainList * - Field 'domainList[0][domainPdo]' returns errors for: * - domainList[0][domainPdo] * - Field 'domainList[1][domainLabel]' returns errors for: * - domainList[1][domainLabel] * - domainList[1] * - Field 'domainList[1][domainPdo]' returns errors for: * - domainList[1][domainPdo] * * @param string $name * @return array */ public function getErrorsForField($name = '') { $errors = []; if (count($this->errors) > 0) { $errors = FormBuilderTools::extractErrorsForField(FormBuilderTools::convertArrayFieldHtmlName2DotNotation($name), $this->errors, Request::old()); } return $errors; }
/** * Applies laravel-validation-rules to properties of this object. * */ private function applyRules() { if (FormBuilderTools::existRules($this->rules)) { // Format the laravel-rules-array. $rules = FormBuilderTools::explodeRules($this->rules); foreach ($rules as $rule => $parameters) { switch ($rule) { case 'accepted': case 'required': $this->attrRequired = true; break; case 'not_numeric': $this->attrPattern = '\\D+'; break; case 'url': case 'active_url': $this->attrType = 'url'; break; case 'alpha': $this->attrPattern = '[a-zA-Z]+'; break; case 'alpha_dash': $this->attrPattern = '[a-zA-Z0-9_\\-]+'; break; case 'alpha_num': $this->attrPattern = '[a-zA-Z0-9]+'; break; case 'between': if ($this::TAG === 'input') { if ($this->attrType === 'number') { $this->attrMin = $parameters[0]; $this->attrMax = $parameters[1]; } else { $this->attrPattern .= '.{' . $parameters[0] . ',' . $parameters[1] . '}'; $this->attrMaxlength = $parameters[1]; } } else { if ($this::TAG === 'textarea') { $this->attrMaxlength = $parameters[1]; } } break; case 'in': $parameters = sizeof($parameters) == 1 ? $parameters[0] : '(' . join('|', $parameters) . ')'; $this->attrPattern = '^' . $parameters . '$'; break; case 'ip': //TODO break; case 'max': if ($this::TAG === 'input') { if ($this->attrType === 'number') { $this->attrMax = $parameters[0]; } else { $this->attrMaxlength = $parameters[0]; } } else { if ($this::TAG === 'textarea') { $this->attrMaxlength = $parameters[0]; } } break; case 'min': if ($this->attrType === 'number') { $this->attrMin = $parameters[0]; } else { if (isset($this->attrPattern)) { $this->attrPattern .= ".{" . $parameters[0] . ",}"; } else { $this->attrPattern = ".{" . $parameters[0] . ",}"; } } break; case 'not_in': $this->attrPattern = '(?:(?!^' . join('$|^', $parameters) . '$).)*'; break; case 'numeric': $this->attrType = 'number'; $this->attrPattern = '[+-]?\\d*\\.?\\d+'; break; case 'mimes': if (array_search('jpeg', $parameters) !== false) { array_push($parameters, 'jpg'); } $this->attrAccept = '.' . implode(',.', $parameters); break; } } } }
/** * Handle setting the session-info and generation of the captcha-field, if captcha-protection is enabled in the config. */ protected function task_1100_handleCaptchaProtection() { if (config('htmlbuilder.formBuilder.captcha.enabled')) { // We retrieve the captcha-rules. $captchaRules = app()['formbuilder']->getRulesForField('_captcha'); // If there are any, ... if (strlen($captchaRules) > 0) { // Captcha-protection only works, if a request-object was stated via the requestObject() method, // so we throw an exception, if this was not the case. if (!$this->hasRequestObject()) { throw new MandatoryOptionMissingException('The form with ID "' . app()['formbuilder']->getID() . '"" should display a captcha, ' . 'but no request-object was stated via the Form::open()->requestObject() method. ' . 'Captcha only works if this is the case.'); } // Set where the captcha-answer will be stored in the session. $sessionKeyForCaptchaData = 'htmlBuilder.formBuilder.captcha.' . $this->getRequestObject(); // We unset any old captcha-answer currently set in the session for this form Request::session()->forget($sessionKeyForCaptchaData); $ruleParameters = FormBuilderTools::explodeRules($captchaRules); $ruleParameters = $ruleParameters['captcha']; // If a specific limit is set for this request via the first rule-parameter, we use this value. if (isset($ruleParameters[0]) && is_numeric($ruleParameters[0])) { $requestLimit = $ruleParameters[0]; } else { $requestLimit = config('htmlbuilder.formBuilder.captcha.defaultLimit'); } // If a specific decay-time is set for this request via the first rule-parameter, we use this value. if (isset($ruleParameters[1]) && is_numeric($ruleParameters[1])) { $decayTime = $ruleParameters[1]; } else { $decayTime = config('htmlbuilder.formBuilder.captcha.decayTime'); } // Now let's see, if the limit for this particular request has been reached. // We use the laravel-built in RateLimiter for that. // The Key of the RateLimiter is a hash of the RequestObject and the client-IP. $rateLimiterKey = sha1($this->getRequestObject() . Request::ip()); // A requestLimit of 0 means, a captcha is always required. if ($requestLimit === "0" || app(RateLimiter::class)->tooManyAttempts($rateLimiterKey, $requestLimit, $decayTime)) { // If it has been reached, we must append a captcha-field. // First we generate a captcha-question and an answer. $captchaData = FormBuilderTools::generateCaptcha(); // Then we add the captcha-field to the output. $this->output .= app()->make(InputText::class, ['_captcha'])->required(true)->value('')->label($captchaData['question'])->placeholder(trans('Nicat-HtmlBuilder::htmlbuilder.captchaPlaceholder'))->helpText(trans('Nicat-HtmlBuilder::htmlbuilder.captchaHelpText'))->generate(); // Furthermore we also set the required captcha-answer in the session. // This is used when the CaptchaValidator actually checks the captcha. Request::session()->put($sessionKeyForCaptchaData, $captchaData); } } } }
public function dynamicList($arrayName = '', DynamicListTemplateContract $template, $addButtonLabel = '', $minItems = null, $maxItems = null) { $this->dynamicListArrayName = $arrayName; $this->dynamicListTemplate = $template; // Get the submitted base-array. $submittedArray = app()['formbuilder']->getSubmittedValueForField($this->dynamicListArrayName); // If $minItems or $maxItems was not set via arguments, we try to get them from the FormBuilder. if (is_null($minItems) || is_null($maxItems)) { // Get the array-rules from the formbuilder. $arrayRules = FormBuilderTools::explodeRules(app()['formbuilder']->getRulesForField($this->dynamicListArrayName)); // Set minimum count of items from the gathered rules, or use default value. if (is_null($minItems) && isset($arrayRules['min'][0])) { $minItems = $arrayRules['min'][0]; } else { $minItems = 1; } // Set maximum count of items from the gathered rules, or use default value. if (is_null($minItems) && isset($arrayRules['max'][0])) { $maxItems = $arrayRules['max'][0]; } else { $maxItems = 10; } } // Set some basic stuff at the template. $this->dynamicListTemplate->isDynamicListTemplate = true; $this->dynamicListTemplate->performDynamicListModifications(); $this->dynamicListTemplate->wrap(false); if (method_exists($this, 'labelMode')) { $this->dynamicListTemplate->labelMode('sr-only'); } $this->dynamicListTemplate->data('dynamiclist-group', $this->dynamicListArrayName); // If data was submitted for this array, we have to add each submitted child using the same key it was submitted with. if (is_array($submittedArray) && count($submittedArray) > 0) { foreach ($submittedArray as $submittedChildKey => $submittedChild) { $this->addDynamicListChild($submittedChildKey); } } else { if (app('formbuilder')->getDefaultValueForField($this->dynamicListArrayName)) { foreach (app('formbuilder')->getDefaultValueForField($this->dynamicListArrayName) as $itemKey => $item) { $this->addDynamicListChild($itemKey); } } else { $i = 0; while ($i < $minItems) { $this->addDynamicListChild($i); $i++; } } } // We must also add an empty template to be available for the javascript-functionality. $template = $this->dynamicListTemplate; $template->hidden(); $template->data('dynamiclist-template', true); $this->addDynamicListChild('%itemID%', true); // Then we add the add-row button. if (!(strlen($addButtonLabel) > 0)) { $addButtonLabel = trans('Nicat-HtmlBuilder::htmlbuilder.dynamicListAddButtonTitle'); } $button = new Button('addRow'); $button->context('success')->addClass('btn-sm')->title($addButtonLabel)->data('dynamiclist-add', true)->data('dynamiclist-group', $this->dynamicListArrayName)->data('dynamiclist-min', $minItems)->data('dynamiclist-max', $maxItems)->content('<i class="fa fa-plus"></i> ' . $addButtonLabel); $this->appendChild($button); // We add the alert for maximum-reached. $alert = new DivAlertInfo(); $alert->hidden()->content(trans('Nicat-HtmlBuilder::htmlbuilder.dynamicListMaximumReached'))->data('dynamiclist-maxalert', true)->data('dynamiclist-group', $this->dynamicListArrayName); $this->appendChild($alert); return $this; }