Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

426 lines (404 sloc) 15.329 kb
<?php // vim:ts=4:sw=4:et:fdm=marker
/*
* Undocumented
*
* @link http://agiletoolkit.org/
*//*
==ATK4===================================================
This file is part of Agile Toolkit 4
http://agiletoolkit.org/
(c) 2008-2013 Agile Toolkit Limited <info@agiletoolkit.org>
Distributed under Affero General Public License v3 and
commercial license.
See LICENSE or LICENSE_COM for more information
=====================================================ATK4=*/
/**
* Implementation of abstract form's field
*
* @author Romans <romans@adevel.com>
* @copyright See file COPYING
* @version $Id$
*/
abstract class Form_Field extends AbstractView {
/**
* Description of the field shown next to it on the form
*/
public $error_template; // template used to put errors on the field line
public $mandatory_template; // template used to mark mandatory fields
public $caption;
protected $value=null; // use $this->get(), ->set().
public $short_name=null;
public $attr=array();
public $no_save=null;
public $comment='&nbsp;';
protected $disabled=false;
protected $mandatory=false;
public $default_value=null;
// Field customization
private $separator=':';
public $show_input_only;
public $form=null;
public $button_prepend=null;
public $button_append=null;
function init(){
parent::init();
if(@$_GET[$this->owner->name.'_cut_field']==$this->name){
$this->api->addHook('pre-render',array($this,'_cutField'));
}
/** TODO: finish refactoring
// find the form
$obj=$this->owner;
while(!$obj instanceof Form){
if($obj === $this->app)throw $this->exception('You must add fields only inside Form');
$obj = $obj->owner;
}
$this->setForm($obj);
*/
}
function setForm($form){
$form->addHook('loadPOST',$this);
$form->addHook('validate',$this);
$this->form=$form;
$this->form->data[$this->short_name] = ($this->value!==null ? $this->value : $this->default_value);
$this->value =& $this->form->data[$this->short_name];
return $this;
}
function _cutField(){
// this method is used by ui.atk4_form, when doing reloadField();
unset($_GET['cut_object']);
$this->recursiveRender();
if($this->api->jquery)$this->api->jquery->getJS($this);
throw new Exception_StopRender(
$this->template->renderRegion($this->template->tags['before_field']).
$this->getInput().
$this->template->renderRegion($this->template->tags['after_field'])
);
}
function setMandatory($mandatory=true){
$this->mandatory=$mandatory;
return $this;
}
function setReadonly($readonly=true){
$this->readonly=$readonly;
return $this;
}
function isMandatory(){
return $this->mandatory;
}
function setCaption($_caption){
$this->caption=$this->api->_($_caption);
if ($this->show_input_only || !$this->template->hasTag('field_caption')) {
$this->setAttr('placeholder',$this->caption);
}
return $this;
}
function displayFieldError($msg=null){
if(!isset($msg))$msg='Error in field "'.$this->caption.'"';
$this->form->js(true)
->atk4_form('fieldError',$this->short_name,$msg)
->execute();
$this->form->errors[$this->short_name]=$msg;
}
function setNoSave(){
// Field value will not be saved into defined source (such as database)
$this->no_save=true;
return $this;
}
function disable(){
// sets 'disabled' property and setNoSave()
$this->setAttr('disabled');
$this->setNoSave();
$this->disabled=true;
return $this;
}
function isDisabled(){
return $this->disabled;
}
function set($value){
// Use this function when you want to assign $this->value.
// If you use this function, your field will operate in AJAX mode.
$this->value=$value;
return $this;
}
/** Position can be either 'before' or 'after' */
function addButton($label,$options=array()){
$position='after';
if(is_string($options)){
$position=$options;
}else{
if(isset($options['position']))$position=$options['position'];
}
if($position=='after'){
$button = $this->afterField()->add('Button',$options)->set($label);
}else{
$button = $this->beforeField()->add('Button',$options)->set($label);
}
$this->js('change', $button->js()->data('val', $this->js()->val()) );
return $button;
}
/** Layout changes in response to adding more elements before / after */
public $_icon=null;
/** Wraps input field into a <span> to align icon correctly */
function addIcon($icon,$link=null)
{
if(!$this->_icon){
$this->_icon=$this->add('Icon',null,'icon');
}
$this->_icon->set($icon);
if($link){
$this->_icon->setElement('a')
->setAttr('href',$link);
}
$this->template->trySetHTML('before_input','<span class="atk-input-icon atk-jackscrew">');
$this->template->trySetHTML('after_input','</span>');
return $this->_icon;
}
/** Will enable field wrappin inside a atk-cells/atk-cell block */
public $_use_cells=false;
function beforeField(){
$this->_use_cells=true;
return $this->add('View',null,'before_field')->addClass('atk-cell');
}
function afterField(){
$this->_use_cells=true;
return $this->add('View',null,'after_field')->addClass('atk-cell');
}
function aboveField(){
return $this->add('View',null,'above_field');
}
function belowField(){
return $this->add('View',null,'below_field');
}
function setComment($text=''){
$this->belowField()->setElement('ins')->set($text);
return $this;
}
function addComment($text=''){
return $this->belowField()->setElement('ins')->set($text);
}
function get(){
return $this->value;
}
function setClass($class){
$this->attr['class']=$class;
return $this;
}
function addClass($class){
$this->attr['class'].=($this->attr['class']?' ':'').$class;
return $this;
}
function setAttr($attr,$value=undefined){
if(is_array($attr)&&$value===undefined){
foreach($attr as $k=>$v) $this->setAttr($k,$v);
return $this;
}
if($attr){
$this->attr[$attr] = $value===undefined?'true':$value;
}
return $this;
}
function setProperty($property,$value){ // synonym, setAttr is preferred
return $this->setAttr($property,$value);
}
function setFieldHint($var_args=null){
/* Adds a hint after this field. Thes will call Field_Hint->set()
with same arguments you called this funciton.
*/
if(!$this->template->hasTag('after_field'))return $this;
$hint=$this->add('Form_Hint',null,'after_field');
call_user_func_array(array($hint,'set'), func_get_args());
return $this;
}
function loadPOST(){
if(isset($_POST[$this->name]))$this->set($_POST[$this->name]);
else $this->set($this->default_value);
$this->normalize();
}
function normalize(){
/* Normalization will make sure that entry conforms to the field type.
Possible trimming, rounding or length enforcements may happen. */
$this->hook('normalize');
}
function validate(){
// NoSave and disabled fields should not be validated
if($this->disabled || $this->no_save)return true;
// we define "validate" hook, so actual validators could hook it here
// and perform their checks
if(is_bool($result = $this->hook('validate')))return $result;
}
/** @private - handles field validation callback output */
function _validateField($caller,$condition,$msg){
$ret=call_user_func($condition,$this);
if($ret===false){
if(is_null($msg))$msg=$this->api->_('Error in ').$this->caption;
$this->displayFieldError($msg);
}elseif(is_string($ret)){
$this->displayFieldError($ret);
}
return $this;
}
/** Executes a callback. If callback returns string, shows it as error message.
* If callback returns "false" shows either $msg or a standard error message
* about field being incorrect */
function validateField($condition,$msg=null){
if(is_callable($condition)){
$this->addHook('validate',array($this,'_validateField'),array($condition,$msg));
}else{
$this->addHook('validate','if(!('.$condition.'))$this->displayFieldError("'.
($msg?:'Error in '.$this->caption).'");');
}
return $this;
}
function _validateNotNull($field){
if($field->get()==="" || is_null($field->get()))return false;
}
/** Adds "X is a mandatory field" message */
function validateNotNULL($msg=null){
$this->setMandatory();
if($msg && $msg!==true){
$msg=$this->api->_($msg);
}else{
$msg=sprintf($this->api->_('%s is a mandatory field'),$this->caption);
}
$this->validateField(array($this,'_validateNotNull'),$msg);
return $this;
}
function getInput($attr=array()){
// This function returns HTML tag for the input field. Derived classes
// should inherit this and add new properties if needed
return $this->getTag('input',
array_merge(array(
'name'=>$this->name,
'data-shortname'=>$this->short_name,
'id'=>$this->name,
'value'=>$this->value,
),$attr,$this->attr)
);
}
function setSeparator($separator){
$this->separator = $separator;
return $this;
}
function render(){
if($this->show_input_only){
$this->output($this->getInput());
return;
}
if(!$this->error_template)$this->error_template = $this->form->template_chunks['field_error'];
if((!property_exists($this, 'mandatory_template')) || (!$this->mandatory_template))$this->mandatory_template=$this->form->template_chunks['field_mandatory'];
$this->template->trySet('field_caption',$this->caption?($this->caption.$this->separator):'');
$this->template->trySet('field_name',$this->name);
$this->template->trySet('field_comment',$this->comment);
// some fields may not have field_input tag at all...
if($this->_use_cells){
$this->template->trySetHTML('cells_start','<div class="atk-cells atk-input-combo">');
$this->template->trySetHTML('cells_stop','</div>');
$this->template->trySetHTML('input_cell_start','<div class="atk-cell atk-jackscrew">');
$this->template->trySetHTML('input_cell_stop','</div>');
}
$this->template->trySetHTML('field_input',$this->getInput());
$this->template->trySetHTML('field_error',
isset($this->form->errors[$this->short_name])?
$this->error_template->set('field_error_str',$this->form->errors[$this->short_name])->render()
:''
);
if (is_object($this->mandatory_template)) {
$this->template->trySetHTML('field_mandatory',$this->isMandatory()?$this->mandatory_template->render():'');
}
$this->output($this->template->render());
}
function getTag($tag, $attr=null, $value=null){
/**
* Draw HTML attribute with supplied attributes.
*
* Short description how this getTag may be used:
*
* Use get tag to build HTML tag.
* echo getTag('img',array('src'=>'foo.gif','border'=>0);
*
* The unobvius advantage of this function is ability to merge
* attribute arrays. For example, if you have function, which
* must display img tag, you may add optional $attr argument
* to this function.
*
* function drawImage($src,$attr=array()){
* echo getTag('img',array_merge(array('src'=>$src),$attr));
* }
*
* so calling drawImage('foo.gif') will echo: <img src="foo.gif">
*
* The benefit from such a function shows up when you use 2nd argument:
*
* 1. adding additional attributes
* drawImage('foo.gif',array('border'=>0'));
* --> <img src="foo.gif" border="0">
* (NOTE: you can even have attr templates!)
*
* 2. adding no-value attributes, such as nowrap:
* getTag('td',arary('nowrap'=>true));
* --> <td nowrap>
*
* 3. disabling some attributes.
* drawImage('foo.gif',array('src'=>false));
* --> <img>
*
* 4. re-defining attributes
* drawImage('foo.gif',array('src'=>'123'));
* --> <img src="123">
*
* 5. or you even can re-define tag itself
* drawImage('foo.gif',array(
* ''=>'input',
* 'type'=>'picture'));
* --> <input type="picture" src="foo.gif">
*
* 6. xml-valid tags without closing tag
* getTag('img/',array('src'=>'foo.gif'));
* --> <img src=>"foo.gif"/>
*
* 7. closing tags
* getTag('/td');
* --> </td>
*
* 8. using $value will add $value after tag followed by closing tag
* getTag('a',array('href'=>'foo.html'),'click here');
* --> <a href="foo.html">click here</a>
*
* 9. you may not skip attribute argument.
* getTag('b','text in bold');
* --> <b>text in bold</b>
*
* 10. nesting
* getTag('a',array('href'=>'foo.html'),getTag('b','click here'));
* --> <a href="foo.html"><b>click here</b></a>
*/
if(is_string($attr)){
$value=$attr;
$attr=null;
}
if(!$attr){
return "<$tag>".($value?$value."</$tag>":"");
}
$tmp = array();
if(substr($tag,-1)=='/'){
$tag = substr($tag,0,-1);
$postfix = '/';
} else $postfix = '';
foreach ($attr as $key => $val) {
if($val === false) continue;
if($val === true) $tmp[] = "$key";
elseif($key === '')$tag=$val;
else $tmp[] = "$key=\"".$this->api->encodeHtmlChars($val)."\"";
}
return "<$tag ".join(' ',$tmp).$postfix.">".($value?$value."</$tag>":"");
}
function destroy(){
parent::destroy();
if ($this->form != $this->owner) {
$this->form->_removeElement($this->short_name);
}
}
function defaultTemplate(){
return array('form_field');
}
}
Jump to Line
Something went wrong with that request. Please try again.