PageRenderTime 40ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/atk4/lib/Form/Field.php

https://github.com/mahimarathore/mahi
PHP | 401 lines | 272 code | 13 blank | 116 comment | 39 complexity | d24e2c6e8b422f673e1743273768df4b MD5 | raw file
Possible License(s): AGPL-3.0, MPL-2.0-no-copyleft-exception
  1. <?php // vim:ts=4:sw=4:et:fdm=marker
  2. /*
  3. * Undocumented
  4. *
  5. * @link http://agiletoolkit.org/
  6. *//*
  7. ==ATK4===================================================
  8. This file is part of Agile Toolkit 4
  9. http://agiletoolkit.org/
  10. (c) 2008-2013 Agile Toolkit Limited <info@agiletoolkit.org>
  11. Distributed under Affero General Public License v3 and
  12. commercial license.
  13. See LICENSE or LICENSE_COM for more information
  14. =====================================================ATK4=*/
  15. /**
  16. * Implementation of abstract form's field
  17. *
  18. * @author Romans <romans@adevel.com>
  19. * @copyright See file COPYING
  20. * @version $Id$
  21. */
  22. abstract class Form_Field extends AbstractView {
  23. /**
  24. * Description of the field shown next to it on the form
  25. */
  26. public $error_template; // template used to put errors on the field line
  27. public $mandatory_template; // template used to mark mandatory fields
  28. public $caption;
  29. protected $value=null; // use $this->get(), ->set().
  30. public $short_name=null;
  31. public $attr=array();
  32. public $no_save=null;
  33. public $field_prepend='';
  34. public $field_append='';
  35. public $comment='&nbsp;';
  36. protected $disabled=false;
  37. protected $mandatory=false;
  38. public $default_value=null;
  39. // Field customization
  40. private $separator='';
  41. public $show_input_only;
  42. public $form=null;
  43. public $button_prepend=null;
  44. public $button_append=null;
  45. function init(){
  46. parent::init();
  47. if(@$_GET[$this->owner->name.'_cut_field']==$this->name){
  48. $this->api->addHook('pre-render',array($this,'_cutField'));
  49. }
  50. }
  51. function setForm($form){
  52. $form->addHook('loadPOST',$this);
  53. $form->addHook('validate',$this);
  54. $this->form=$form;
  55. $this->form->data[$this->short_name] = $this->value;
  56. $this->value =& $this->form->data[$this->short_name];
  57. return $this;
  58. }
  59. function _cutField(){
  60. // this method is used by ui.atk4_form, when doing reloadField();
  61. unset($_GET['cut_object']);
  62. $this->recursiveRender();
  63. if($this->api->jquery)$this->api->jquery->getJS($this);
  64. throw new Exception_StopRender(
  65. $this->template->renderRegion($this->template->tags['before_field']).
  66. $this->getInput().
  67. $this->template->renderRegion($this->template->tags['after_field'])
  68. );
  69. }
  70. function setMandatory($mandatory=true){
  71. $this->mandatory=$mandatory;
  72. return $this;
  73. }
  74. function setReadonly($readonly=true){
  75. $this->readonly=$readonly;
  76. return $this;
  77. }
  78. function isMandatory(){
  79. return $this->mandatory;
  80. }
  81. function setCaption($_caption){
  82. $this->caption=$this->api->_($_caption);
  83. return $this;
  84. }
  85. function displayFieldError($msg=null){
  86. if(!isset($msg))$msg='Error in field "'.$this->caption.'"';
  87. $this->form->js(true)
  88. ->atk4_form('fieldError',$this->short_name,$msg)
  89. ->execute();
  90. $this->form->errors[$this->short_name]=$msg;
  91. }
  92. function setNoSave(){
  93. // Field value will not be saved into defined source (such as database)
  94. $this->no_save=true;
  95. return $this;
  96. }
  97. function disable(){
  98. // sets 'disabled' property and setNoSave()
  99. $this->setAttr('disabled');
  100. $this->setNoSave();
  101. $this->disabled=true;
  102. return $this;
  103. }
  104. function isDisabled(){
  105. return $this->disabled;
  106. }
  107. function set($value){
  108. // Use this function when you want to assign $this->value.
  109. // If you use this function, your field will operate in AJAX mode.
  110. $this->value=$value;
  111. return $this;
  112. }
  113. /** Position can be either 'before' or 'after' */
  114. function addButton($label,$options=array()){
  115. $position='after';
  116. if(is_string($options)){
  117. $position=$options;
  118. }else{
  119. if(isset($options['position']))$position=$options['position'];
  120. }
  121. if($position=='after'){
  122. return $this->afterField()->add('Button',$options)->setLabel($label);
  123. }else{
  124. return $this->beforeField()->add('Button',$options)->setLabel($label);
  125. }
  126. }
  127. function beforeField(){
  128. if(!$this->template->hasTag('after_input')){
  129. $el=$this->owner->add('HtmlElement');
  130. $this->owner->add('Order')->move($el,'before',$this)->now();
  131. return $el;
  132. }
  133. if(!$this->button_prepend)return $this->button_prepend=$this
  134. ->add('HtmlElement',null,'before_input')->addClass('input-cell');
  135. return $this->button_prepend;
  136. }
  137. function afterField(){
  138. if(!$this->template->hasTag('after_input')){
  139. $el=$this->owner->add('HtmlElement');
  140. $this->owner->add('Order')->move($el,'after',$this)->now();
  141. return $el;
  142. }
  143. if(!$this->button_append)return $this->button_append=$this
  144. ->add('HtmlElement',null,'after_input')->addClass('input-cell');
  145. return $this->button_append;
  146. }
  147. function aboveField(){
  148. return $this->add('HtmlElement',null,'before_field');
  149. }
  150. function belowField(){
  151. return $this->add('HtmlElement',null,'after_field');
  152. }
  153. function setComment($text=''){
  154. $this->belowField()->setElement('ins')->set($text);
  155. return $this;
  156. }
  157. function addComment($text=''){
  158. return $this->belowField()->setElement('ins')->set($text);
  159. }
  160. function get(){
  161. return $this->value;
  162. }
  163. function setClass($class){
  164. $this->attr['class']=$class;
  165. return $this;
  166. }
  167. function addClass($class){
  168. $this->attr['class'].=($this->attr['class']?' ':'').$class;
  169. return $this;
  170. }
  171. function setAttr($attr,$value=undefined){
  172. if(is_array($attr)&&$value===undefined){
  173. foreach($attr as $k=>$v) $this->setAttr($k,$v);
  174. return $this;
  175. }
  176. if($attr){
  177. $this->attr[$attr] = $value===undefined?'true':$value;
  178. }
  179. return $this;
  180. }
  181. function setProperty($property,$value){ // synonym, setAttr is preferred
  182. return $this->setAttr($property,$value);
  183. }
  184. function setFieldHint($var_args=null){
  185. /* Adds a hint after this field. Thes will call Field_Hint->set()
  186. with same arguments you called this funciton.
  187. */
  188. if(!$this->template->hasTag('after_field'))return $this;
  189. $hint=$this->add('Form_Hint',null,'after_field');
  190. call_user_func_array(array($hint,'set'), func_get_args());
  191. return $this;
  192. }
  193. function loadPOST(){
  194. if(isset($_POST[$this->name]))$this->set($_POST[$this->name]);
  195. else $this->set($this->default_value);
  196. $this->normalize();
  197. }
  198. function normalize(){
  199. /* Normalization will make sure that entry conforms to the field type.
  200. Possible trimming, rounding or length enforcements may happen. */
  201. $this->hook('normalize');
  202. }
  203. function validate(){
  204. // NoSave and disabled fields should not be validated
  205. if($this->disabled || $this->no_save)return true;
  206. // we define "validate" hook, so actual validators could hook it here
  207. // and perform their checks
  208. if(is_bool($result = $this->hook('validate')))return $result;
  209. }
  210. /** @private - handles field validation callback output */
  211. function _validateField($caller,$condition,$msg){
  212. $ret=call_user_func($condition,$this);
  213. if($ret===false){
  214. if(is_null($msg))$msg=$this->api->_('Error in ').$this->caption;
  215. $this->displayFieldError($msg);
  216. }elseif(is_string($ret)){
  217. $this->displayFieldError($ret);
  218. }
  219. return $this;
  220. }
  221. /** Executes a callback. If callback returns string, shows it as error message.
  222. * If callback returns "false" shows either $msg or a standard error message
  223. * about field being incorrect */
  224. function validateField($condition,$msg=null){
  225. if(is_callable($condition)){
  226. $this->addHook('validate',array($this,'_validateField'),array($condition,$msg));
  227. }else{
  228. $this->addHook('validate',$s='if(!('.$condition.'))$this->displayFieldError("'.
  229. ($msg?$msg:'Error in ".$this->caption."').'");');
  230. }
  231. return $this;
  232. }
  233. function _validateNotNull($field){
  234. if($field->get()==="" || is_null($field->get()))return false;
  235. }
  236. /** Adds "X is a mandatory field" message */
  237. function validateNotNULL($msg=null){
  238. $this->setMandatory();
  239. if($msg && $msg!==true){
  240. $msg=$this->api->_($msg);
  241. }else{
  242. $msg=sprintf($this->api->_('%s is a mandatory field'),$this->caption);
  243. }
  244. $this->validateField(array($this,'_validateNotNull'),$msg);
  245. return $this;
  246. }
  247. function getInput($attr=array()){
  248. // This function returns HTML tag for the input field. Derived classes
  249. // should inherit this and add new properties if needed
  250. return $this->getTag('input',
  251. array_merge(array(
  252. 'name'=>$this->name,
  253. 'data-shortname'=>$this->short_name,
  254. 'id'=>$this->name,
  255. 'value'=>$this->value,
  256. ),$attr,$this->attr)
  257. );
  258. }
  259. function setSeparator($separator){
  260. $this->separator = $separator;
  261. return $this;
  262. }
  263. function render(){
  264. if($this->show_input_only){
  265. $this->output($this->getInput());
  266. return;
  267. }
  268. if(!$this->error_template)$this->error_template = $this->form->template_chunks['field_error'];
  269. if((!property_exists($this, 'mandatory_template')) || (!$this->mandatory_template))$this->mandatory_template=$this->form->template_chunks['field_mandatory'];
  270. $this->template->trySet('field_caption',$this->caption?($this->caption.$this->separator):'');
  271. $this->template->trySet('field_name',$this->name);
  272. $this->template->trySet('field_comment',$this->comment);
  273. // some fields may not have field_input tag at all...
  274. if($this->button_prepend || $this->button_append){
  275. $this->field_prepend.='<div class="input-cell expanded">';
  276. $this->field_append='</div>'.$this->field_append;
  277. $this->template->trySetHTML('input_row_start','<div class="input-row">');
  278. $this->template->trySetHTML('input_row_stop','</div>');
  279. }
  280. $this->template->trySetHTML('field_input',$this->field_prepend.$this->getInput().$this->field_append);
  281. $this->template->trySetHTML('field_error',
  282. isset($this->form->errors[$this->short_name])?
  283. $this->error_template->set('field_error_str',$this->form->errors[$this->short_name])->render()
  284. :''
  285. );
  286. if (is_object($this->mandatory_template)) {
  287. $this->template->trySet('field_mandatory',$this->isMandatory()?$this->mandatory_template->render():'');
  288. }
  289. $this->output($this->template->render());
  290. }
  291. function getTag($tag, $attr=null, $value=null){
  292. /**
  293. * Draw HTML attribute with supplied attributes.
  294. *
  295. * Short description how this getTag may be used:
  296. *
  297. * Use get tag to build HTML tag.
  298. * echo getTag('img',array('src'=>'foo.gif','border'=>0);
  299. *
  300. * The unobvius advantage of this function is ability to merge
  301. * attribute arrays. For example, if you have function, which
  302. * must display img tag, you may add optional $attr argument
  303. * to this function.
  304. *
  305. * function drawImage($src,$attr=array()){
  306. * echo getTag('img',array_merge(array('src'=>$src),$attr));
  307. * }
  308. *
  309. * so calling drawImage('foo.gif') will echo: <img src="foo.gif">
  310. *
  311. * The benefit from such a function shows up when you use 2nd argument:
  312. *
  313. * 1. adding additional attributes
  314. * drawImage('foo.gif',array('border'=>0'));
  315. * --> <img src="foo.gif" border="0">
  316. * (NOTE: you can even have attr templates!)
  317. *
  318. * 2. adding no-value attributes, such as nowrap:
  319. * getTag('td',arary('nowrap'=>true));
  320. * --> <td nowrap>
  321. *
  322. * 3. disabling some attributes.
  323. * drawImage('foo.gif',array('src'=>false));
  324. * --> <img>
  325. *
  326. * 4. re-defining attributes
  327. * drawImage('foo.gif',array('src'=>'123'));
  328. * --> <img src="123">
  329. *
  330. * 5. or you even can re-define tag itself
  331. * drawImage('foo.gif',array(
  332. * ''=>'input',
  333. * 'type'=>'picture'));
  334. * --> <input type="picture" src="foo.gif">
  335. *
  336. * 6. xml-valid tags without closing tag
  337. * getTag('img/',array('src'=>'foo.gif'));
  338. * --> <img src=>"foo.gif"/>
  339. *
  340. * 7. closing tags
  341. * getTag('/td');
  342. * --> </td>
  343. *
  344. * 8. using $value will add $value after tag followed by closing tag
  345. * getTag('a',array('href'=>'foo.html'),'click here');
  346. * --> <a href="foo.html">click here</a>
  347. *
  348. * 9. you may not skip attribute argument.
  349. * getTag('b','text in bold');
  350. * --> <b>text in bold</b>
  351. *
  352. * 10. nesting
  353. * getTag('a',array('href'=>'foo.html'),getTag('b','click here'));
  354. * --> <a href="foo.html"><b>click here</b></a>
  355. */
  356. if(is_string($attr)){
  357. $value=$attr;
  358. $attr=null;
  359. }
  360. if(!$attr){
  361. return "<$tag>".($value?$value."</$tag>":"");
  362. }
  363. $tmp = array();
  364. if(substr($tag,-1)=='/'){
  365. $tag = substr($tag,0,-1);
  366. $postfix = '/';
  367. } else $postfix = '';
  368. foreach ($attr as $key => $val) {
  369. if($val === false) continue;
  370. if($val === true) $tmp[] = "$key";
  371. elseif($key === '')$tag=$val;
  372. else $tmp[] = "$key=\"".htmlspecialchars($val)."\"";
  373. }
  374. return "<$tag ".join(' ',$tmp).$postfix.">".($value?$value."</$tag>":"");
  375. }
  376. function setSource(){
  377. return call_user_func_array(array($this->form,'setSource'),func_get_args());
  378. }
  379. function addField(){
  380. return call_user_func_array(array($this->form,'addField'),func_get_args());
  381. //throw new ObsoleteException('$form->addField() now returns Field object and not Form. Do not chain it.');
  382. }
  383. }
  384. ///////// Because many fields are really simple extenions of the base-line field,
  385. ///////// they are defined here.
  386. // Visually different fields