PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/CoreVersions/0.2.0/Frameworks/Formal/Form.php

http://github.com/jeromeschneider/Baikal
PHP | 368 lines | 254 code | 72 blank | 42 comment | 30 complexity | 1e17092d59181f72713d8070a632b95d MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause
  1. <?php
  2. #################################################################
  3. # Copyright notice
  4. #
  5. # (c) 2012 J?r?me Schneider <mail@jeromeschneider.fr>
  6. # All rights reserved
  7. #
  8. # http://formal.codr.fr
  9. #
  10. # This script is part of the Formal project. The Formal
  11. # project is free software; you can redistribute it
  12. # and/or modify it under the terms of the GNU General Public
  13. # License as published by the Free Software Foundation; either
  14. # version 2 of the License, or (at your option) any later version.
  15. #
  16. # The GNU General Public License can be found at
  17. # http://www.gnu.org/copyleft/gpl.html.
  18. #
  19. # This script is distributed in the hope that it will be useful,
  20. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. # GNU General Public License for more details.
  23. #
  24. # This copyright notice MUST APPEAR in all copies of the script!
  25. #################################################################
  26. namespace Formal;
  27. class Form {
  28. protected $sModelClass = "";
  29. protected $aOptions = array(
  30. "action" => "",
  31. "close" => TRUE,
  32. "closeurl" => "",
  33. );
  34. protected $oModelInstance = null;
  35. protected $oElements = null;
  36. protected $aErrors = array();
  37. protected $bPersisted = null; # TRUE when form has persisted; available only after execute
  38. protected $sDisplayTitle = ""; # Displayed form title; generated in setModelInstance()
  39. protected $sDisplayMessage = ""; # Displayed confirm message; generated in execute()
  40. public function __construct($sModelClass, $aOptions = array()) {
  41. $this->sModelClass = $sModelClass;
  42. $this->aOptions = array_merge($this->aOptions, $aOptions);
  43. $this->oElements = new \Flake\Core\CollectionTyped("\Formal\Element");
  44. }
  45. public function option($sName) {
  46. if(array_key_exists($sName, $this->aOptions)) {
  47. return $this->aOptions[$sName];
  48. }
  49. throw new \Exception("\Formal\Form->option(): Option '" . htmlspecialchars($sName) . "' not found.");
  50. }
  51. public function setOption($sName, $sValue) {
  52. $this->aOptions[$sName] = $sValue;
  53. return $this;
  54. }
  55. public function options() {
  56. $aOptions = $this->aOptions;
  57. return $aOptions;
  58. }
  59. public function setModelInstance($oModelInstance) {
  60. if(!\Flake\Util\Tools::is_a($oModelInstance, $this->sModelClass)) {
  61. throw new \Exception("\Formal\Core->setModelInstance(): Given instance is not of class '" . $this->sModelClass . "'");
  62. }
  63. $this->oModelInstance = $oModelInstance;
  64. $this->oElements->reset();
  65. foreach($this->oElements as $oElement) {
  66. $oElement->setValue(
  67. $this->modelInstance()->get(
  68. $oElement->option("prop")
  69. )
  70. );
  71. }
  72. # Displayed form title is generated depending on modelInstance floatingness
  73. if($this->floatingModelInstance()) {
  74. $this->sDisplayTitle = "Creating new<i class=" . $this->modelInstance()->mediumicon() . "></i><strong>" . $this->modelInstance()->humanName() . "</strong>";
  75. } else {
  76. # This is changed if form is persisted, after persistance, to reflect possible change in model instance label
  77. $this->sDisplayTitle = "Editing " . $this->modelInstance()->humanName() . "<i class=" . $this->modelInstance()->mediumicon() . "></i><strong>" . $this->modelInstance()->label() . "</strong>";
  78. }
  79. return $this;
  80. }
  81. public function modelInstance() {
  82. return $this->oModelInstance;
  83. }
  84. public function floatingModelInstance() {
  85. return $this->modelInstance()->floating();
  86. }
  87. public function execute() {
  88. # Obtaining morphology from model object
  89. $oMorpho = $this->modelInstance()->formMorphologyForThisModelInstance();
  90. $this->aErrors = array();
  91. $oMorpho->elements()->reset();
  92. foreach($oMorpho->elements() as $oElement) {
  93. # If element is readonly, skip process
  94. if($oElement->option("readonly")) {
  95. continue;
  96. }
  97. $sPropName = $oElement->option("prop");
  98. # posted value is fetched, then passes to element before persistance
  99. $sPostValue = $this->postValue($sPropName);
  100. $oElement->setValue($sPostValue);
  101. $sValue = $oElement->value();
  102. $this->modelInstance()->set(
  103. $sPropName,
  104. $sValue
  105. );
  106. }
  107. $oMorpho->elements()->reset();
  108. foreach($oMorpho->elements() as $oElement) {
  109. $aValidation = $oElement->optionArray("validation");
  110. if(empty($aValidation)) {
  111. continue;
  112. }
  113. $sValue = $oElement->value();
  114. foreach($aValidation as $sValidation) {
  115. # If element is readonly, skip process
  116. if($oElement->option("readonly")) {
  117. continue;
  118. }
  119. $sParam = FALSE;
  120. if(strpos($sValidation, ":") !== FALSE) {
  121. $sValidation = strtok($sValidation, ":");
  122. $sParam = strtok(":");
  123. }
  124. $sMethod = "validate" . ucfirst(strtolower($sValidation));
  125. if(!method_exists($this, $sMethod)) {
  126. throw new \Exception("\Formal\Form::execute(): no validation method for '" . htmlspecialchars($sValidation) . "'");
  127. }
  128. if($sParam === FALSE) {
  129. $mValid = $this->$sMethod($sValue, $oMorpho, $oElement);
  130. } else {
  131. $mValid = $this->$sMethod($sValue, $oMorpho, $oElement, $sParam);
  132. }
  133. if($mValid !== TRUE) {
  134. $this->aErrors[] = array(
  135. "element" => $oElement,
  136. "message" => $mValid,
  137. );
  138. $oElement->setOption("error", TRUE);
  139. break; # one error per element per submit
  140. }
  141. }
  142. }
  143. if(empty($this->aErrors)) {
  144. # Model object is persisted
  145. # Last chance to generate a confirm message corresponding to what *was* submitted ("Creating", instead of "Editing")
  146. if($this->floatingModelInstance()) {
  147. $this->sDisplayMessage = \Formal\Core\Message::notice(
  148. $this->modelInstance()->humanName() . " <i class='" . $this->modelInstance()->icon() . "'></i> <strong>" . $this->modelInstance()->label() . "</strong> has been created.",
  149. "",
  150. FALSE
  151. );
  152. $bWasFloating = TRUE;
  153. } else {
  154. $bWasFloating = FALSE;
  155. $this->sDisplayMessage = \Formal\Core\Message::notice(
  156. "Changes on <i class='" . $this->modelInstance()->icon() . "'></i> <strong>" . $this->modelInstance()->label() . "</strong> have been saved.",
  157. FALSE, # No title
  158. FALSE # No close button
  159. );
  160. }
  161. $this->modelInstance()->persist();
  162. if($bWasFloating === FALSE) {
  163. # Title is generated now, as submitted data might have changed the model instance label
  164. $this->sDisplayTitle = "Editing " . $this->modelInstance()->humanName() . "<strong><i class=" . $this->modelInstance()->mediumicon() . "></i><strong>" . $this->modelInstance()->label() . "</strong>";
  165. }
  166. $this->bPersisted = TRUE;
  167. } else {
  168. $this->bPersisted = FALSE;
  169. }
  170. }
  171. public function persisted() {
  172. if($this->submitted()) {
  173. if(is_null($this->bPersisted)) {
  174. throw new \Exception("\Formal\Form->persisted(): information is not available yet. This method may only be called after execute()");
  175. }
  176. return $this->bPersisted;
  177. }
  178. return FALSE;
  179. }
  180. public function validateRequired($sValue, \Formal\Form\Morphology $oMorpho, \Formal\Element $oElement) {
  181. if(trim($sValue) !== "") {
  182. return TRUE;
  183. }
  184. return "<strong>" . $oElement->option("label") . "</strong> is required.";
  185. }
  186. public function validateEmail($sValue, \Formal\Form\Morphology $oMorpho, \Formal\Element $oElement) {
  187. if(\Flake\Util\Tools::validEmail($sValue)) {
  188. return TRUE;
  189. }
  190. return "<strong>" . $oElement->option("label") . "</strong> should be an email.";
  191. }
  192. public function validateSameas($sValue, \Formal\Form\Morphology $oMorpho, \Formal\Element $oElement, $sReferencePropName) {
  193. $sReferenceValue = $oMorpho->element($sReferencePropName)->value();
  194. if($sValue === $sReferenceValue) {
  195. return TRUE;
  196. }
  197. return "<strong>" . $oElement->option("label") . "</strong> does not match " . $oMorpho->element($sReferencePropName)->option("label") . ".";
  198. }
  199. public function validateUnique($sValue, \Formal\Form\Morphology $oMorpho, \Formal\Element $oElement) {
  200. $oModelInstance = $this->modelInstance();
  201. $oRequest = $oModelInstance->getBaseRequester()->addClauseEquals(
  202. $oElement->option("prop"),
  203. $sValue
  204. );
  205. if(!$oModelInstance->floating()) {
  206. # checking id only if model instance is not floating
  207. $oRequest->addClauseNotEquals(
  208. $oModelInstance::PRIMARYKEY,
  209. $oModelInstance->get(
  210. $oModelInstance::PRIMARYKEY
  211. )
  212. );
  213. }
  214. $oColl = $oRequest->execute();
  215. if($oColl->count() > 0) {
  216. return "<strong>" . $oElement->option("label") . "</strong> has to be unique. Given value is not available.";
  217. }
  218. return TRUE;
  219. }
  220. public function validateTokenid($sValue, \Formal\Form\Morphology $oMorpho, \Formal\Element $oElement) {
  221. if(!preg_match("/^[a-z0-9\-]+$/", $sValue)) {
  222. return "<strong>" . $oElement->option("label") . "</strong> is not valid. Allowed characters are digits, lowercase letters and the dash symbol '-'.";
  223. }
  224. return TRUE;
  225. }
  226. public function postValue($sPropName) {
  227. return \Flake\Util\Tools::POST($sPropName);
  228. }
  229. public function render() {
  230. $aHtml = array();
  231. $oMorpho = $this->modelInstance()->formMorphologyForThisModelInstance();
  232. $oMorpho->elements()->reset();
  233. foreach($oMorpho->elements() as $oElement) {
  234. # Setting current prop value for element
  235. # Set on empty (just created) FormMorphology
  236. # And obtained from Model instance
  237. $oElement->setValue(
  238. $this->modelInstance()->get(
  239. $oElement->option("prop")
  240. )
  241. );
  242. $aHtml[] = $oElement->render();
  243. }
  244. $elements = implode("\n", $aHtml);
  245. $sModelClass = $this->sModelClass;
  246. ######################################################
  247. # Displaying messages
  248. ######################################################
  249. if($this->submitted()) {
  250. # There were errors detected during execute()
  251. # Error messages are displayed
  252. if(!empty($this->aErrors)) {
  253. $this->sDisplayMessage = "";
  254. $aMessages = array();
  255. reset($this->aErrors);
  256. foreach($this->aErrors as $aError) {
  257. $aMessages[] = $aError["message"];
  258. }
  259. $this->sDisplayMessage = \Formal\Core\Message::error(
  260. implode("<br />", $aMessages),
  261. "Validation error"
  262. );
  263. }
  264. }
  265. $sSubmittedFlagName = $this->submitSignatureName();
  266. if($this->option("close") === TRUE) {
  267. $sCloseUrl = $this->option("closeurl");
  268. $sCloseButton = '<a class="btn" href="' . $sCloseUrl . '">Close</a>';
  269. } else {
  270. $sCloseButton = "";
  271. }
  272. $sActionUrl = $this->option("action");
  273. $sHtml =<<<HTML
  274. <form class="form-horizontal" action="{$sActionUrl}" method="post" enctype="multipart/formdata">
  275. <input type="hidden" name="{$sSubmittedFlagName}" value="1" />
  276. <fieldset>
  277. <legend style="line-height: 40px;">{$this->sDisplayTitle}</legend>
  278. {$this->sDisplayMessage}
  279. {$elements}
  280. <div class="form-actions">
  281. <button type="submit" class="btn btn-primary">Save changes</button>
  282. {$sCloseButton}
  283. </div>
  284. </fieldset>
  285. </form>
  286. HTML;
  287. return $sHtml;
  288. }
  289. protected function submitSignatureName() {
  290. return str_replace('\\', '_', $this->sModelClass . "::submitted");
  291. }
  292. public function submitted() {
  293. return intval(\Flake\Util\Tools::POST($this->submitSignatureName())) === 1;
  294. }
  295. }