Exemple #1
0
 /**
  *	Errors
  *	@return \Touchbase\Data\Store
  */
 public function errors($key = null)
 {
     $errors = SessionStore::get("touchbase.key.session.errors", new Store());
     if (isset($key)) {
         return $errors->get($key);
     }
     return $errors;
 }
Exemple #2
0
 /**
  *	Route History
  *	@return string
  */
 public static function routeHistory()
 {
     return SessionStore::get(self::ROUTE_HISTORY_KEY, []);
 }
Exemple #3
0
 /**
  *	Reflash
  *	NB. Currently not implemented
  *	@return VOID
  */
 public static function reflash()
 {
     $aged = SessionStore::get(self::FLASH_KEY . ".aged", []);
     $values = array_unique(array_merge(SessionStore::get(self::FLASH_KEY, []), $aged));
     SessionStore::set(self::FLASH_KEY, $values);
 }
 /**
  *	Validate Post Request
  *	This method adds default validation to input fields based on the served HTML.
  *	If any errors are found, the redirect will be made on the response object with the errors.
  *	@param \Touchbase\Control\HTTPRequest $request
  *	@param \Touchbase\Control\HTTPResponse &$response
  *	@param \Touchbase\Utils\Validation &$validation
  *	@return VOID
  */
 protected function validatePostRequest($request, &$response, &$validation)
 {
     if (!$request->isMainRequest()) {
         return;
     }
     $formNameToken = $request->_VAR("tb_form_token");
     $formName = substr($formNameToken, 0, strrpos($formNameToken, "_"));
     if (isset($formNameToken) && ($form = SessionStore::get($formNameToken, false))) {
         SessionStore::consume($formName, $formNameToken);
         $data = $request->_VARS();
         libxml_use_internal_errors(true);
         $dom = new \DOMDocument();
         $dom->loadHtml(gzinflate(base64_decode($form)), LIBXML_NOWARNING | LIBXML_NOERROR);
         if ($dom->documentElement->getAttribute("novalidate")) {
             return;
         }
         $formValidation = Validation::create($formName);
         $privateFields = [];
         foreach (["input", "textarea", "select"] as $tag) {
             foreach ($dom->getElementsByTagName($tag) as $input) {
                 if ($input->hasAttributes() && ($inputName = $input->getAttribute("name"))) {
                     $inputType = $input->getAttribute("type");
                     $inputValidation = Validation::create($inputName);
                     if ($inputType === "password") {
                         $privateFields[] = $inputName;
                     }
                     $inputValidation->type($inputType);
                     if ($inputType === "file" && $input->hasAttribute("accept")) {
                         $validTypes = explode(",", $input->getAttribute("accept"));
                         //File Extentions
                         $fileExt = array_filter($validTypes, function ($value) {
                             return strpos($value, ".") === 0;
                         });
                         if (!empty($fileExt)) {
                             $inputValidation->addRule(function ($value) use($fileExt) {
                                 $name = $value['name'];
                                 if (is_array($name)) {
                                     foreach ($name as $nme) {
                                         if (!in_array(pathinfo($nme, PATHINFO_EXTENSION), $fileExt)) {
                                             return false;
                                         }
                                     }
                                     return true;
                                 }
                                 return in_array(pathinfo($name, PATHINFO_EXTENSION), $fileExt);
                             }, "A file uploaded did not have the correct extension");
                         }
                         //Mime Types
                         $validTypes = array_diff($validTypes, $fileExt);
                         $implicitFileMime = array_filter($validTypes, function ($value) {
                             return strpos($value, "/*") !== false;
                         });
                         if (!empty($implicitFileMime)) {
                             $inputValidation->addRule(function ($value) use($implicitFileMime) {
                                 $tmpName = $value['tmp_name'];
                                 if (is_array($tmpName)) {
                                     foreach ($tmpName as $tmp) {
                                         $mime = strstr(File::create($tmp)->mime(), "/", true) . "/*";
                                         if (!in_array($mime, $implicitFileMime)) {
                                             return false;
                                         }
                                     }
                                     return true;
                                 }
                                 $mime = strstr(File::create($tmpName)->mime(), "/", true) . "/*";
                                 return in_array($mime, $implicitFileMime);
                             }, "A file uploaded did not have the correct mime type");
                         }
                         $validTypes = array_diff($validTypes, $implicitFileMime);
                         $fileMime = array_filter($validTypes, function ($value) {
                             return strpos($value, "/") !== false;
                         });
                         if (!empty($fileMime)) {
                             $inputValidation->addRule(function ($value) use($fileMime) {
                                 $tmpName = $value['tmp_name'];
                                 if (is_array($tmpName)) {
                                     foreach ($tmpName as $tmp) {
                                         if (!in_array(File::create($tmp)->mime(), $fileMime)) {
                                             return false;
                                         }
                                     }
                                     return true;
                                 }
                                 return in_array(File::create($tmpName)->mime(), $fileMime);
                             }, "A file uploaded did not have the correct mime type");
                         }
                     }
                     if ($input->hasAttribute("required")) {
                         $errorMessage = null;
                         if ($placeholder = $input->getAttribute("placeholder")) {
                             $errorMessage = sprintf("Please complete the `%s` field", $placeholder);
                         }
                         $inputValidation->required($errorMessage);
                     }
                     if ($input->hasAttribute("readonly")) {
                         $inputValidation->readonly($input->getAttribute("value"));
                     }
                     if ($input->hasAttribute("disabled")) {
                         $inputValidation->disabled();
                     }
                     if ($minLength = $input->getAttribute("minlength")) {
                         $inputValidation->minLength($minLength);
                     }
                     if ($maxLength = $input->getAttribute("maxlength")) {
                         $inputValidation->maxLength($maxLength);
                     }
                     if (in_array($inputType, ["number", "range", "date", "datetime", "datetime-local", "month", "time", "week"])) {
                         if ($min = $input->getAttribute("min")) {
                             $inputValidation->min($min, $inputType);
                         }
                         if ($max = $input->getAttribute("max")) {
                             $inputValidation->max($max, $inputType);
                         }
                     }
                     if ($pattern = $input->getAttribute("pattern")) {
                         $inputValidation->pattern($pattern);
                     }
                     if (count($inputValidation)) {
                         $validation->addRule($formValidation->addRule($inputValidation));
                     }
                 }
             }
         }
         if (!$validation->validate($data)) {
             $response->withData(array_diff_key($data, array_flip($privateFields)));
             $response->redirect(-1)->withErrors($validation->errors, $formName);
         }
     } else {
         $response->redirect(-1)->withErrors(["Session timed out, please try again."], $formName);
     }
 }
Exemple #5
0
 /**
  *	With Data
  *	Attach data to the response
  *	@param array $data
  *	@return \Touchbase\Control\HTTPResponse
  */
 public function withData(array $data)
 {
     SessionStore::flash($key = "touchbase.key.session.post", SessionStore::get($key)->set($data));
     return $this;
 }
 /**
  *	Validate Html
  *	This method scans the outgoing HTML for any forms, if found it will save the form to the session in order to validate.
  *	If any errors previously existed in the session, this method will apply `bootstrap` style css error classes.
  *	@pararm string &$htmlDocument - The outgoing HTML string
  *	@return VOID
  */
 private function validateHtml(&$htmlDocument)
 {
     if (!empty($htmlDocument)) {
         $dom = new DOMDocument();
         $dom->loadHtml($htmlDocument, LIBXML_NOWARNING | LIBXML_NOERROR | LIBXML_NOENT | LIBXML_HTML_NOIMPLIED);
         //Automatically apply an active class to links that relate to the current URL
         foreach ($dom->getElementsByTagName('a') as $link) {
             if (Router::isSiteUrl($href = Router::relativeURL($link->getAttribute("href")))) {
                 $currentClasses = explode(" ", $link->getAttribute("class"));
                 if (strcasecmp($href, $this->controller->request()->url()) == 0) {
                     $currentClasses[] = 'active';
                 }
                 if (strpos($this->controller->request()->url(), $href) === 0) {
                     $currentClasses[] = 'child-active';
                 }
                 $link->setAttribute('class', implode(" ", $currentClasses));
             }
         }
         //Save the outgoing form elements for future validation.
         foreach ($dom->getElementsByTagName('form') as $form) {
             $savedom = new \DOMDocument();
             if ($formAction = $form->getAttribute("action") && !Router::isSiteUrl($formAction)) {
                 continue;
             }
             //Add CSRF
             $rand = function_exists('random_bytes') ? random_bytes(32) : null;
             $rand = !$rand && function_exists('mcrypt_create_iv') ? mcrypt_create_iv(32, MCRYPT_DEV_URANDOM) : $rand;
             $rand = $rand ? $rand : openssl_random_pseudo_bytes(32);
             $csrfToken = bin2hex($rand);
             $formName = $form->getAttribute("name");
             $formNameToken = $formName . "_" . $csrfToken;
             $csrf = $dom->createDocumentFragment();
             $csrf->appendXML(HTML::input()->attr("type", "hidden")->attr("name", "tb_form_token")->attr("value", $formNameToken)->attr("readonly", true));
             $form->insertBefore($csrf, $form->firstChild);
             //TODO: Should we assume the form will allways have content
             foreach (["input", "textarea", "select"] as $tag) {
                 foreach ($form->getElementsByTagName($tag) as $input) {
                     $savedom->appendChild($savedom->importNode($input->cloneNode()));
                     //Populate form with previous data
                     if (($newValue = SessionStore::get("touchbase.key.session.post")->get($input->getAttribute("name"), false)) !== false) {
                         if (is_scalar($newValue) && $input->getAttribute("type") !== "hidden" && !$input->hasAttribute("readonly")) {
                             $input->setAttribute('value', $newValue);
                         }
                     }
                     //Populate errors
                     if ($errorMessage = $this->controller->errors($formName)->get($input->getAttribute("name"), false)) {
                         $currentClasses = explode(" ", $input->parentNode->getAttribute("class"));
                         foreach (["has-feedback", "has-error"] as $class) {
                             if (!in_array($class, $currentClasses)) {
                                 $currentClasses[] = $class;
                             }
                         }
                         $input->parentNode->setAttribute('class', implode(" ", $currentClasses));
                         $input->setAttribute("data-error", $errorMessage);
                     }
                 }
             }
             SessionStore::recycle($formName, $formNameToken, base64_encode(gzdeflate($savedom->saveHTML(), 9)));
         }
         //Move body scripts to bottom
         $bodies = $dom->getElementsByTagName('body');
         $body = $bodies->item(0);
         if ($body) {
             foreach ($body->getElementsByTagName('script') as $script) {
                 if ($script->parentNode->nodeName === "body") {
                     break;
                 }
                 $body->appendChild($dom->importNode($script));
             }
         }
         //Look for the special attribute that moves nodes.
         //This is useful for moving modals from the template files to the bottom output.
         $xpath = new \DOMXPath($dom);
         $appendToBodyRef = NULL;
         foreach ($xpath->query("//*[@tb-append]") as $element) {
             $appendTo = $xpath->query($element->getAttribute("tb-append"))->item(0);
             $element->removeAttribute("tb-append");
             if ($appendTo) {
                 if ($appendTo->nodeName === "body") {
                     //Special case to append above the included javascript files.
                     if (!$appendToBodyRef) {
                         $appendToBodyRef = $xpath->query('/html/body/comment()[. = " END CONTENT "][1]')->item(0);
                     }
                     $body->insertBefore($dom->importNode($element), $appendToBodyRef);
                 } else {
                     $appendTo->appendChild($dom->importNode($element));
                 }
             }
         }
         //Save the HTML with the updates.
         if ($this->controller->request()->isAjax() || !$this->controller->request()->isMainRequest()) {
             //This will remove the doctype that's automatically appended.
             $htmlDocument = $dom->saveHTML($dom->documentElement);
         } else {
             $htmlDocument = $dom->saveHTML();
         }
     }
 }