2002-02-06 10:03:11 +01:00
< ? php
2008-10-07 11:14:17 +02:00
/**
2014-03-23 09:41:31 +01:00
* EGroupware - EditableTemplates - HTML User Interface
*
* @ link http :// www . egroupware . org
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ author Ralf Becker < RalfBecker @ outdoor - training . de >
* @ copyright 2002 - 14 by RalfBecker @ outdoor - training . de
* @ package etemplate
* @ subpackage api
* @ version $Id $
*/
2012-03-27 17:45:31 +02:00
2008-10-07 11:14:17 +02:00
/**
2014-03-23 09:41:31 +01:00
* creates dialogs / HTML - forms from eTemplate descriptions
*
* Usage example :
*< code >
* $tmpl = new etemplate ( 'app.template.name' );
* $tmpl -> exec ( 'app.class.callback' , $content_to_show );
*</ code >
* This creates a form from the eTemplate 'app.template.name' and takes care that
* the method / public function 'callback' in class ' class ' of ' app ' gets called
* if the user submits the form . For the complete param ' s see the description of exec .
*
* etemplate or uietemplate extends boetemplate , all vars and public functions are inherited
*/
class etemplate extends boetemplate
2008-10-07 11:14:17 +02:00
{
/**
2014-03-23 09:41:31 +01:00
* integer debug - level or template - name or cell - type or '' = off
* 1 = calls to show and process_show , 2 = content after process_show ,
* 3 = calls to show_cell and process_show_cell
*
* @ public int / string
*/
public $debug ;
/**
* Inner width of browser window
*
* @ public int
*/
public $innerWidth ;
/**
* Reference to the content - param of the last call to show , for extensions to use
*
* @ public array
*/
public $content ;
/**
* Reference to the sel_options - param of the last call to show , for extensions to use
*
* @ public array
*/
public $sel_options ;
/**
* Name of the form of the currently processed etemplate
*
* @ public string
*/
static $name_form = 'eTemplate' ;
/**
* Used form - names in this request
*
* @ public array
*/
static $name_forms = array ();
/**
* Basename of the variables ( content ) in $_POST and id 's, usually ' exec ' ,
* if there ' s not more then one eTemplate on the page ( then it will be exec , exec2 , exec3 , ...
*
* @ public string
*/
static $name_vars = 'exec' ;
/**
* Are we running as sitemgr module or not
*
* @ public boolean
*/
public $sitemgr = false ;
/**
* Javascript to be called , when a widget get ' s double - clicked ( used only by the editor )
* A '%p' gets replace with the colon ':' separated template - name , - version and path of the clicked widget .
2009-03-16 13:58:24 +01:00
*
2014-03-23 09:41:31 +01:00
* @ public string
2009-03-16 13:58:24 +01:00
*/
2014-03-23 09:41:31 +01:00
public $onclick_handler ;
/**
* Does template processes onclick itself , or forwards it to a proxy
*
* @ var boolean
*/
public $no_onclick = false ;
/**
* handler to call for onclick
*
* @ var string
*/
public $onclick_proxy ;
/**
* Extra options for forms , eg . enctype = " multipart/form-data "
*
* @ public string
*/
static protected $form_options = '' ;
/**
* Validation errors from process_show and the extensions , should be set via self :: set_validation_error
*
* @ public array form_name => message pairs
*/
static protected $validation_errors = array ();
2009-03-16 13:58:24 +01:00
2012-04-18 00:58:39 +02:00
/**
2014-03-23 09:41:31 +01:00
* Flag if exec () is called as part of a hook , replaces the 1.6 and earlier $GLOBALS [ 'egw_info' ][ 'etemplate' ][ 'hooked' ] global variable
*
* @ var boolean
2012-04-18 00:58:39 +02:00
*/
2014-03-23 09:41:31 +01:00
static public $hooked ;
2012-04-18 00:58:39 +02:00
2009-03-16 13:58:24 +01:00
/**
2014-03-23 09:41:31 +01:00
* The following 3 static vars are used to allow eTemplate apps to hook into each other
2009-03-16 13:58:24 +01:00
*
2014-03-23 09:41:31 +01:00
* @ var mixed
2009-03-16 13:58:24 +01:00
*/
2014-03-23 09:41:31 +01:00
static private $previous_content ;
static private $hook_content ;
static private $hook_app ;
/**
* constructor of etemplate class , reads an eTemplate if $name is given
*
* @ param string $name of etemplate or array with name and other keys
* @ param string / array $load_via with keys of other etemplate to load in order to get $name
*/
2009-03-16 13:58:24 +01:00
function __construct ( $name = '' , $load_via = '' )
2008-10-07 11:14:17 +02:00
{
2014-03-23 09:41:31 +01:00
// tell framework old eTemplate apps needs eval and inline javascript :(
egw_framework :: csp_script_src_attrs ( array ( 'unsafe-eval' , 'unsafe-inline' ));
2014-03-10 12:58:49 +01:00
2014-03-23 09:41:31 +01:00
parent :: __construct ( $name , $load_via );
2008-10-07 11:14:17 +02:00
2014-03-23 09:41:31 +01:00
$this -> sitemgr = isset ( $GLOBALS [ 'Common_BO' ]) && is_object ( $GLOBALS [ 'Common_BO' ]);
2012-07-09 23:24:47 +02:00
2014-03-23 09:41:31 +01:00
if (( $this -> innerWidth = ( int ) $_POST [ 'innerWidth' ]))
{
$GLOBALS [ 'egw' ] -> session -> appsession ( 'innerWidth' , 'etemplate' , $this -> innerWidth );
}
elseif ( ! ( $this -> innerWidth = ( int ) $GLOBALS [ 'egw' ] -> session -> appsession ( 'innerWidth' , 'etemplate' )))
{
$this -> innerWidth = 1018 ; // default width for an assumed screen-resolution of 1024x768
}
//echo "<p>_POST[innerWidth]='$_POST[innerWidth]', innerWidth=$this->innerWidth</p>\n";
2008-10-07 11:14:17 +02:00
}
/**
2014-03-23 09:41:31 +01:00
* Abstracts a html - location - header call
*
* In other UI ' s than html this needs to call the methode , defined by menuaction or
* open a browser - window for any other links .
*
* @ param string / array $params url or array with get - params incl . menuaction
*/
2008-10-07 11:14:17 +02:00
static function location ( $params = '' )
{
2009-03-16 13:58:24 +01:00
egw :: redirect_link ( is_array ( $params ) ? '/index.php' : $params ,
2008-10-07 11:14:17 +02:00
is_array ( $params ) ? $params : '' );
}
/**
2014-03-23 09:41:31 +01:00
* Generats a Dialog from an eTemplate - abstract the UI - layer
*
* This is the only function an application should use , all other are INTERNAL and
* do NOT abstract the UI - layer , because they return HTML .
* Generates a webpage with a form from the template and puts process_exec in the
* form as submit - url to call process_show for the template before it
* ExecuteMethod ' s the given $method of the caller .
*
* @ param string $method Methode ( e . g . 'etemplate.editor.edit' ) to be called if form is submitted
* @ param array $content with content to fill the input - fields of template , eg . the text - field
* with name 'name' gets its content from $content [ 'name' ]
* @ param $sel_options array or arrays with the options for each select - field , keys are the
* field - names , eg . array ( 'name' => array ( 1 => 'one' , 2 => 'two' )) set the
* options for field 'name' . ( $content [ 'options-name' ] is possible too !!! )
* @ param array $readonlys with field - names as keys for fields with should be readonly
* ( eg . to implement ACL grants on field - level or to remove buttons not applicable )
* @ param array $preserv with vars which should be transported to the $method - call ( eg . an id ) array ( 'id' => $id ) sets $_POST [ 'id' ] for the $method - call
* @ param int $output_mode
* 0 = echo incl . navbar
* 1 = return html
* - 1 = first time return html , after use 0 ( echo html incl . navbar ), eg . for home
* 2 = echo without navbar ( eg . for popups )
* 3 = return eGW independent html site
* @ param string $ignore_validation if not empty regular expression for validation - errors to ignore
* @ param array $changes change made in the last call if looping , only used internaly by process_exec
* @ return string html for $output_mode == 1 , else nothing
*/
function exec ( $method , $content , $sel_options = '' , $readonlys = '' , $preserv = '' , $output_mode = 0 , $ignore_validation = '' , $changes = '' )
2008-10-07 11:14:17 +02:00
{
2014-03-23 09:41:31 +01:00
if ( ! $sel_options )
{
$sel_options = array ();
}
if ( ! $readonlys )
{
$readonlys = array ();
}
if ( ! $preserv )
{
$preserv = array ();
}
if ( ! $changes )
{
$changes = array ();
}
if ( isset ( $content [ 'app_header' ]))
{
$GLOBALS [ 'egw_info' ][ 'flags' ][ 'app_header' ] = $content [ 'app_header' ];
}
if ( $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ] != 'etemplate' )
{
translation :: add_app ( 'etemplate' ); // some extensions have own texts
}
// use different form-names to allows multiple eTemplates in one page, eg. addressbook-view
self :: $name_form = 'eTemplate' ;
if ( in_array ( self :: $name_form , self :: $name_forms ))
{
self :: $name_form .= 1 + count ( self :: $name_forms );
self :: $name_vars .= 1 + count ( self :: $name_forms );
}
self :: $name_forms [] = self :: $name_form ;
2012-03-27 17:45:31 +02:00
2014-03-23 09:41:31 +01:00
self :: $request = etemplate_request :: read ();
2009-03-16 13:58:24 +01:00
self :: $request -> output_mode = $output_mode ; // let extensions "know" they are run eg. in a popup
2014-03-23 09:41:31 +01:00
self :: $request -> readonlys = $readonlys ;
self :: $request -> content = $content ;
2009-03-16 13:58:24 +01:00
self :: $request -> changes = $changes ;
2014-03-23 09:41:31 +01:00
self :: $request -> sel_options = $sel_options ;
self :: $request -> preserv = $preserv ;
2009-03-16 13:58:24 +01:00
self :: $request -> method = $method ;
self :: $request -> ignore_validation = $ignore_validation ;
2014-03-23 09:41:31 +01:00
self :: $request -> name_vars = self :: $name_vars ;
2012-03-27 17:45:31 +02:00
2014-03-23 09:41:31 +01:00
// tell html5 form validation NOT to validate
if ( $ignore_validation ) self :: $form_options .= ' novalidate="novalidate"' ;
2012-03-27 17:45:31 +02:00
2014-03-23 09:41:31 +01:00
if (( int ) $output_mode == 3 )
2013-10-07 19:00:03 +02:00
{
2014-03-23 09:41:31 +01:00
self :: $styles_included [ $this -> name ] = True ;
return " <!DOCTYPE html PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'> \n "
. " <html> \n <head> \n " . html :: style ( $this -> style ) . " \n </head> \n "
. " <body> \n " . $this -> show ( $content ) . " \n </body> \n </html> " ;
2013-10-07 19:00:03 +02:00
}
2014-03-23 09:41:31 +01:00
$html = $this -> show ( self :: complete_array_merge ( $content , $changes ), $sel_options , $readonlys , self :: $name_vars );
2013-08-20 14:06:41 +02:00
2014-03-23 09:41:31 +01:00
self :: $request -> java_script_from_flags = $GLOBALS [ 'egw_info' ][ 'flags' ][ 'java_script' ];
self :: $request -> java_script_body_tags = array (
'onload' => egw_framework :: set_onload (),
'onunload' => egw_framework :: set_onunload (),
'onresize' => egw_framework :: set_onresize (),
2013-07-20 15:57:53 +02:00
);
2014-03-23 09:41:31 +01:00
self :: $request -> java_script_files = egw_framework :: js_files ();
self :: $request -> include_xajax = $GLOBALS [ 'egw_info' ][ 'flags' ][ 'include_xajax' ];
// check if application of template has a app.js file --> load it
list ( $app ) = explode ( '.' , $this -> name );
if ( file_exists ( EGW_SERVER_ROOT . '/' . $app . '/js/app.js' ))
2009-07-23 13:25:10 +02:00
{
2014-03-23 09:41:31 +01:00
egw_framework :: validate_file ( '.' , 'app' , $app , false );
2009-07-23 13:25:10 +02:00
}
2014-03-23 09:41:31 +01:00
if ( ! $this -> sitemgr )
2009-03-16 13:58:24 +01:00
{
2014-03-23 09:41:31 +01:00
$hooked = isset ( self :: $previous_content ) || ! isset ( $GLOBALS [ 'egw' ] -> template ) ?
self :: $previous_content : $GLOBALS [ 'egw' ] -> template -> get_var ( 'phpgw_body' );
}
self :: $request -> hooked = $hooked ? $hooked : self :: $hook_content ;
self :: $request -> hook_app = $hooked ? $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ] : self :: $hook_app ;
self :: $request -> app_header = $GLOBALS [ 'egw_info' ][ 'flags' ][ 'app_header' ];
if ( self :: $request -> output_mode == - 1 ) self :: $request -> output_mode = 0 ;
self :: $request -> template = $this -> as_array ( 2 );
2009-03-16 13:58:24 +01:00
2014-03-23 09:41:31 +01:00
$html = html :: form ( html :: input ( 'etemplate_exec_id' , self :: $request -> id (), 'hidden' , ' id="etemplate_exec_id"' ) . $html .
html :: input_hidden ( array (
'submit_button' => '' ,
'innerWidth' => '' ,
), '' , false ), array (), $this -> sitemgr ? '' : '/etemplate/process_exec.php?menuaction=' . $method ,
'' , self :: $name_form , self :: $form_options .
// dont set the width of popups!
( $output_mode != 0 ? '' : ' onsubmit="this.innerWidth.value=window.innerWidth ? window.innerWidth : document.body.clientWidth;"' ));
//echo "to_process="; _debug_array(self::$request->to_process);
2013-04-16 20:46:23 +02:00
2014-03-23 09:41:31 +01:00
egw_framework :: validate_file ( '/etemplate/js/etemplate.js' );
2013-02-05 15:38:16 +01:00
2014-03-23 09:41:31 +01:00
//echo '<p>'.__METHOD__."($method,...) etemplate[hooked]=".(int)self::$hooked.", etemplate[hook_app]='".self::$hook_app."', isset(etemplate[content])=".(int)isset(self::$previous_content)."</p>\n";
if ( ! $this -> sitemgr )
{
// support the old global var, in case old apps like 1.6 infolog use it
if ( isset ( $GLOBALS [ 'egw_info' ][ 'etemplate' ][ 'hooked' ])) self :: $hooked = $GLOBALS [ 'egw_info' ][ 'etemplate' ][ 'hooked' ];
if ( !@ self :: $hooked && ( int ) $output_mode != 1 && ( int ) $output_mode != - 1 ) // not just returning the html
2013-02-13 17:30:30 +01:00
{
2014-03-23 09:41:31 +01:00
if ( $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ] != 'etemplate' )
2014-01-09 13:20:13 +01:00
{
2014-03-23 09:41:31 +01:00
egw_framework :: includeCSS ( 'etemplate' , 'app' );
2014-01-09 13:20:13 +01:00
}
2013-11-13 23:43:19 +01:00
}
2014-03-23 09:41:31 +01:00
// saving the etemplate content for other hooked etemplate apps (atm. infolog hooked into addressbook)
self :: $previous_content =& $html ;
}
//echo '<p>'.__METHOD__."($method,...) after show: sitemgr=$this->sitemgr, hooked=".(int)$hooked.", output_mode=$output_mode</p>\n";
if ( $output_mode == 2 )
{
$html .= '
< script language = " javascript " >
egw_LAB . wait ( function (){
$j () . ready ( function () {
2014-03-24 10:03:38 +01:00
window . setTimeout ( popup_resize , 150 );
2014-03-23 09:41:31 +01:00
});
});
</ script > ' . " \n " ;
}
2013-11-03 10:30:25 +01:00
2014-03-23 09:41:31 +01:00
if ( ! $this -> sitemgr && ( int ) $output_mode != 1 && ( int ) $output_mode != - 1 ) // NOT returning html
{
if ( !@ self :: $hooked )
2014-03-10 12:58:49 +01:00
{
2014-03-25 13:12:00 +01:00
// let framework know, if we are a popup or not ('popup' not true, which is allways used by index.php!)
$GLOBALS [ 'egw_info' ][ 'flags' ][ 'nonavbar' ] = $output_mode == 2 ? 'popup' : false ;
2014-03-23 09:41:31 +01:00
if (( int ) $output_mode != 2 )
{
echo $GLOBALS [ 'egw' ] -> framework -> navbar ();
}
else
{
2014-05-15 11:41:31 +02:00
echo $GLOBALS [ 'egw' ] -> framework -> header ();
2014-03-23 09:41:31 +01:00
echo '<div id="popupMainDiv">' . " \n " ;
if ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'manual' ]) // adding a manual icon to every popup
{
2014-03-23 10:34:18 +01:00
$manual = new etemplate ( 'etemplate.popup.manual' );
2014-03-23 09:41:31 +01:00
echo $manual -> show ( array ());
unset ( $manual );
echo '<style type="text/css">.ajax-loader { position: absolute; right: 27px; top: 24px; display: none; }</style>' . " \n " ;
echo '<div class="ajax-loader">' . html :: image ( 'phpgwapi' , 'ajax-loader' ) . '</div>' ;
}
}
2013-02-13 17:30:30 +01:00
}
2014-03-23 09:41:31 +01:00
echo self :: $hook_content . $html ;
2013-08-20 14:06:41 +02:00
2014-03-23 09:41:31 +01:00
if ( ! self :: $hooked && ( ! isset ( $_GET [ 'menuaction' ]) || strpos ( $_SERVER [ 'PHP_SELF' ], 'process_exec.php' ) !== false ))
2013-06-12 23:06:40 +02:00
{
2014-03-23 09:41:31 +01:00
if (( int ) $output_mode == 2 )
{
echo " </div> \n " ;
}
common :: egw_footer ();
2013-06-12 23:06:40 +02:00
}
2010-02-03 15:11:16 +01:00
}
2014-03-23 09:41:31 +01:00
if ( $this -> sitemgr || ( int ) $output_mode == 1 || ( int ) $output_mode == - 1 ) // return html
{
return $html ;
}
2012-03-27 17:45:31 +02:00
}
2010-02-03 15:11:16 +01:00
2012-03-27 17:45:31 +02:00
/**
2014-03-23 09:41:31 +01:00
* Check if we have not ignored validation errors
*
* @ param string $ignore_validation = '' if not empty regular expression for validation - errors to ignore
* @ param string $cname = null name - prefix , which need to be ignored , default self :: $name_vars
* @ return boolean true if there are not ignored validation errors , false otherwise
*/
static function validation_errors ( $ignore_validation = '' , $cname = null )
2012-03-27 17:45:31 +02:00
{
2014-03-23 09:41:31 +01:00
if ( is_null ( $cname )) $cname = self :: $name_vars ;
//echo "<p>uiself::validation_errors('$ignore_validation','$cname') validation_error="; _debug_array(self::$validation_errors);
if ( ! $ignore_validation ) return count ( self :: $validation_errors ) > 0 ;
2012-03-27 17:45:31 +02:00
2014-03-23 09:41:31 +01:00
foreach ( self :: $validation_errors as $name => $error )
2008-10-07 11:14:17 +02:00
{
2014-03-23 09:41:31 +01:00
if ( ! self :: ignore_validation_match ( $name , $ignore_validation ))
{
//echo "<p>uiself::validation_errors('$ignore_validation','$cname') name='$name' ($error) not ignored!!!</p>\n";
return true ;
}
//echo "<p>uiself::validation_errors('$ignore_validation','$cname') name='$name' ($error) ignored</p>\n";
2013-07-17 11:57:50 +02:00
}
2014-03-23 09:41:31 +01:00
return false ;
2012-04-18 00:58:39 +02:00
}
2014-01-16 13:13:16 +01:00
/**
2014-03-23 09:41:31 +01:00
* Check if given form - name matches ai ignore - validation rule
2014-01-16 13:13:16 +01:00
*
2014-03-23 09:41:31 +01:00
* @ param string $ignore_validation = '' if not empty regular expression for validation - errors to ignore
* @ param string $cname = null name - prefix , which need to be ignored , default self :: $name_vars
* @ param string $cname
* @ return boolean
2014-01-16 13:13:16 +01:00
*/
2014-03-23 09:41:31 +01:00
static function ignore_validation_match ( $form_name , $ignore_validation , $cname = null )
2014-01-16 13:13:16 +01:00
{
2014-03-23 09:41:31 +01:00
if ( is_null ( $cname )) $cname = self :: $name_vars ;
if ( $cname ) $form_name = preg_replace ( '/^' . $cname . '\[([^\]]+)\](.*)$/' , '\\1\\2' , $form_name );
return empty ( $ignore_validation ) ||
$ignore_validation [ 0 ] == '/' && preg_match ( $ignore_validation , $form_name ) ||
$ignore_validation [ 0 ] != '/' && $ignore_validation == $form_name ;
2014-01-16 13:13:16 +01:00
}
2012-04-18 00:58:39 +02:00
/**
2014-03-23 09:41:31 +01:00
* Makes the necessary adjustments to _POST before it calls the app ' s method
*
* This function is only to submit forms to , create with exec .
* All eTemplates / forms executed with exec are submited to this function
* via / etemplate / process_exec . php ? menuaction =< callback >. We cant use the global index . php as
* it would set some constants to etemplate instead of the calling app .
* process_exec then calls process_show for the eTemplate ( to adjust the content of the _POST ) and
* ExecMethod ' s the given callback from the app with the content of the form as first argument .
*
* @ return mixed false if no sessiondata and $this -> sitemgr , else the returnvalue of exec of the method - calls
*/
function process_exec ( $etemplate_exec_id = null , $submit_button = null , $exec = null , $type = 'regular' )
2012-04-18 00:58:39 +02:00
{
2014-03-23 09:41:31 +01:00
if ( ! $etemplate_exec_id ) $etemplate_exec_id = $_POST [ 'etemplate_exec_id' ];
if ( ! $submit_button ) $submit_button = $_POST [ 'submit_button' ];
if ( ! $exec ) $exec = $_POST ;
//echo "process_exec: _POST ="; _debug_array($_POST);
if ( ! $etemplate_exec_id || ! ( self :: $request = etemplate_request :: read ( $etemplate_exec_id )))
2012-07-11 22:06:37 +02:00
{
2014-03-23 09:41:31 +01:00
if ( $this -> sitemgr ) return false ;
//echo "uitemplate::process_exec() id='$_POST[etemplate_exec_id]' invalid session-data !!!"; _debug_array($_SESSION);
// this prevents an empty screen, if the sessiondata gets lost somehow
$redirect = array (
'menuaction' => $_GET [ 'menuaction' ],
);
if ( ! $_POST && $_SERVER [ 'REQUEST_METHOD' ] == 'POST' )
{
$redirect [ 'post_empty' ] = 1 ;
// check if we have a failed upload, because user tried to uploaded a file
// bigger then php.ini setting post_max_size
// in that case the webserver calls PHP with $_POST === array()
if ( substr ( $_SERVER [ 'CONTENT_TYPE' ], 0 , 19 ) == 'multipart/form-data' &&
$_SERVER [ 'CONTENT_LENGTH' ] > self :: km2int ( ini_get ( 'post_max_size' )))
{
$redirect [ 'failed_upload' ] = 1 ;
$redirect [ 'msg' ] = lang ( 'Error uploading file!' ) . " \n " . self :: max_upload_size_message ();
}
} else if ( $_SERVER [ 'REQUEST_METHOD' ] == 'GET' ) {
// Pass along any parameters
$redirect = $_GET ;
}
$this -> location ( $redirect );
2012-07-11 22:06:37 +02:00
}
2014-03-23 09:41:31 +01:00
self :: $name_vars = self :: $request -> name_vars ;
if ( isset ( $submit_button ) && ! empty ( $submit_button ))
2012-04-18 00:58:39 +02:00
{
2014-03-23 09:41:31 +01:00
self :: set_array ( $exec , $submit_button , 'pressed' );
2012-04-18 00:58:39 +02:00
}
2014-03-23 09:41:31 +01:00
$content = $exec [ self :: $name_vars ];
if ( ! is_array ( $content ))
2012-04-18 00:58:39 +02:00
{
2014-03-23 09:41:31 +01:00
$content = array ();
2012-04-18 00:58:39 +02:00
}
2014-03-23 09:41:31 +01:00
$this -> init ( self :: $request -> template );
//echo "process_exec($this->name) content ="; _debug_array($content);
if ( $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ] != 'etemplate' )
{
$GLOBALS [ 'egw' ] -> translation -> add_app ( 'etemplate' ); // some extensions have own texts
}
$this -> process_show ( $content , self :: $request -> to_process , self :: $name_vars , $type );
2013-11-27 01:06:12 +01:00
2014-03-23 09:41:31 +01:00
self :: $loop |= ! $this -> canceled && $this -> button_pressed &&
$this -> validation_errors ( self :: $request -> ignore_validation ); // set by process_show
2008-10-07 11:14:17 +02:00
2014-03-23 09:41:31 +01:00
// If a tab has an error on it, change to that tab
foreach ( self :: $validation_errors as $form_name => $msg )
{
$name = $this -> template_name ( $form_name );
if ( ! $this -> get_widget_by_name ( $name ))
{
foreach ( $this -> get_widgets_by_type ( 'tab' ) as $widget )
{
$tab_name = $tabs = $widget [ 'name' ];
if ( strpos ( $tabs , '=' ) !== false ) list ( $tab_name , $tabs ) = explode ( '=' , $tabs , 2 );
foreach ( explode ( '|' , $tabs ) as $tab )
{
if ( strpos ( '.' , $tab ) === false ) $tab = $this -> name . '.' . $tab ;
2014-03-23 10:34:18 +01:00
$tab_tpl = new etemplate ( $tab );
2014-03-23 09:41:31 +01:00
if ( $tab_tpl -> get_widget_by_name ( $name ))
{
$content [ $tab_name ] = $tab ;
break 3 ;
}
elseif ( $name [ 0 ] == '#' )
{
foreach ( $tab_tpl -> get_widgets_by_type ( 'customfields' ) as $cf_widget )
{
if ( empty ( $cf_widget [ 'name' ]))
{
$content [ $tab_name ] = $tab ;
break 4 ;
}
}
}
}
}
// widget NOT found --> as last resort add valdation message to $content['msg']
$content [ 'msg' ] .= ( $content [ 'msg' ] ? " \n " : '' ) . $msg ;
}
}
2008-10-07 11:14:17 +02:00
2014-03-23 09:41:31 +01:00
//echo "process_exec($this->name) process_show(content) ="; _debug_array($content);
//echo "process_exec($this->name) session_data[changes] ="; _debug_array(self::$request->changes);
$content = self :: complete_array_merge ( self :: $request -> changes , $content );
//echo "process_exec($this->name) merge(changes,content) ="; _debug_array($content);
2008-10-07 11:14:17 +02:00
2014-03-23 09:41:31 +01:00
if ( self :: $loop && $type == 'regular' ) // only loop for regular (not ajax_submit) requests
{
if ( self :: $request -> hooked != '' ) // set previous phpgw_body if we are called as hook
{
self :: $hook_content = self :: $request -> hooked ;
$GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ] = self :: $hook_app = self :: $request -> hook_app ;
2008-10-07 11:14:17 +02:00
2014-03-23 09:41:31 +01:00
// Prevent previous content form names from being used again in exec()
preg_match_all ( '/<form .+name="(' . self :: $name_form . '[\d]*)"/' , self :: $request -> hooked , $used );
if ( is_array ( $used [ 1 ]) && count ( $used [ 1 ]))
{
self :: $name_forms += $used [ 1 ];
}
}
if ( self :: $request -> include_xajax ) $GLOBALS [ 'egw_info' ][ 'flags' ][ 'include_xajax' ] = true ;
2008-10-07 11:14:17 +02:00
2014-03-23 09:41:31 +01:00
if ( ! empty ( self :: $request -> app_header ))
{
$GLOBALS [ 'egw_info' ][ 'flags' ][ 'app_header' ] = self :: $request -> app_header ;
}
2008-10-07 11:14:17 +02:00
2014-03-23 09:41:31 +01:00
$GLOBALS [ 'egw_info' ][ 'flags' ][ 'java_script' ] .= self :: $request -> java_script_from_flags ;
if ( ! empty ( self :: $request -> java_script_body_tags ))
{
foreach ( self :: $request -> java_script_body_tags as $tag => $code )
{
call_user_func ( 'egw_framework::set_' . $tag , $code );
}
}
if ( is_array ( self :: $request -> java_script_files ))
2008-10-07 11:14:17 +02:00
{
2014-03-23 09:41:31 +01:00
$files = egw_framework :: js_files ();
if ( is_array ( $files ))
2010-06-15 18:08:10 +02:00
{
2014-03-23 09:41:31 +01:00
$files = array_unique ( array_merge ( $files , self :: $request -> java_script_files ));
2010-06-15 18:08:10 +02:00
}
else
{
2014-03-23 09:41:31 +01:00
$files = self :: $request -> java_script_files ;
2010-06-15 18:08:10 +02:00
}
2014-03-23 09:41:31 +01:00
egw_framework :: js_files ( $files );
2008-10-07 11:14:17 +02:00
}
2012-03-27 17:45:31 +02:00
2014-03-23 09:41:31 +01:00
//echo "<p>process_exec($this->name): <font color=red>loop is set</font>, content=</p>\n"; _debug_array(self::complete_array_merge(self::$request->content,$content));
return $this -> exec ( self :: $request -> method , self :: complete_array_merge ( self :: $request -> content , $content ),
self :: $request -> sel_options , self :: $request -> readonlys , self :: $request -> preserv ,
self :: $request -> output_mode , self :: $request -> ignore_validation , $content );
}
else
{
//echo "<p>process_exec($this->name): calling ".($type == 'regular' ? self::$request->method : $_GET['menuaction'])."</p>\n";
return ExecMethod ( $type == 'regular' ? self :: $request -> method : $_GET [ 'menuaction' ],
self :: complete_array_merge ( self :: $request -> preserv , $content ));
}
2008-10-07 11:14:17 +02:00
}
2010-02-05 03:54:47 +01:00
/**
* Message containing the max Upload size from the current php . ini settings
*
* We have to take the smaler one of upload_max_filesize AND post_max_size - 2800 into account .
* memory_limit does NOT matter any more , because of the stream - interface of the vfs .
*
2011-09-08 21:00:57 +02:00
* @ param int & $max_upload = null on return max . upload size in byte
2010-02-05 03:54:47 +01:00
* @ return string
*/
2011-09-08 21:00:57 +02:00
static function max_upload_size_message ( & $max_upload = null )
2010-02-05 03:54:47 +01:00
{
$upload_max_filesize = ini_get ( 'upload_max_filesize' );
$post_max_size = ini_get ( 'post_max_size' );
$max_upload = min ( self :: km2int ( $upload_max_filesize ), self :: km2int ( $post_max_size ) - 2800 );
return lang ( 'Maximum size for uploads' ) . ': ' . egw_vfs :: hsize ( $max_upload ) .
" (php.ini: upload_max_filesize= $upload_max_filesize , post_max_size= $post_max_size ) " ;
}
/**
* Convert numbers like '32M' or '512k' to integers
*
* @ param string $size
* @ return int
*/
private static function km2int ( $size )
{
if ( ! is_numeric ( $size ))
{
switch ( strtolower ( substr ( $size , - 1 )))
{
case 'm' :
$size = 1024 * 1024 * ( int ) $size ;
break ;
case 'k' :
$size = 1024 * ( int ) $size ;
break ;
}
}
return ( int ) $size ;
}
2008-10-07 11:14:17 +02:00
/**
2014-03-23 09:41:31 +01:00
* process the values transfered with the javascript function values2url
2008-10-07 11:14:17 +02:00
*
2014-03-23 09:41:31 +01:00
* The returned array contains the preserved values overwritten ( only ! ) with the variables named in values2url
2012-03-27 17:45:31 +02:00
*
2014-03-23 09:41:31 +01:00
* @ return array / boolean content array or false on error
*/
function process_values2url ()
{
//echo "process_exec: _GET ="; _debug_array($_GET);
if ( ! $_GET [ 'etemplate_exec_id' ] || ! ( $request = etemplate_request :: read ( $_GET [ 'etemplate_exec_id' ])))
{
return false ;
}
self :: $name_vars = $request -> name_vars ;
$content = $_GET [ self :: $name_vars ];
if ( ! is_array ( $content ))
{
$content = array ();
}
$this -> process_show ( $content , $request -> to_process , self :: $name_vars );
return self :: complete_array_merge ( $request -> preserv , $content );
}
/**
* Flag if the styles of a certain template are already included
*
* @ var array template - name => boolean
*/
static private $styles_included = array ();
/**
* creates HTML from an eTemplate
*
* This is done by calling show_cell for each cell in the form . show_cell itself
* calls show recursivly for each included eTemplate .
2008-10-07 11:14:17 +02:00
* You could use it in the UI - layer of an app , just make shure to call process_show !!!
* This is intended as internal function and should NOT be called by new app ' s direct ,
* as it deals with HTML and is so UI - dependent , use exec instead .
*
2014-03-23 09:41:31 +01:00
* @ internal
2008-10-07 11:14:17 +02:00
* @ param array $content with content for the cells , keys are the names given in the cells / form elements
* @ param array $sel_options with options for the selectboxes , keys are the name of the selectbox
* @ param array $readonlys with names of cells / form - elements to be not allowed to change
* This is to facilitate complex ACL ' s which denies access on field - level !!!
* @ param string $cname basename of names for form - elements , means index in $_POST
* eg . $cname = 'cont' , element - name = 'name' returned content in $_POST [ 'cont' ][ 'name' ]
* @ param string $show_c name / index for name expansion
* @ param string $show_row name / index for name expansion
* @ return string the generated HTML
*/
function show ( $content , $sel_options = '' , $readonlys = '' , $cname = '' , $show_c = 0 , $show_row = 0 )
{
2014-03-23 09:41:31 +01:00
if ( ! $sel_options )
{
$sel_options = array ();
}
// make it globaly availible for show_cell and show_grid, or extensions
$this -> sel_options =& $sel_options ;
if ( ! $readonlys )
{
$readonlys = array ();
}
if ( ++ $this -> already_showed > 1 ) return '' ; // prefens infinit self-inclusion
2008-10-07 11:14:17 +02:00
2014-03-23 09:41:31 +01:00
if ( is_int ( $this -> debug ) && $this -> debug >= 1 || $this -> name && $this -> debug == $this -> name )
{
echo " <p>etemplate.show( $this->name ): $cname = \n " ; _debug_array ( $content );
echo " readonlys= " ; _debug_array ( $readonlys );
}
if ( ! is_array ( $content ))
{
$content = array (); // happens if incl. template has no content
}
// make the content availible as class-public for extensions
$this -> content =& $content ;
$html = " \n \n <!-- BEGIN eTemplate $this->name --> \n <div id= \" " . str_replace ( '"' , '"' , $this -> name ) . " \" > \n \n " ;
if ( ! self :: $styles_included [ $this -> name ])
{
self :: $styles_included [ $this -> name ] = True ;
$html .= html :: style ( $this -> style ) . " \n \n " ;
}
$path = '/' ;
foreach ( $this -> children as $n => $child )
{
$h = $this -> show_cell ( $child , $content , $readonlys , $cname , $show_c , $show_row , $nul , $class , $path . $n );
$html .= $class || $child [ 'align' ] ? html :: div ( $h , html :: formatOptions ( array (
$class ,
$child [ 'align' ],
), 'class,align' )) : $h ;
}
return $html . " \n </div> \n <!-- END eTemplate $this->name --> \n \n " ;
2008-10-07 11:14:17 +02:00
}
2012-06-06 20:47:04 +02:00
/**
2014-03-23 09:41:31 +01:00
* Get the color of a category
*
* For multiple cats , the first with a color is used
*
* @ param int / string $cats multiple comma - separated cat_id ' s
* @ return string
*/
static function cats2color ( $cats )
2012-06-06 20:47:04 +02:00
{
2014-03-23 09:41:31 +01:00
static $cat2color ;
// ACL check
$cats = $GLOBALS [ 'egw' ] -> categories -> check_list ( EGW_ACL_READ , $cats );
if ( ! $cats ) return null ;
if ( isset ( $cat2color [ $cats ]))
{
return $cat2color [ $cats ];
}
foreach ( explode ( ',' , $cats ) as $cat )
{
if ( isset ( $cat2color [ $cat ]))
{
return $cat2color [ $cat ];
}
$data = categories :: id2name ( $cat , 'data' );
if ( is_array ( $data ) && ( $color = $data [ 'color' ]))
{
//echo "<p>cats2color('$cats')=$color</p>\n";
return $cat2color [ $cats ] = $cat2color [ $cat ] = $color ;
}
}
return null ;
2012-06-06 20:47:04 +02:00
}
2008-10-07 11:14:17 +02:00
2014-03-23 09:41:31 +01:00
/**
* creates HTML from an eTemplate
*
* This is done by calling show_cell for each cell in the form . show_cell itself
* calls show recursivly for each included eTemplate .
* You can use it in the UI - layer of an app , just make shure to call process_show !!!
* This is intended as internal function and should NOT be called by new app ' s direct ,
* as it deals with HTML and is so UI - dependent , use exec instead .
*
* @ internal
* @ param array $grid representing a grid
* @ param array $content with content for the cells , keys are the names given in the cells / form elements
* @ param array $readonlys with names of cells / form - elements to be not allowed to change
* This is to facilitate complex ACL ' s which denies access on field - level !!!
* @ param string $cname basename of names for form - elements , means index in $_POST
* eg . $cname = 'cont' , element - name = 'name' returned content in $_POST [ 'cont' ][ 'name' ]
* @ param string $show_c name / index for name expansion
* @ param string $show_row name / index for name expansion
* @ param string $path path in the widget tree
* @ return string the generated HTML
*/
private function show_grid ( & $grid , $content , $readonlys = '' , $cname = '' , $show_c = 0 , $show_row = 0 , $path = '' )
{
if ( ! $readonlys )
{
$readonlys = array ();
}
if ( is_int ( $this -> debug ) && $this -> debug >= 2 || $grid [ 'name' ] && $this -> debug == $grid [ 'name' ] ||
$this -> name && $this -> debug == $this -> name )
{
echo " <p>etemplate.show_grid( $grid[name] ): $cname = \n " ; _debug_array ( $content );
}
if ( ! is_array ( $content ))
{
$content = array (); // happens if incl. template has no content
}
$content += array ( // for var-expansion in names in show_cell
'.c' => $show_c ,
'.col' => $this -> num2chrs ( $show_c - 1 ),
'.row' => $show_row
);
$rows = array ();
$data = & $grid [ 'data' ];
reset ( $data );
if ( isset ( $data [ 0 ]))
{
list (, $opts ) = each ( $data );
}
else
{
$opts = array ();
}
$row_id = $content [ '_row_id' ];
$max_cols = $grid [ 'cols' ];
for ( $r = 0 ; $row = 1 + $r /*list($row,$cols) = each($data)*/ ; ++ $r )
{
if ( ! ( list ( $r_key ) = each ( $data ))) // no further row
{
if ( ! (( $this -> autorepeat_idx ( $cols [ 'A' ], 0 , $r , $idx , $idx_cname , false , $content ) && $idx_cname ) ||
( in_array ( $cols [ 'A' ][ 'type' ], array ( 'vbox' , 'hbox' , 'box' )) && $this -> autorepeat_idx ( $cols [ 'A' ][ 1 ], 0 , $r , $idx , $idx_cname , false , $content ) && $idx_cname ) ||
( $this -> autorepeat_idx ( $cols [ 'B' ], 1 , $r , $idx , $idx_cname , false , $content ) && $idx_cname )) ||
! $this -> isset_array ( $content , $idx_cname ))
{
break ; // no auto-row-repeat
}
}
else
{
$cols = & $data [ $r_key ];
$part = '' ; // '' = body-prefix
list ( $height , $disabled , $part ) = explode ( ',' , $opts [ " h $row " ]);
$class = $opts [ " c $row " ];
}
if ( $disabled != '' && $this -> check_disabled ( $disabled , $content , $r ))
{
continue ; // row is disabled
}
if ( $part ) $row = $part [ 0 ] . $row ; // add part prefix
$rows [ " . $row " ] .= html :: formatOptions ( $height , 'height' );
list ( $cl ) = explode ( ',' , $class );
if ( $cl == '@' || $cl && strpos ( $cl , '$' ) !== false )
{
$cl = $this -> expand_name ( $cl , 0 , $r , $content [ '.c' ], $content [ '.row' ], $content );
if ( ! $cl || preg_match ( '/(^| )([0-9,]+)( |$)/' , $cl , $matches ))
{
if (( $color = $this -> cats2color ( $matches [ 2 ])))
{
$rows [ " . $row " ] .= ' style="background-color: ' . $color . ';"' ;
}
$cl = str_replace ( $matches [ 2 ], 'row' , $cl );
}
}
if ( $cl == 'nmr' || substr ( $cl , 0 , 3 ) == 'row' ) // allow to have further classes behind row
{
$cl = 'row_' . ( $nmr_alternate ++ & 1 ? 'off' : 'on' ) . substr ( $cl , 3 ); // alternate color
}
$cl = isset ( self :: $class_conf [ $cl ]) ? self :: $class_conf [ $cl ] : $cl ;
$rows [ " . $row " ] .= html :: formatOptions ( $cl , 'class' );
$rows [ " . $row " ] .= html :: formatOptions ( $class , ',valign' );
// set row-id, if requested
if ( $row_id && isset ( $content [ $r ][ $row_id ]))
{
$rows [ " . $row " ] .= ' id="' . htmlspecialchars ( $content [ $r ][ $row_id ]) . '"' ;
}
reset ( $cols );
$row_data = array ();
for ( $c = 0 ; True /*list($col,$cell) = each($cols)*/ ; ++ $c )
{
$col = $this -> num2chrs ( $c );
if ( ! ( list ( $c_key ) = each ( $cols ))) // no further cols
{
// only check if the max. column-number reached so far is exeeded
// otherwise the rows have a differen number of cells and it saved a lot checks
if ( $c >= $max_cols )
{
if ( ! $this -> autorepeat_idx ( $cell , $c , $r , $idx , $idx_cname , True , $content ) ||
! $this -> isset_array ( $content , $idx ))
{
break ; // no auto-col-repeat
}
$max_cols = $c + 1 ;
}
}
else
{
$cell = $cols [ $c_key ];
list ( $col_width , $col_disabled ) = explode ( ',' , $opts [ $col ]);
if ( ! $cell [ 'height' ]) // if not set, cell-height = height of row
{
$cell [ 'height' ] = $height ;
}
if ( ! $cell [ 'width' ]) // if not set, cell-width = width of column or table
{
list ( $col_span ) = explode ( ',' , $cell [ 'span' ]);
if ( $col_span == 'all' && ! $c )
{
list ( $cell [ 'width' ]) = explode ( ',' , $this -> size );
}
else
{
$cell [ 'width' ] = $col_width ;
}
}
}
if ( $col_disabled != '' && $this -> check_disabled ( $col_disabled , $content , $r , $c ))
{
continue ; // col is disabled
}
$align_was = $cell [ 'align' ];
$row_data [ $col ] = $this -> show_cell ( $cell , $content , $readonlys , $cname , $c , $r , $span , $cl , $path . '/' . $r_key . $c_key );
if ( $row_data [ $col ] == '' && $this -> rows == 1 )
{
unset ( $row_data [ $col ]); // omit empty/disabled cells if only one row
continue ;
}
if ( strlen ( $cell [ 'onclick' ]) > 1 )
{
$onclick = $cell [ 'onclick' ];
if ( strpos ( $onclick , '$' ) !== false || $onclick [ 0 ] == '@' )
{
$onclick = $this -> expand_name ( $onclick , $c , $r , $content [ '.c' ], $content [ '.row' ], $content );
}
$row_data [ " . $col " ] .= ' onclick="' . $this -> js_pseudo_funcs ( $onclick , $cname ) . '"' . self :: get_id ( '' , $cell [ 'name' ], $cell [ 'id' ]);
}
$colspan = $span == 'all' ? $grid [ 'cols' ] - $c : 0 + $span ;
if ( $colspan > 1 )
{
$row_data [ " . $col " ] .= " colspan= \" $colspan\ " " ;
for ( $i = 1 ; $i < $colspan ; ++ $i , ++ $c )
{
each ( $cols ); // skip next cell(s)
}
}
else
{
list ( $width , $disable ) = explode ( ',' , $opts [ $col ]);
if ( $width ) // width only once for a non colspan cell
{
$row_data [ " . $col " ] .= " width= \" $width\ " " ;
$opts [ $col ] = " 0, $disable " ;
}
}
$row_data [ " . $col " ] .= html :: formatOptions ( $cell [ 'align' ] ? $cell [ 'align' ] : ( $align_was ? $align_was : 'left' ), 'align' );
// allow to set further attributes in the tablecell, beside the class
if ( is_array ( $cl ))
{
foreach ( $cl as $attr => $val )
{
if ( $attr != 'class' && $val )
{
$row_data [ '.' . $col ] .= ' ' . $attr . '="' . $val . '"' ;
}
}
$cl = $cl [ 'class' ];
}
$cl = $this -> expand_name ( isset ( self :: $class_conf [ $cl ]) ? self :: $class_conf [ $cl ] : $cl ,
$c , $r , $show_c , $show_row , $content );
// else the class is set twice, in the table and the table-cell, which is not good for borders
if ( $cl && $cell [ 'type' ] != 'template' && $cell [ 'type' ] != 'grid' )
{
$row_data [ " . $col " ] .= html :: formatOptions ( $cl , 'class' );
}
}
$rows [ $row ] = $row_data ;
}
if ( ! $rows ) return '' ;
list ( $width , $height ,,,,, $overflow ) = $options = explode ( ',' , $grid [ 'size' ]);
if ( $overflow && $height )
{
$options [ 1 ] = '' ; // set height in div only
}
$html = html :: table ( $rows , html :: formatOptions ( $options , 'width,height,border,class,cellspacing,cellpadding' ) .
html :: formatOptions ( $grid [ 'span' ], ',class' ) .
html :: formatOptions ( $grid [ 'name' ] ? self :: form_name ( $cname , $grid [ 'name' ]) : '' , 'id' ));
if ( ! empty ( $overflow ))
{
if ( is_numeric ( $height )) $height .= 'px' ;
if ( is_numeric ( $width )) $width .= 'px' ;
if ( $width == '100%' ) $overflow .= '; overflow-x: hidden' ; // no horizontal scrollbar
$div_style = ' style="' . ( $width ? " width: $width ; " : '' ) . ( $height ? " height: $height ; " : '' ) . " overflow: $overflow ; \" " ;
$html = html :: div ( $html , $div_style );
}
// initialise egw_actions for nextmatch widget, if egwGridView_grid CSS class set
if ( $options [ 3 ] == 'egwGridView_grid' )
{
$html .= nextmatch_widget :: init_egw_actions ( $content [ '_actions' ], $content [ 'action_links' ], $this -> name );
}
return " \n \n <!-- BEGIN grid $grid[name] --> \n $html <!-- END grid $grid[name] --> \n \n " ;
}
/**
* build the name of a form - element from a basename and name
*
* name and basename can contain sub - indices in square bracets , eg . basename = " base[basesub1][basesub2] "
* and name = " name[sub] " gives " base[basesub1][basesub2][name][sub] "
*
* @ param string $cname basename
* @ param string $name name
* @ return string complete form - name
*/
static function form_name ( $cname , $name )
2012-07-24 19:48:55 +02:00
{
2014-03-23 09:41:31 +01:00
if ( is_object ( $name )) return '' ;
$name_parts = explode ( '[' , str_replace ( ']' , '' , $name ));
if ( ! empty ( $cname ))
2012-07-24 19:48:55 +02:00
{
2014-03-23 09:41:31 +01:00
array_unshift ( $name_parts , $cname );
2012-07-24 19:48:55 +02:00
}
2014-03-23 09:41:31 +01:00
$form_name = array_shift ( $name_parts );
if ( count ( $name_parts ))
2012-07-24 19:48:55 +02:00
{
2014-03-23 09:41:31 +01:00
$form_name .= '[' . implode ( '][' , $name_parts ) . ']' ;
2012-07-24 19:48:55 +02:00
}
2014-03-23 09:41:31 +01:00
return $form_name ;
2012-07-24 19:48:55 +02:00
}
2013-11-04 18:23:58 +01:00
2014-03-23 09:41:31 +01:00
/**
* strip the prefix of a form - element from a form_name
* This function removes the prefix of form_name () . It takes a name like base [ basesub1 ][ basesub2 ][ name ][ sub ]
* and gives basesub1 [ basesub2 ][ name ][ sub ]
*
* @ param string form_name
* @ return string name without prefix
*/
static private function template_name ( $form_name )
{
$parts = explode ( '[' , str_replace ( ']' , '' , $form_name ));
array_shift ( $parts ); // remove exec
$name = array_shift ( $parts );
if ( $parts ) $name .= '[' . implode ( '][' , $parts ) . ']' ;
return $name ;
}
static private $class_conf = array ( 'nmh' => 'th' , 'nmr0' => 'row_on' , 'nmr1' => 'row_off' );
/**
* generates HTML for one widget ( input - field / cell )
*
* calls show to generate included eTemplates . Again only an INTERMAL function .
*
* @ internal
* @ param array $cell with data of the cell : name , type , ...
* @ param array $content with content for the cells , keys are the names given in the cells / form elements
* @ param array $readonlys with names of cells / form - elements to be not allowed to change
* This is to facilitate complex ACL ' s which denies access on field - level !!!
* @ param string $cname basename of names for form - elements , means index in $_POST
* eg . $cname = 'cont' , element - name = 'name' returned content in $_POST [ 'cont' ][ 'name' ]
* @ param string $show_c name / index for name expansion
* @ param string $show_row name / index for name expansion
* @ param string & $span on return number of cells to span or 'all' for the rest ( only used for grids )
* @ param string & $class on return the css class of the cell , to be set in the < td > tag
* @ param string $path path in the widget tree
* @ return string the generated HTML
*/
private function show_cell ( & $cell , $content , $readonlys , $cname , $show_c , $show_row , & $span , & $class , $path = '' )
2013-11-04 18:23:58 +01:00
{
2014-03-23 09:41:31 +01:00
if ( $this -> debug && ( is_int ( $this -> debug ) && $this -> debug >= 3 || $this -> debug == $cell [ 'type' ]))
{
echo " <p>etemplate.show_cell( $this->name ,name=' ${ cell['name']}',type='${cell['type'] } ',cname=' $cname ',...,' $path ')</p> \n " ;
}
list ( $span ) = explode ( ',' , $cell [ 'span' ]); // evtl. overriten later for type template
if ( $cell [ 'name' ][ 0 ] == '@' && $cell [ 'type' ] != 'template' )
{
$cell [ 'name' ] = $this -> get_array ( $content , $this -> expand_name ( substr ( $cell [ 'name' ], 1 ),
$show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content ));
}
$name = $this -> expand_name ( $cell [ 'name' ], $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
// allow names like "tabs=one|two|three", which will be equal to just "tabs"
// eg. for tabs to use a name independent of the tabs contained
if ( is_string ( $name ) && strpos ( $name , '=' ) !== false )
{
list ( $name ) = explode ( '=' , $name );
}
$form_name = self :: form_name ( $cname , $name );
2014-04-08 13:59:55 +02:00
$value = $this -> get_array ( $content , $name );
2014-03-23 09:41:31 +01:00
$options = '' ;
if ( $readonly = $cell [ 'readonly' ] && $readonlys [ $name ] !== false || // allow to overwrite readonly settings of a cell
@ $readonlys [ $name ] && ! is_array ( $readonlys [ $name ]) || $readonlys [ '__ALL__' ] && ( ! is_string ( $name ) || $readonlys [ $name ] !== false ) ||
! empty ( $name ) && is_string ( $name ) && ( $p = strrpos ( $name , '[' )) !== false && ( $parent = substr ( $name , 0 , $p )) && $readonlys [ $parent ]) // allow also set parent readonly (instead each child)
{
$options .= ' readonly="readonly"' ;
}
if (( int ) $cell [ 'tabindex' ]) $options .= ' tabindex="' . ( int ) $cell [ 'tabindex' ] . '"' ;
if ( $cell [ 'accesskey' ]) $options .= ' accesskey="' . html :: htmlspecialchars ( $cell [ 'accesskey' ]) . '"' ;
if ( is_string ( $cell [ 'size' ]) && ( strchr ( $cell [ 'size' ], '$' ) || $cell [ 'size' ][ 0 ] == '@' )) // expand cell['size'] for the button-disabled-check now
{
$cell [ 'size' ] = $this -> expand_name ( $cell [ 'size' ], $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
}
if ( $cell [ 'type' ] == 'template' && ! $this -> check_disabled ( $cell [ 'disabled' ], $content ))
{
// template is NOT disabled (eg. in editor)
}
elseif ( $cell [ 'disabled' ] && $readonlys [ $name ] !== false || $readonly && in_array ( $cell [ 'type' ], array ( 'button' , 'buttononly' , 'image' , 'progress' )) && strpos ( $cell [ 'size' ], ',' ) === false )
{
if ( $this -> rows == 1 )
{
return '' ; // if only one row omit cell
}
$cell = $this -> empty_cell ( 'label' , '' , array ( 'span' => $cell [ 'span' ])); // show nothing (keep the css class!)
$name = $value = '' ;
}
$extra_label = True ;
if ( strchr ( $cell [ 'onchange' ], '$' ) || $cell [ 'onchange' ][ 0 ] == '@' )
{
$cell [ 'onchange' ] = $this -> expand_name ( $cell [ 'onchange' ], $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
}
if ( $cell [ 'type' ][ 0 ] == '@' )
{
$cell [ 'type' ] = $this -> expand_name ( $t = $cell [ 'type' ], $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
}
// the while loop allows to build extensions from other extensions
// please note: only the first extension's post_process function is called !!!
list ( $type , $sub_type ) = explode ( '-' , $cell [ 'type' ]);
while (( ! self :: $types [ $cell [ 'type' ]] || ! empty ( $sub_type )) && $this -> haveExtension ( $type , 'pre_process' ))
{
//echo "<p>pre_process($cell[name]/$cell[type])</p>\n";
if ( is_string ( $cell [ 'size' ]) && ( strchr ( $cell [ 'size' ], '$' ) || $cell [ 'size' ][ 0 ] == '@' ))
{
$cell [ 'size' ] = $this -> expand_name ( $cell [ 'size' ], $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
}
if ( strchr ( $cell [ 'onchange' ], '$' ) || $cell [ 'onchange' ][ 0 ] == '@' )
{
$cell [ 'onchange' ] = $this -> expand_name ( $cell [ 'onchange' ], $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
}
if ( ! $ext_type ) $ext_type = $type ;
// if readonlys[__ALL__] is set, also set readonlys[$name] (extensions can mark themselfs as 'noReadonlysALL', eg. tab-widget!)
2014-03-24 10:03:38 +01:00
if ( $readonlys [ '__ALL__' ] && $readonlys [ $name ] !== false && ! $this -> haveExtension ( $type , 'noReadonlysALL' ))
2014-03-23 09:41:31 +01:00
{
$readonlys [ $name ] = true ;
}
$extra_label = $this -> extensionPreProcess ( $type , $form_name , $value , $cell , $readonlys [ $name ]);
$readonly = $cell [ 'readonly' ] !== false && ( $readonly || $cell [ 'readonly' ]); // might be set or unset (===false) by extension
self :: set_array ( $content , $name , $value );
if ( $cell [ 'type' ] == $type . '-' . $sub_type ) break ; // stop if no further type-change
list ( $type , $sub_type ) = explode ( '-' , $cell [ 'type' ]);
}
list (, $class ) = explode ( ',' , $cell [ 'span' ]); // might be set by extension
if ( strchr ( $class , '$' ) || $class [ 0 ] == '@' )
{
$class = $this -> expand_name ( $class , $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
}
if ( $cell [ 'needed' ] && ! in_array ( $cell [ 'type' ], array ( 'button' , 'buttononly' )))
{
$class .= ' inputRequired' ;
}
$cell_options = $cell [ 'size' ];
if ( strchr ( $cell_options , '$' ) || $cell_options [ 0 ] == '@' )
{
$cell_options = $this -> expand_name ( $cell_options , $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
}
$label = $cell [ 'label' ];
if ( strchr ( $label , '$' ) || $label [ 0 ] == '@' )
{
$label = $this -> expand_name ( $label , $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
}
$help = $cell [ 'help' ];
if ( strchr ( $help , '$' ) || $help [ 0 ] == '@' )
{
$no_lang_on_help = true ;
$help = $this -> expand_name ( $help , $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
}
$blur = $cell [ 'blur' ][ 0 ] == '@' ? $this -> get_array ( $content , substr ( $cell [ 'blur' ], 1 )) :
( strlen ( $cell [ 'blur' ]) <= 1 ? $cell [ 'blur' ] : lang ( $cell [ 'blur' ]));
if ( $blur )
{
if (( string ) $value === '' )
{
$value = $blur ;
}
$onFocus .= " if(this.value==' " . addslashes ( html :: htmlspecialchars ( $blur )) . " ') this.value=''; " ;
$onBlur .= " if(this.value=='') this.value=' " . addslashes ( html :: htmlspecialchars ( $blur )) . " '; " ;
}
if ( $help )
{
if (( int ) $cell [ 'no_lang' ] < 2 && ! $no_lang_on_help )
{
if (( $use_tooltip_for_help = $help [ 0 ] == '|' )) $help = substr ( $help , 1 );
$help = lang ( $help );
}
if ( substr ( $help , 0 , 5 ) == 'call:' )
{
$options .= ' onMouseOver="' . html :: htmlspecialchars ( substr ( $help , 5 )) . '"' ;
}
elseif (( $use_tooltip_for_help = $use_tooltip_for_help || strpos ( $help , '<' ) !== false && strip_tags ( $help ) != $help )) // helptext is html => use a tooltip
{
$options .= html :: tooltip ( $help );
}
else // "regular" help-text in the statusline
{
$onFocus .= " self.status=' " . addslashes ( html :: htmlspecialchars ( $help )) . " '; return true; " ;
$onBlur .= " self.status=''; return true; " ;
if ( in_array ( $cell [ 'type' ], array ( 'button' , 'buttononly' , 'file' ))) // for button additionally when mouse over button
{
$options .= " onMouseOver= \" self.status=' " . addslashes ( html :: htmlspecialchars ( $help )) . " '; return true; \" " ;
$options .= " onMouseOut= \" self.status=''; return true; \" " ;
}
}
}
if ( $onBlur )
{
$options .= " onFocus= \" $onFocus\ " onBlur = \ " $onBlur\ " " ;
}
if ( $cell [ 'onchange' ] && ! ( $cell [ 'type' ] == 'button' || $cell [ 'type' ] == 'buttononly' ))
{
$onchange = $cell [ 'onchange' ] == '1' ? 'this.form.submit();' : $this -> js_pseudo_funcs ( $cell [ 'onchange' ], $cname );
// rewriting onchange for checkboxes for IE to an onclick
if ( $cell [ 'type' ] == 'checkbox' && html :: $user_agent == 'msie' )
{
$options .= ' onClick="' . $onchange . '; return true;"' ;
}
else
{
$options .= ' onChange="' . $onchange . '"' ;
}
}
if ( $form_name != '' )
2013-11-04 18:23:58 +01:00
{
2014-03-23 09:41:31 +01:00
$options = self :: get_id ( $form_name , $cell [ 'name' ], $cell [ 'id' ]) . ' ' . $options ;
2013-11-04 18:23:58 +01:00
}
2014-03-23 09:41:31 +01:00
switch ( $type )
2013-11-04 18:23:58 +01:00
{
2014-03-23 09:41:31 +01:00
case 'label' : // size: [b[old]][i[talic]],[link],[activate_links],[label_for],[link_target],[link_popup_size],[link_title]
if ( is_array ( $value )) break ;
if ( $cell_options )
{
list ( $style , $extra_link , $activate_links , $label_for , $extra_link_target , $extra_link_popup , $extra_link_title ) = self :: csv_split ( $cell_options , 7 );
}
else
{
$style = $cell [ 'font_style' ];
$extra_link = $cell [ 'href' ];
$activate_links = $cell [ 'activate_links' ];
$label_for = $cell [ 'for' ];
$extra_link_target = $cell [ 'extra_link_target' ];
$extra_link_popup = $cell [ 'extra_link_popup' ];
$extra_link_title = $cell [ 'extra_link_title' ];
}
$value = strlen ( $value ) > 1 && ! $cell [ 'no_lang' ] ? lang ( $value ) : $value ;
$value = nl2br ( html :: htmlspecialchars ( $value ));
if ( $activate_links ) $value = html :: activate_links ( $value );
if ( $value != '' && $style && strpos ( $style , 'b' ) !== false ) $value = html :: bold ( $value );
if ( $value != '' && $style && strpos ( $style , 'i' ) !== false ) $value = html :: italic ( $value );
// if the label has a name, use it as id in a span, to allow addressing it via javascript
$html .= ( $name ? '<span id="' . ( $cell [ 'id' ] ? $cell [ 'id' ] : $name ) . '">' : '' ) . $value . ( $name ? '</span>' : '' );
if ( $help )
{
$class = array (
'class' => $class ,
);
list ( $class [ 'onmouseover' ], $class [ 'onmouseout' ]) = html :: tooltip ( $help , False , False , true );
}
break ;
case 'html' : // size: [link],[link_target],[link_popup_size],[link_title],[activate_links]
list ( $extra_link , $extra_link_target , $extra_link_popup , $extra_link_title , $activate_links ) = explode ( ',' , $cell_options );
if ( $activate_links ) $value = html :: activate_links ( $value );
$html .= $value ;
break ;
case 'int' : // size: [min],[max],[len],[precision/sprint format],[step]
case 'integer' :
case 'float' :
list ( $min , $max , $cell_options , $pre , $step ) = explode ( ',' , $cell_options );
// a few html5 options
if (( string ) $min !== '' ) $options .= ' min="' . htmlspecialchars ( $min ) . '"' ;
if (( string ) $max !== '' ) $options .= ' max="' . htmlspecialchars ( $max ) . '"' ;
// default step="any" for float, as not setting it limits value to integer as step defaults to 1 in html5!
if ( is_numeric ( $step ) || $type == 'float' )
{
$options .= ' step="' . ( is_numeric ( $step ) ? $step : 'any' ) . '"' ;
}
if ( $cell_options == '' && ! $readonly )
{
$cell_options = $cell [ 'type' ] != 'float' ? 5 : 8 ;
}
// html5 input type=nummeric seems to ignore size, setting a width instead
$options .= ' style="width: ' . ( 3 + abs ( $cell_options )) . 'ex"' ;
if (( $type == 'float' || ! is_numeric ( $pre )) && $value && $pre )
{
$value = is_numeric ( $pre ) ? self :: number_format ( $value , $pre , $readonly ) : sprintf ( $pre , $value );
}
$cell_options .= ',,' . ( $cell [ 'type' ] != 'float' ? '/^-?[0-9]*$/' : '"/^-?[0-9]*[,.]?[0-9]*$/"' ) . ',number' ;
// fall-through
case 'hidden' :
case 'passwd' :
case 'text' : // size: [length][,maxLength[,preg[,html5type]]]
case 'textbox' :
$cell_opts = $c = self :: csv_split ( $cell_options ); // allows to enclose preg in quote to allow comma
// fix preg, in case it contains a comma (html5type is only letters and always last option!)
if ( count ( $cell_opts ) > 3 && ( $cell_opts2 = explode ( ',' , $cell_options )) && $cell_opts2 [ 2 ][ 0 ] != '"' )
{
$html5type = array_pop ( $cell_opts );
$cell_opts = explode ( ',' , $cell_options , 3 );
if ( preg_match ( '/^[a-z]+$/i' , $html5type ))
{
$cell_opts [ 2 ] = substr ( $cell_opts [ 2 ], 0 , - strlen ( $html5type ) - 1 );
if ( $cell_opts [ 2 ][ 0 ] == ',' ) $cell_opts [ 2 ] = '' ; // happens in link-entry under some condition
$cell_opts [] = $html5type ;
}
}
if ( $readonly && ( int ) $cell_opts [ 0 ] >= 0 )
{
$html .= strlen ( $value ) ? html :: bold ( html :: htmlspecialchars ( $value )) : '' ;
}
else
{
if ( $cell_opts [ 0 ] < 0 )
{
$cell_opts [ 0 ] = abs ( $cell_opts [ 0 ]);
$options .= ' readonly="readonly"' ;
}
// only add html5 required attribute, if validation is NOT ignored
if ( $cell [ 'needed' ] && self :: ignore_validation_match ( $form_name , self :: $request -> ignore_validation , $cname ))
{
$required = ' required' ;
}
$html .= html :: input ( $form_name , $value , $type == 'passwd' ? 'password' : ( $type == 'hidden' ? 'hidden' : $cell_opts [ 3 ]),
$options . html :: formatOptions ( $cell_opts , 'SIZE,MAXLENGTH' ) .
$required . ( $type == 'passwd' ? ' autocomplete="off"' : '' ));
if ( ! $readonly )
{
self :: $request -> set_to_process ( $form_name , $cell [ 'type' ], array (
'maxlength' => $cell_opts [ 1 ],
'needed' => $cell [ 'needed' ],
'preg' => $cell_opts [ 2 ],
'min' => $min , // int and float only
'max' => $max ,
));
}
}
unset ( $cell_opts );
break ;
case 'textarea' : // Multiline Text Input, size: [rows][,cols]
if ( $readonly && ! $cell_options )
{
$html .= '<div>' . nl2br ( html :: htmlspecialchars ( $value )) . " </div> \n " ;
}
else
{
// if textarea is readonly, but form_name is already used by an other widget, dont use it
// browser would only send the content of the readonly (and therefore unchanged) field
if ( $readonly && self :: $request -> isset_to_process ( $form_name )) $form_name = '' ;
$html .= html :: textarea ( $form_name , $value ,
$options . html :: formatOptions ( $cell_options , 'ROWS,COLS' ) . ( $cell [ 'needed' ] ? ' required="required"' : '' ));
}
if ( ! $readonly )
{
self :: $request -> set_to_process ( $form_name , $cell [ 'type' ], array (
'needed' => $cell [ 'needed' ],
));
}
break ;
case 'htmlarea' : // Multiline formatted Text Input, size: {simple|extended|advanced},height,width,toolbar-expanded,upload-path
list ( $mode , $height , $width , $toolbar , $baseref , $convertnl ) = explode ( ',' , $cell_options );
if ( $convertnl )
{
$value = nl2br ( html :: htmlspecialchars ( $value ));
}
if ( ! $readonly )
{
$height = $height ? $height : '400px' ;
$width = $width ? $width : '100%' ;
$fckoptions = array (
'toolbar_expanded' => $toolbar ,
);
// html::fckEditor runs everything through html::purify
$html .= html :: fckEditor ( $form_name , $value , $mode , $fckoptions , $height , $width , $baseref );
self :: $request -> set_to_process ( $form_name , $cell [ 'type' ], array (
'needed' => $cell [ 'needed' ],
'mode' => $mode , // need mode to not run purify for $mode=='ascii'
));
}
else
{
$html .= html :: div ( html :: purify ( html :: activate_links ( $value )), 'style="overflow: auto; width=' . $width . '; height=' . $height . '"' );
}
break ;
case 'checkbox' :
$set_val = 1 ; $unset_val = 0 ;
if ( ! empty ( $cell_options ))
{
list ( $set_val , $unset_val , $ro_true , $ro_false ) = self :: csv_split ( $cell_options );
if ( ! $set_val && ! $unset_val ) $set_val = 1 ;
$value = $value == $set_val ;
}
if (( $multiple = substr ( $cell [ 'name' ], - 2 ) == '[]' ) && ! $readonly )
{
$readonly = $readonlys [ substr ( $cell [ 'name' ], 0 , - 1 ) . $set_val . ']' ];
}
if ( $readonly )
{
if ( count ( explode ( ',' , $cell_options )) < 3 )
{
$ro_true = 'x' ;
$ro_false = '' ;
}
if ( ! $value && $ro_false == 'disable' ) return '' ;
$html .= $value ? html :: bold ( $ro_true ) : $ro_false ;
}
else
{
if ( $value ) $options .= ' checked="checked"' ;
if ( $multiple )
{
// add the set_val to the id to make it unique
2014-07-17 14:39:20 +02:00
$options = str_replace ( self :: get_id ( $form_name ),
self :: get_id ( substr ( $form_name , 0 , - 2 ) . " [ $set_val ] " ), $options );
2014-03-23 09:41:31 +01:00
}
$html .= html :: input ( $form_name , $set_val , 'checkbox' , $options );
if ( $multiple ) $form_name = self :: form_name ( $cname , substr ( $cell [ 'name' ], 0 , - 2 ));
if ( ! self :: $request -> isset_to_process ( $form_name ))
{
self :: $request -> set_to_process ( $form_name , $cell [ 'type' ], array (
'unset_value' => $unset_val ,
'multiple' => $multiple ,
'needed' => $cell [ 'needed' ],
));
}
self :: $request -> set_to_process_attribute ( $form_name , 'values' , $set_val , true );
if ( ! $multiple ) unset ( $set_val ); // otherwise it will be added to the label
}
break ;
case 'radio' : // size: value if checked, readonly set, readonly unset
list ( $set_val , $ro_true , $ro_false ) = self :: csv_split ( $cell_options );
$set_val = $this -> expand_name ( $set_val , $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
if ( $value == $set_val )
{
$options .= ' checked="checked"' ;
}
// add the set_val to the id to make it unique
2014-07-17 14:39:20 +02:00
$options = str_replace ( self :: get_id ( $form_name ),
self :: get_id ( $form_name . " [ $set_val ] " ), $options );
2014-03-23 09:41:31 +01:00
if ( $readonly )
{
if ( ! $ro_true && ! $ro_false ) $ro_true = 'x' ;
$html .= $value == $set_val ? html :: bold ( $ro_true ) : $ro_false ;
}
else
{
$html .= html :: input ( $form_name , $set_val , 'RADIO' , $options );
self :: $request -> set_to_process ( $form_name , $cell [ 'type' ], array (
'needed' => $cell [ 'needed' ],
));
}
break ;
case 'button' :
case 'buttononly' :
case 'cancel' : // cancel button
if ( $name == 'cancel' || stripos ( $name , '[cancel]' ) !== false ) $type = 'cancel' ;
list ( $app ) = explode ( '.' , $this -> name );
list ( $img , $ro_img ) = explode ( ',' , $cell_options );
if ( $img [ 0 ] != '/' && strpos ( $img , '/' ) !== false && count ( $img_parts = explode ( '/' , $img )) == 2 )
{
list ( $app , $img ) = $img_parts ; // allow to specify app in image name (eg. img='addressbook/navbar')
}
$title = strlen ( $label ) <= 1 || $cell [ 'no_lang' ] ? $label : lang ( $label );
if ( $cell [ 'onclick' ] &&
( $onclick = $this -> expand_name ( $cell [ 'onclick' ], $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content )))
{
$onclick = $this -> js_pseudo_funcs ( $onclick , $cname );
}
unset ( $cell [ 'onclick' ]); // otherwise the grid will handle it
if (( $cell [ 'onchange' ] != '' || $img && ! $readonly ) && ! $cell [ 'needed' ]) // use a link instead of a button
{
$onclick = ( $onclick ? preg_replace ( '/^return(.*);$/' , 'if (\\1) ' , $onclick ) : '' ) .
((( string ) $cell [ 'onchange' ] === '1' || $img ) ?
'return submitit(' . self :: $name_form . " ,' " . $form_name . " '); " : $cell [ 'onchange' ]) . '; return false;' ;
if ( ! html :: $netscape4 && substr ( $img , - 1 ) == '%' && is_numeric ( $percent = substr ( $img , 0 , - 1 )))
{
$html .= html :: progressbar ( $percent , $title , 'onclick="' . $onclick . '" ' . $options );
}
else
{
$html .= '<a href="" onClick="' . $onclick . '" ' . $options . '>' .
( $img ? html :: image ( $app , $img , $title , 'border="0"' ) : $title ) . '</a>' ;
}
}
else
{
if ( ! empty ( $img ))
{
2014-07-16 14:18:04 +02:00
$options .= ' title="' . html :: htmlspecialchars ( $title ) . '"' ;
2014-03-23 09:41:31 +01:00
}
if ( $cell [ 'onchange' ] && $cell [ 'onchange' ] != 1 )
{
$onclick = ( $onclick ? preg_replace ( '/^return(.*);$/' , 'if (\\1) ' , $onclick ) : '' ) . $cell [ 'onchange' ];
}
if ( $type == 'cancel' ) $options .= ' novalidate="novalidate"' ; // tell html5 form validation NOT to validate
$html .= ! $readonly ? html :: submit_button ( $form_name , $label , $onclick ,
strlen ( $label ) <= 1 || $cell [ 'no_lang' ], $options , $img , $app , $type == 'buttononly' ? 'button' : 'submit' ) :
html :: image ( $app , $ro_img , '' , $options );
}
$extra_label = False ;
if ( ! $readonly && $type != 'buttononly' ) // input button, are never submitted back!
{
self :: $request -> set_to_process ( $form_name , $type );
}
break ;
case 'hrule' :
$html .= html :: hr ( $cell_options );
break ;
case 'grid' :
if ( $readonly && ! $readonlys [ '__ALL__' ])
{
if ( ! is_array ( $readonlys )) $readonlys = array ();
$set_readonlys_all = $readonlys [ '__ALL__' ] = True ;
}
if ( $name != '' )
{
$cname .= $cname == '' ? $name : '[' . str_replace ( '[' , '][' , str_replace ( ']' , '' , $name )) . ']' ;
}
$html .= $this -> show_grid ( $cell , $name ? $value : $content , $readonlys + ( array ) $readonlys [ $name ], $cname , $show_c , $show_row , $path );
if ( $set_readonlys_all ) unset ( $readonlys [ '__ALL__' ]);
break ;
case 'template' : // size: index in content-array (if not full content is past further on)
if ( is_object ( $cell [ 'name' ]))
{
$cell [ 'obj' ] = & $cell [ 'name' ];
unset ( $cell [ 'name' ]);
$cell [ 'name' ] = 'was Object' ;
echo " <p>Object in Name in tpl ' $this->name ': " ; _debug_array ( $grid );
}
$obj_read = 'already loaded' ;
if ( is_array ( $cell [ 'obj' ]))
{
2014-03-23 10:34:18 +01:00
$obj = new etemplate ();
2014-03-23 09:41:31 +01:00
$obj -> init ( $cell [ 'obj' ]);
$cell [ 'obj' ] =& $obj ;
unset ( $obj );
}
if ( ! is_object ( $cell [ 'obj' ]))
{
if ( $cell [ 'name' ][ 0 ] == '@' )
{
$cell [ 'obj' ] = $this -> get_array ( $content , substr ( $cell [ 'name' ], 1 ));
$obj_read = is_object ( $cell [ 'obj' ]) ? 'obj from content' : 'obj read, obj-name from content' ;
if ( ! is_object ( $cell [ 'obj' ]))
{
2014-03-23 10:34:18 +01:00
$cell [ 'obj' ] = new etemplate ( $cell [ 'obj' ], $this -> as_array ());
2014-03-23 09:41:31 +01:00
}
}
else
{ $obj_read = 'obj read' ;
2014-03-23 10:34:18 +01:00
$cell [ 'obj' ] = new etemplate ( $name , $this -> as_array ());
2014-03-23 09:41:31 +01:00
}
}
if ( is_int ( $this -> debug ) && $this -> debug >= 3 || $this -> debug == $cell [ 'type' ])
{
echo " <p>show_cell::template(tpl= $this->name ,name= $cell[name] ): $obj_read , readonly= $readonly </p> \n " ;
}
if ( $this -> autorepeat_idx ( $cell , $show_c , $show_row , $idx , $idx_cname , false , $content ) || $cell_options != '' )
{
if ( $span == '' && isset ( $content [ $idx ][ 'span' ]))
{ // this allows a colspan in autorepeated cells like the editor
list ( $span ) = explode ( ',' , $content [ $idx ][ 'span' ]);
if ( $span == 'all' )
{
$span = 1 + $content [ 'cols' ] - $show_c ;
}
}
$readonlys = $this -> get_array ( $readonlys , $idx );
$content = $this -> get_array ( $content , $idx );
if ( $idx_cname != '' )
{
$cname .= $cname == '' ? $idx_cname : '[' . str_replace ( '[' , '][' , str_replace ( ']' , '' , $idx_cname )) . ']' ;
}
//echo "<p>show_cell-autorepeat($name,$show_c,$show_row,cname='$cname',idx='$idx',idx_cname='$idx_cname',span='$span'): content ="; _debug_array($content);
}
if ( $readonly && ! $readonlys [ '__ALL__' ])
{
if ( ! is_array ( $readonlys )) $readonlys = array ();
$set_readonlys_all = $readonlys [ '__ALL__' ] = True ;
}
// propagate our onclick handler to embeded templates, if they dont have their own
if ( ! isset ( $cell [ 'obj' ] -> onclick_handler )) $cell [ 'obj' ] -> onclick_handler = $this -> onclick_handler ;
if ( $cell [ 'obj' ] -> no_onclick )
{
$cell [ 'obj' ] -> onclick_proxy = $this -> onclick_proxy ? $this -> onclick_proxy : $this -> name . ':' . $this -> version . ':' . $path ;
}
// propagate the CSS class to the template
if ( $class )
{
$grid_size = array_pad ( explode ( ',' , $cell [ 'obj' ] -> size ), 4 , '' );
$grid_size [ 3 ] = ( $grid_size [ 3 ] ? $grid_size [ 3 ] . ' ' : '' ) . $class ;
$cell [ 'obj' ] -> size = implode ( ',' , $grid_size );
}
$html = $cell [ 'obj' ] -> show ( $content , $this -> sel_options , $readonlys , $cname , $show_c , $show_row );
if ( $set_readonlys_all ) unset ( $readonlys [ '__ALL__' ]);
break ;
case 'select' : // size:[linesOnMultiselect|emptyLabel,extraStyleMulitselect, [<varies>,]{5} enhance]
$sels = array ();
list ( $multiple , $extraStyleMultiselect ) = explode ( ',' , $cell_options , 2 );
// Allow widget to specify using enhanced select or not
$c_options = explode ( ',' , $cell_options );
if ( array_key_exists ( 'enhance' , $cell ))
{
$enhance = $cell [ 'enhance' ];
}
else if ( count ( $c_options ) >= 8 )
{
// 8 or more optionsu - #7 is enhance flag
$enhance = ( $c_options [ 7 ] == '1' || $c_options [ 7 ] == 'true' );
}
if ( ! empty ( $multiple ) && 0 + $multiple <= 0 )
{
$sels [ '' ] = $multiple < 0 ? 'all' : $multiple ;
// extra-option: no_lang=0 gets translated later and no_lang=1 gets translated too (now), only no_lang>1 gets not translated
if (( int ) $cell [ 'no_lang' ] == 1 )
{
$sels [ '' ] = substr ( $sels [ '' ], - 3 ) == '...' ? lang ( substr ( $sels [ '' ], 0 , - 3 )) . '...' : lang ( $sels [ '' ]);
}
$multiple = 0 ;
}
$sels += $this -> _sel_options ( $cell , $name , $content );
if ( $multiple && ! is_array ( $value )) $value = explode ( ',' , $value );
if ( $readonly || $cell [ 'noprint' ])
{
foreach ( $multiple || is_array ( $value ) ? $value : array ( $value ) as $val )
{
if ( is_array ( $sels [ $val ]))
{
$option_label = $sels [ $val ][ 'label' ];
$option_title = $sels [ $val ][ 'title' ];
}
else
{
$option_label = ( $sels [ $val ] ? $sels [ $val ] : $val );
$option_title = '' ;
}
if ( ! $cell [ 'no_lang' ]) $option_label = lang ( $option_label );
if ( $html ) $html .= " <br> \n " ;
if ( $option_title )
{
$html .= '<span title="' . html :: htmlspecialchars ( $option_title ) . '">' . html :: htmlspecialchars ( $option_label ) . '</span>' ;
}
else
{
$html .= html :: htmlspecialchars ( $option_label );
}
}
}
if ( ! $readonly )
{
if ( $cell [ 'noprint' ])
{
$html = '<span class="onlyPrint">' . $html . '</span>' ;
$options .= ' class="noPrint"' ;
}
if ( $multiple && is_numeric ( $multiple )) // eg. "3+" would give a regular multiselectbox
{
$html .= html :: checkbox_multiselect ( $form_name . ( $multiple > 1 ? '[]' : '' ), $value , $sels ,
$cell [ 'no_lang' ], $options , $multiple , $multiple [ 0 ] !== '0' ,
$extraStyleMultiselect , $enhance );
}
else
{
$html .= html :: select ( $form_name . ( $multiple > 1 ? '[]' : '' ), $value , $sels ,
$cell [ 'no_lang' ], $options , $multiple , $enhance );
}
if ( ! self :: $request -> isset_to_process ( $form_name ))
{
// fix for optgroup's
$options = array ();
foreach ( $sels as $key => $val )
{
# we want the key anyway, even if this allowes more values than wanted (the name/key of the optgroup if there is one,
# the keys of the arrays in case you have key/value pair(s) as value for the value of your option ).
$options [ $key ] = $key ;
if ( is_array ( $val ))
{
foreach ( array_keys ( $val ) as $key2 )
{
$options [ $key2 ] = $key2 ;
}
}
}
self :: $request -> set_to_process ( $form_name , $cell [ 'type' ], array (
'needed' => $cell [ 'needed' ],
'allowed' => array_keys ( $options ),
'multiple' => $multiple ,
));
}
}
break ;
case 'image' : // size: [link],[link_target],[imagemap],[link_popup],[id]
case 'progress' :
if ( is_string ( $value ) && $value !== '' &&
( $is_progress = substr ( $value , - 1 ) == '%' && is_numeric ( substr ( $value , 0 , - 1 ))) !== ( $type == 'progress' ))
{
error_log ( " Please use correct widget-type ' " . ( $is_progress ? 'progress' : 'image' ) . " ' in eTemplate ' $this->name '! " );
}
$image = $value != '' ? $value : $name ;
if ( is_string ( $image )) list ( $app , $img ) = explode ( '/' , $image , 2 );
if ( ! $app || ! $img || ! is_dir ( EGW_SERVER_ROOT . '/' . $app ) || strpos ( $img , '/' ) !== false )
{
$img = $image ;
list ( $app ) = explode ( '.' , $this -> name );
}
if ( ! $readonly )
{
list ( $extra_link , $extra_link_target , $imagemap , $extra_link_popup , $id ) = self :: csv_split ( $cell [ 'size' ]);
}
$html .= html :: image ( $app , $img , strlen ( $label ) > 1 && ! $cell [ 'no_lang' ] ? lang ( $label ) : $label ,
'border="0"' . ( $imagemap ? ' usemap="#' . html :: htmlspecialchars ( $imagemap ) . '"' : '' ) .
( $id || $value ? self :: get_id ( $name , $cell [ 'name' ], $id ) : '' ));
$extra_label = False ;
break ;
case 'file' : // size: size of the filename field
if ( ! $readonly )
{
if (( int ) $cell_options ) $options .= ' size="' . ( int ) $cell_options . '"' ;
if ( substr ( $name , - 2 ) == '[]' )
{
self :: $form_options .= ' enctype="multipart/form-data"' ;
if ( strpos ( $options , 'onChange="' ) !== false )
{
$options = preg_replace ( '/onChange="([^"]+)"/i' , 'onChange="\\1; if (!this.multiple) add_upload(this);"' , $options );
}
else
{
$options .= ' onChange="if (!this.multiple) add_upload(this);"' ;
}
$options .= ' multiple="multiple"' ; // allow html5 browsers to select more then one file
}
else
{
$html .= html :: input_hidden ( $path_name = str_replace ( $name , $name . '_path' , $form_name ), '.' );
self :: $form_options = " enctype= \" multipart/form-data \" onsubmit= \" set_element2(this,' $path_name ',' $form_name ') \" " ;
}
$html .= html :: input ( $form_name , '' , 'file' , $options );
self :: $request -> set_to_process ( $form_name , $cell [ 'type' ], array (
'needed' => $cell [ 'needed' ],
));
}
break ;
case 'split' :
// Render this et2 widget as a box
$orient = $cell [ 'orientation' ];
case 'vbox' :
case 'hbox' :
case 'groupbox' :
case 'box' : // size: num,orient,cellpadding,cellspacing,keep
$rows = array ();
$box_row = 1 ;
$box_col = 'A' ;
$box_anz = 0 ;
list ( $num , $orient ,,, $keep_empty ) = explode ( ',' , $cell_options );
if ( ! $orient ) $orient = $type == 'hbox' ? 'horizontal' : ( $type == 'box' ? false : 'vertical' );
for ( $n = 1 ; $n <= ( int ) $num ; ++ $n )
{
$child = $cell [ $n ]; // first param is a var_param now!
$h = $this -> show_cell ( $child , $content , $readonlys , $cname , $show_c , $show_row , $nul , $cl , $path . '/' . $n );
if ( $h != '' && $h != ' ' || $keep_empty )
{
if ( $orient != 'horizontal' )
{
$box_row = $n ;
}
else
{
$box_col = $this -> num2chrs ( $n );
}
if ( ! $orient )
{
$html .= $cl ? html :: div ( $h , " class= \" $cl\ " " ) : $h ;
}
else
{
$rows [ $box_row ][ $box_col ] = $html = $h ;
}
$box_anz ++ ;
if ( $cell [ $n ][ 'align' ])
{
$rows [ $box_row ][ '.' . $box_col ] = html :: formatOptions ( $child [ 'align' ], 'align' );
$sub_cell_has_align = true ;
}
if ( strlen ( $child [ 'onclick' ]) > 1 )
{
$rows [ $box_row ][ '.' . $box_col ] .= ' onclick="' . $this -> js_pseudo_funcs ( $child [ 'onclick' ], $cname ) . '"' .
self :: get_id ( '' , $child [ 'name' ], $child [ 'id' ]);
}
// allow to set further attributes in the tablecell, beside the class
if ( is_array ( $cl ))
{
foreach ( $cl as $attr => $val )
{
if ( $attr != 'class' && $val )
{
$rows [ $box_row ][ '.' . $box_col ] .= ' ' . $attr . '="' . $val . '"' ;
}
}
$cl = $cl [ 'class' ];
}
$box_item_class = $this -> expand_name ( isset ( self :: $class_conf [ $cl ]) ? self :: $class_conf [ $cl ] : $cl ,
$show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content );
$rows [ $box_row ][ '.' . $box_col ] .= html :: formatOptions ( $box_item_class , 'class' );
}
}
if ( $box_anz > 1 && $orient ) // a single cell is NOT placed into a table
{
$html = html :: table ( $rows , html :: formatOptions ( $cell_options , ',,cellpadding,cellspacing' ) .
( $type != 'groupbox' ? html :: formatOptions ( $class , 'class' ) .
( $cell [ 'name' ] ? self :: get_id ( $form_name , $cell [ 'name' ], $cell [ 'id' ]) : '' ) : '' ) .
( $cell [ 'align' ] && $orient != 'horizontal' || $sub_cell_has_align ? ' width="100%"' : '' )); // alignment only works if table has full width
if ( $type != 'groupbox' ) $class = '' ; // otherwise we create an extra div
}
// put the class of the box-cell, into the the class of this cell
elseif ( $box_item_class && $box_anz == 1 )
{
$class = ( $class ? $class . ' ' : '' ) . $box_item_class ;
// if we have onclick or tooltip, add it to an extra div around single cell
if ( ! empty ( $rows [ $box_row ][ '.' . $box_col ])) $html = html :: div ( $html , $rows [ $box_row ][ '.' . $box_col ]);
}
if ( $type == 'groupbox' )
{
if ( strlen ( $label ) > 1 && $cell [ 'label' ] == $label )
{
$label = lang ( $label );
}
$html = html :: fieldset ( $html , $label , self :: get_id ( $form_name , $cell [ 'name' ], $cell [ 'id' ]) .
( $class ? ' class="' . $class . '"' : '' ));
$class = '' ; // otherwise we create an extra div
}
elseif ( ! $orient )
{
if ( strpos ( $html , 'class="' . $class )) $class = '' ; // dont add class a 2. time
$html = html :: div ( $html , html :: formatOptions ( array (
$cell [ 'height' ],
$cell [ 'width' ],
$class ,
), 'height,width,class' ) . self :: get_id ( $form_name , $cell [ 'name' ], $cell [ 'id' ])) . ( $html ? '' : '</div>' );
$class = '' ; // otherwise we create an extra div
}
if ( $box_anz > 1 ) // small docu in the html-source
{
$html = " \n \n <!-- BEGIN $cell[type] --> \n \n " . $html . " \n \n <!-- END $cell[type] --> \n \n " ;
}
// we need noPrint on td
if ( strpos ( $cell [ 'span' ], 'noPrint' )) $class .= ' noPrint' ;
$extra_label = False ;
break ;
case 'deck' :
for ( $n = 1 ; $n <= $cell_options && ( empty ( $value ) || $value != $cell [ $n ][ 'name' ]); ++ $n ) ;
if ( $n > $cell_options )
{
$value = $cell [ 1 ][ 'name' ];
}
if ( $s_width = $cell [ 'width' ])
{
$s_width = " width: $s_width " . ( substr ( $s_width , - 1 ) != '%' ? 'px' : '' ) . ';' ;
}
if ( $s_height = $cell [ 'height' ])
{
$s_height = " height: $s_height " . ( substr ( $s_height , - 1 ) != '%' ? 'px' : '' ) . ';' ;
}
$html = html :: input_hidden ( $form_name , $value );
self :: $request -> set_to_process ( $form_name , $cell [ 'type' ]);
for ( $n = 1 ; $n <= $cell_options ; ++ $n )
{
$child = $cell [ $n ]; // first param is a var_param now!
$html .= html :: div ( $this -> show_cell ( $child , $content , $readonlys , $cname , $show_c ,
$show_row , $nul , $cl , $path . '/' . $n ), html :: formatOptions ( array (
'display: ' . ( $value == $child [ 'name' ] ? 'inline' : 'none' ) . ';' ,
$child [ 'name' ]
), 'style,id' ));
}
break ;
case 'colorpicker' :
if ( $readonly )
{
$html = $value ;
}
else
{
$html = html :: inputColor ( $form_name , $value , $cell [ 'help' ]);
self :: $request -> set_to_process ( $form_name , $cell [ 'type' ], array (
'maxlength' => 7 ,
'needed' => $cell [ 'needed' ],
'preg' => '/^(#[0-9a-f]{6}|)$/i' ,
));
}
break ;
default :
if ( $ext_type && $this -> haveExtension ( $ext_type , 'render' ))
{
$html .= $this -> extensionRender ( $ext_type , $form_name , $value , $cell , $readonly );
}
else
{
$html .= " <i>unknown type ' $cell[type] '</i> " ;
}
break ;
}
// extension-processing need to be after all other and only with diff. name
if ( $ext_type && ! $readonly && $this -> haveExtension ( $ext_type , 'post_process' ))
{ // unset it first, if it is already set, to be after the other widgets of the ext.
$to_process = self :: $request -> get_to_process ( $form_name );
self :: $request -> unset_to_process ( $form_name );
self :: $request -> set_to_process ( $form_name , 'ext-' . $ext_type , $to_process );
}
// save blur-value to strip it in process_exec
if ( ! empty ( $blur ) && self :: $request -> isset_to_process ( $form_name ))
{
self :: $request -> set_to_process_attribute ( $form_name , 'blur' , $blur );
}
if ( $extra_label && ( $label != '' || $html == '' ))
{
if ( strlen ( $label ) > 1 && ! ( $cell [ 'no_lang' ] && $cell [ 'label' ] != $label || ( int ) $cell [ 'no_lang' ] == 2 ))
{
$label = lang ( $label );
}
$accesskey = false ;
if (( $accesskey = $label && strpos ( $label , '&' ) !== false ) && $accesskey [ 1 ] != ' ' && $form_name != '' &&
(( $pos = strpos ( $accesskey , ';' )) === false || $pos > 5 ))
{
$label = str_replace ( '&' . $accesskey [ 1 ], '<u>' . $accesskey [ 1 ] . '</u>' , $label );
$accesskey = $accesskey [ 1 ];
}
if ( $label && ! $readonly && ( $accesskey || $label_for || $type != 'label' && $cell [ 'name' ]))
{
if ( $label_for ) // if label_for starts with a '#', it is already an id - no need to create default id from it
{
$label_for = $label_for [ 0 ] == '#' ? substr ( $label_for , 1 ) : self :: form_name ( $cname , $label_for );
}
else
{
$label_for = $form_name . ( $set_val ? " [ $set_val ] " : '' );
}
$label = html :: label ( $label , $label_for , $accesskey );
}
if ( $type == 'radio' || $type == 'checkbox' || $label && strpos ( $label , '%s' ) !== false ) // default for radio is label after the button
{
$html = strpos ( $label , '%s' ) !== false ? str_replace ( '%s' , $html , $label ) : $html . ' ' . $label ;
}
elseif (( $html = $label . ' ' . $html ) == ' ' )
{
$html = ' ' ;
}
}
if ( $extra_link && (( $extra_link = $this -> expand_name ( $extra_link , $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content ))))
{
$options = $help ? ' onmouseover="self.status=\'' . addslashes ( html :: htmlspecialchars ( $help )) . '\'; return true;"' .
' onmouseout="self.status=\'\'; return true;"' : '' ;
if ( $extra_link_target && (( $extra_link_target = $this -> expand_name ( $extra_link_target , $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content ))))
{
$options .= ' target="' . addslashes ( $extra_link_target ) . '"' ;
}
if ( $extra_link_popup && (( $extra_link_popup = $this -> expand_name ( $extra_link_popup , $show_c , $show_row , $content [ '.c' ], $content [ '.row' ], $content ))))
{
list ( $w , $h ) = explode ( 'x' , $extra_link_popup );
2014-12-01 11:45:24 +01:00
$options .= ' onclick="egw(window).openPopup(this,' . ( int ) $w . ',' . ( int ) $h . ',this.target); return false;"' ;
2014-03-23 09:41:31 +01:00
}
if ( $extra_link_title )
{
$options .= ' title="' . addslashes ( $extra_link_title ) . '"' ;
}
return html :: a_href ( $html , $extra_link , '' , $options );
}
// if necessary show validation-error behind field
if ( isset ( self :: $validation_errors [ $form_name ]))
{
2014-07-16 14:18:04 +02:00
$html .= ' <span style="color: red; white-space: nowrap;">' . htmlspecialchars ( self :: $validation_errors [ $form_name ]) . '</span>' ;
2014-03-23 09:41:31 +01:00
}
// generate an extra div, if we have an onclick handler and NO children or it's an extension
//echo "<p>$this->name($this->onclick_handler:$this->no_onclick:$this->onclick_proxy): $cell[type]/$cell[name]</p>\n";
if ( $this -> onclick_handler && ! isset ( self :: $widgets_with_children [ $cell [ 'type' ]]))
{
$handler = str_replace ( '%p' , $this -> no_onclick ? $this -> onclick_proxy : $this -> name . ':' . $this -> version . ':' . $path ,
$this -> onclick_handler );
if ( $type == 'button' || $type == 'buttononly' || ! $label ) // add something to click on
{
$html = ( substr ( $html , - 1 ) == " \n " ? substr ( $html , 0 , - 1 ) : $html ) . ' ' ;
}
return html :: div ( $html , ' ondblclick="' . $handler . '"' , 'clickWidgetToEdit' );
}
return $html ;
}
/**
* Return id = " ... " attribute , using the following order to determine the id :
* - $id if not empty
* - $name if starting with a hash ( #), without the hash of cause
* - $form_name otherwise
*
* This is necessary to not break backward compatibility : if you want to specify
* a certain id , you can use now " #something " as name to get id = " something " ,
* otherwise the $form_name " exec[something] " is used .
* ( If no id is directly supplied internally . )
*
* @ param string $form_name
* @ param string $name = null
* @ param string $id = null
* @ return string ' id="..."' or '' if no id found
*/
static public function get_id ( $form_name , $name = null , $id = null )
{
if ( empty ( $id ))
{
if ( $name [ 0 ] == '#' )
{
$id = substr ( $name , 1 );
}
else
{
$id = $form_name ;
}
}
2014-07-16 14:18:04 +02:00
return ! empty ( $id ) ? ' id="' . htmlspecialchars ( $id ) . '"' : '' ;
2014-03-23 09:41:31 +01:00
}
/**
* Format a number according to user prefs with decimal and thousands separator ( later only for readonly )
*
* HTML5 input type = number requires a float value with a dot , not comma !
* Chrome 22 and Safari 6 shows no value if a comma is used ,
* while FF 16 , IE 9 and 10 have no support for input type = number :- (
* --> use . as decimal separator for browser supporting html5 input type = number
*
* @ param int | float | string $number
* @ param int $num_decimal_places = 2
* @ param boolean $readonly = true
* @ return string
*/
static public function number_format ( $number , $num_decimal_places = 2 , $readonly = true )
{
static $dec_separator , $thousands_separator ;
if ( is_null ( $dec_separator ))
{
$dec_separator = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'number_format' ][ 0 ];
if ( empty ( $dec_separator )) $dec_separator = '.' ;
$thousands_separator = $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'number_format' ][ 1 ];
}
if (( string ) $number === '' ) return '' ;
$ret = number_format ( str_replace ( ' ' , '' , $number ), $num_decimal_places ,
// need to use '.' as decimal separator for all browser supporting html5 input type=number
$dec_sep_used = $readonly || ! in_array ( html :: $user_agent , array ( 'chrome' , 'safari' , 'opera' )) ?
$dec_separator : '.' ,
$readonly ? $thousands_separator : '' );
//error_log(__METHOD__."($number, $num_decimal_places, $readonly) html::user_agent=".html::$user_agent.", dec_sep='$dec_separator' --> '$dec_sep_used', thousands_sep='$thousands_separator' returning '$ret'");
return $ret ;
}
/**
* Retrive options for selectboxes and similar widgets ( eg . the tree )
*
* @ param array $cell
* @ param string $name
* @ param array $content = array ();
* @ return array
*/
function _sel_options ( $cell , $name , $content = array ())
{
$sels = array ();
if ( ! empty ( $cell [ 'sel_options' ]))
{
if ( ! is_array ( $cell [ 'sel_options' ]))
{
$opts = explode ( ',' , $cell [ 'sel_options' ]);
while ( list (, $opt ) = each ( $opts ))
{
list ( $k , $v ) = explode ( '=' , $opt );
$sels [ $k ] = $v ;
}
}
else
{
$sels += $cell [ 'sel_options' ];
}
}
$explode_needed = true ;
if (( $options = $this -> sel_options [ $name ]) && is_array ( $options ) ||
( $options = self :: get_array ( $this -> sel_options , $name )) && is_array ( $options ))
{
if ( count ( $options ) == 2 && $options [ 'label' ] && $options [ 'title' ])
{
// In too far - need to back up
}
else
{
$sels += $options ;
$explode_needed = false ;
}
}
if ( $explode_needed )
{
$name_parts = explode ( '[' , str_replace ( ']' , '' , $name ));
if ( count ( $name_parts ))
{
$org_name = $name_parts [ count ( $name_parts ) - 1 ];
if ( isset ( $this -> sel_options [ $org_name ]) && is_array ( $this -> sel_options [ $org_name ]))
{
$sels += $this -> sel_options [ $org_name ];
}
elseif ( isset ( $this -> sel_options [ $name_parts [ 0 ]]) && is_array ( $this -> sel_options [ $name_parts [ 0 ]]))
{
$sels += $this -> sel_options [ $name_parts [ 0 ]];
}
}
}
if ( isset ( $content [ " options- $name " ]))
{
$sels += $content [ " options- $name " ];
}
//error_log(__METHOD__."(, '$name') returning ".array2string($sels));
return $sels ;
}
/**
* Resolve javascript pseudo functions in onclick or onchange :
* - egw :: link ( '$l' , '$p' ) calls $egw -> link ( $l , $p )
* - form :: name ( 'name' ) returns expanded name / id taking into account the name at that point of the template hierarchy
* - egw :: lang ( 'Message ...' ) translate the message
* - confirm ( 'message' ) translates 'message' and adds a '?' if not present
2014-12-01 11:45:24 +01:00
* - window . open () replaces it with egw ( window ) . openPopup ()
2014-03-23 09:41:31 +01:00
* - xajax_doXMLHTTP ( ' etemplate . replace ajax calls in widgets with special handler not requiring etemplate run rights
*
* @ param string $on onclick , onchange , ... action
* @ param string $cname name - prefix / name - space
* @ return string
*/
function js_pseudo_funcs ( $on , $cname )
{
if ( strpos ( $on , '::' ) !== false ) // avoid the expensive regular expresions, for performance reasons
{
if ( preg_match_all ( " /egw::link \\ ('([^']+)','(.+?)'(?:,'(.+?)')? \\ )/ " , $on , $matches )) // the ? alters the expression to shortest match
{
foreach ( array_keys ( $matches [ 1 ]) as $n ) // this way we can correctly parse ' in the 2. argument
{
$url = $GLOBALS [ 'egw' ] -> link ( $matches [ 1 ][ $n ], $matches [ 2 ][ $n ], $matches [ 3 ][ $n ]);
$on = str_replace ( $matches [ 0 ][ $n ], '\'' . addslashes ( $url ) . '\'' , $on );
}
}
if ( preg_match_all ( " /form::name \\ ('([^']+)' \\ )/ " , $on , $matches ))
{
foreach ( $matches [ 1 ] as $n => $matche_name )
{
$matches [ 1 ][ $n ] = '\'' . self :: form_name ( $cname , $matche_name ) . '\'' ;
}
$on = str_replace ( $matches [ 0 ], $matches [ 1 ], $on );
}
// we need to search ungready (shortest possible match), to avoid catching to much
if ( preg_match_all ( '/egw::lang\(["\']{1}(.*)["\']{1}\)/U' , $on , $matches )) {
foreach ( $matches [ 1 ] as $n => $string ) {
$str = lang ( $string );
$on = str_replace ( $matches [ 0 ][ $n ], '\'' . addslashes ( $str ) . '\'' , $on );
}
}
// inserts the styles of a named template
if ( preg_match ( '/template::styles\(["\']{1}(.*)["\']{1}\)/U' , $on , $matches ))
{
2014-03-23 10:34:18 +01:00
$tpl = $matches [ 1 ] == $this -> name ? $this : new etemplate ( $matches [ 1 ]);
2014-03-23 09:41:31 +01:00
$on = str_replace ( $matches [ 0 ], " '<style> " . str_replace ( array ( " \n " , " \r " ), '' , $tpl -> style ) . " </style>' " , $on );
}
}
// translate messages in confirm()
if ( strpos ( $on , 'confirm(' ) !== false && preg_match ( '/confirm\(["\']{1}(.*)["\']{1}\)/U' , $on , $matches ))
{
$question = lang ( $matches [ 1 ]) . ( substr ( $matches [ 1 ], - 1 ) != '?' ? '?' : '' ); // add ? if not there, saves extra phrase
$on = str_replace ( $matches [ 0 ], 'confirm(\'' . str_replace ( " ' " , " \\ ' " , $question ) . '\')' , $on );
}
2014-12-01 11:45:24 +01:00
// replace window.open() with EGw's egw(window).openPopup()
2014-03-23 09:41:31 +01:00
if ( strpos ( $on , 'window.open(' ) !== false && preg_match ( " /window.open \ ('(.*)','(.*)','dependent=yes,width=([^,]*),height=([^,]*),scrollbars=yes,status=(.*)' \ )/ " , $on , $matches ))
{
2014-12-01 11:45:24 +01:00
$on = str_replace ( $matches [ 0 ], " egw(window).openPopup(' $matches[1] ', $matches[3] , $matches[4] , ' $matches[2] ', false, false, ' $matches[5] ') " , $on );
2014-03-23 09:41:31 +01:00
}
2014-11-27 18:31:03 +01:00
// replace window.close() with EGw's egw.close()
if ( strpos ( $on , 'window.close(' ) !== false )
{
$on = str_replace ( 'window.close(' , 'egw(window).close(' , $on );
}
2014-03-23 09:41:31 +01:00
// replace xajax calls to code in widgets, with the "etemplate" handler,
// this allows to call widgets with the current app, otherwise everyone would need etemplate run rights
if ( strpos ( $on , " xajax_doXMLHTTP('etemplate. " ) !== false )
{
$on = preg_replace ( " /^xajax_doXMLHTTP \ ('etemplate \ .([a-z]+_widget \ .[a-zA-Z0-9_]+) \ '/ " , 'xajax_doXMLHTTP(\'' . $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ] . '.\\1.etemplate\'' , $on );
}
return $on ;
}
/**
* applies stripslashes recursivly on each element of an array
*
* @ param array & $var
* @ return array
*/
static function array_stripslashes ( $var )
{
if ( ! is_array ( $var ))
{
return stripslashes ( $var );
}
foreach ( $var as $key => $val )
{
$var [ $key ] = is_array ( $val ) ? self :: array_stripslashes ( $val ) : stripslashes ( $val );
}
return $var ;
}
/**
* makes necessary adjustments on $_POST after a eTemplate / form gots submitted
*
* This is only an internal function , dont call it direct use only exec
* Process_show uses a list of input - fields / widgets generated by show .
*
* @ internal
* @ param array $content $_POST [ $cname ], on return the adjusted content
* @ param array $to_process list of widgets / form - fields to process
* @ param string $cname = '' basename of our returnt content ( same as in call to show )
* @ param string $_type = 'regular' type of request
* @ return array with validation errors
*/
function process_show ( & $content , $to_process , $cname = '' , $_type = 'regular' )
{
if ( ! isset ( $content ) || ! is_array ( $content ) || ! is_array ( $to_process ))
{
return ;
}
if ( is_int ( $this -> debug ) && $this -> debug >= 1 || $this -> debug == $this -> name && $this -> name )
{
echo " <p>process_show( $this->name ) cname=' $cname ' start: content = " ; _debug_array ( $content );
}
$content_in = $cname ? array ( $cname => $content ) : $content ;
$content = array ();
if ( get_magic_quotes_gpc ())
{
$content_in = self :: array_stripslashes ( $content_in );
}
self :: $validation_errors = array ();
$this -> canceled = $this -> button_pressed = False ;
foreach ( $to_process as $form_name => $type )
{
if ( is_array ( $type ))
{
$attr = $type ;
$type = $attr [ 'type' ];
}
else
{
$attr = array ();
}
$form_name = str_replace ( array ( '[' , ']' ), array ( '[' , ']' ), $form_name );
$value = self :: get_array ( $content_in , $form_name , True , $GLOBALS [ 'egw_info' ][ 'flags' ][ 'currentapp' ] == 'etemplate' ? false : true );
// The comment below does only aplay to normal posts, not for xajax. Files are not supported anyway by xajax atm.
// not checked checboxes are not returned in HTML and file is in $_FILES and not in $content_in
if ( $value === false && $_type == 'xajaxResponse' /*!in_array($type,array('checkbox','file'))*/ ) continue ;
if ( isset ( $attr [ 'blur' ]) && $attr [ 'blur' ] == $value )
{
$value = '' ; // blur-values is equal to emtpy
}
//echo "<p>process_show($this->name) loop was ".self::$loop.", $type: $form_name = ".array2string($value)."</p>\n";
if ( is_string ( $type )) list ( $type , $sub ) = explode ( '-' , $type );
switch ( $type )
{
case 'ext' :
$_cont = & self :: get_array ( $content , $form_name , True );
if ( ! $this -> extensionPostProcess ( $sub , $form_name , $_cont , $value ))
{
//echo "\n<p><b>unsetting content[$form_name] !!!</b></p>\n";
$this -> unset_array ( $content , $form_name );
}
// this else should NOT be unnecessary as $_cont is a reference to the index
// $form_name of $content, but under some circumstances a set/changed $_cont
// does not result in a change in $content -- RalfBecker 2004/09/18
// seems to depend on the number of (not existing) dimensions of the array -- -- RalfBecker 2005/04/06
elseif ( ! self :: isset_array ( $content , $form_name ))
{
//echo "<p>setting content[$form_name]='$_cont' because is was unset !!!</p>\n";
self :: set_array ( $content , $form_name , $_cont );
}
if ( $_cont === '' && $attr [ 'needed' ] && ! $attr [ 'blur' ])
{
self :: set_validation_error ( $form_name , lang ( 'Field must not be empty !!!' ), '' );
}
break ;
case 'htmlarea' :
if ( $attr [ 'mode' ] !== 'ascii' )
{
self :: set_array ( $content , $form_name , html :: purify ( $value ));
break ;
}
// fall-throught for mode 'ascii', which is identical to textarea
case 'int' :
case 'integer' :
case 'float' :
case 'passwd' :
case 'text' :
case 'textbox' :
case 'hidden' :
case 'textarea' :
case 'colorpicker' :
if (( string ) $value === '' && $attr [ 'needed' ] && ! $attr [ 'blur' ])
{
self :: set_validation_error ( $form_name , lang ( 'Field must not be empty !!!' ), '' );
}
if (( int ) $attr [ 'maxlength' ] > 0 && mb_strlen ( $value ) > ( int ) $attr [ 'maxlength' ])
{
$value = mb_substr ( $value , 0 ,( int ) $attr [ 'maxlength' ]);
}
if ( $attr [ 'preg' ] && ! preg_match ( $attr [ 'preg' ], $value ))
{
switch ( $type )
{
case 'int' :
case 'integer' :
self :: set_validation_error ( $form_name , lang ( " '%1' is not a valid integer !!! " , $value ), '' );
break ;
case 'float' :
self :: set_validation_error ( $form_name , lang ( " '%1' is not a valid floatingpoint number !!! " , $value ), '' );
break ;
default :
self :: set_validation_error ( $form_name , lang ( " '%1' has an invalid format !!! " , $value ) /*." !preg_match('$attr[preg]', '$value')"*/ , '' );
break ;
}
}
elseif ( in_array ( $type , array ( 'int' , 'integer' , 'float' ))) // cast int and float and check range
{
if (( string ) $value !== '' || $attr [ 'needed' ]) // empty values are Ok if needed is not set
{
$value = $type != 'float' ? ( int ) $value : ( float ) str_replace ( ',' , '.' , $value ); // allow for german (and maybe other) format
if ( ! empty ( $attr [ 'min' ]) && $value < $attr [ 'min' ])
{
self :: set_validation_error ( $form_name , lang ( " Value has to be at least '%1' !!! " , $attr [ 'min' ]), '' );
$value = $type != 'float' ? ( int ) $attr [ 'min' ] : ( float ) $attr [ 'min' ];
}
if ( ! empty ( $attr [ 'max' ]) && $value > $attr [ 'max' ])
{
self :: set_validation_error ( $form_name , lang ( " Value has to be at maximum '%1' !!! " , $attr [ 'max' ]), '' );
$value = $type != 'float' ? ( int ) $attr [ 'max' ] : ( float ) $attr [ 'max' ];
}
}
}
self :: set_array ( $content , $form_name , $value );
break ;
case 'cancel' : // cancel button ==> dont care for validation errors
if ( $value )
{
$this -> canceled = True ;
self :: set_array ( $content , $form_name , $value );
}
break ;
case 'button' :
if ( $value )
{
$this -> button_pressed = True ;
self :: set_array ( $content , $form_name , $value );
}
break ;
case 'select' :
if ( $attr [ 'allowed' ]) // only check for $value is allowed, if allowed values are set
{
foreach ( is_array ( $value ) ? $value : array ( $value ) as $val )
{
if ( ! ( $attr [ 'multiple' ] && ! $val ) && ! in_array ( $val , $attr [ 'allowed' ]))
{
self :: set_validation_error ( $form_name , lang ( " '%1' is NOT allowed ('%2')! " , $val , implode ( " ',' " , $attr [ 'allowed' ])), '' );
$value = '' ;
break ;
}
}
}
if ( is_array ( $value )) $value = implode ( ',' , $value );
if ( $value === '' && $attr [ 'needed' ])
{
self :: set_validation_error ( $form_name , lang ( 'Field must not be empty !!!' , $value ), '' );
}
self :: set_array ( $content , $form_name , $value );
break ;
case 'checkbox' :
if ( ! $value && $attr [ 'needed' ])
{
self :: set_validation_error ( $form_name , lang ( 'Field must not be empty !!!' , $value ), '' );
}
if ( $value === false )
{
self :: set_array ( $content , $form_name , $attr [ 'multiple' ] ? array () : $attr [ 'unset_value' ]); // need to be reported too
}
else
{
$value = array_intersect ( is_array ( $value ) ? $value : array ( $value ), $attr [ 'values' ]); // return only allowed values
self :: set_array ( $content , $form_name , $attr [ 'multiple' ] ? $value : $value [ 0 ]);
}
break ;
case 'file' :
if (( $multiple = substr ( $form_name , - 2 ) == '[]' ))
{
$form_name = substr ( $form_name , 0 , - 2 );
}
$parts = explode ( '[' , str_replace ( ']' , '' , $form_name ));
$name = array_shift ( $parts );
$index = count ( $parts ) ? '[' . implode ( '][' , $parts ) . ']' : '' ;
$value = array ();
for ( $i = 0 ; $i < 100 ; ++ $i )
{
$file = array ();
foreach ( array ( 'tmp_name' , 'type' , 'size' , 'name' , 'error' ) as $part )
{
if ( ! is_array ( $_FILES [ $name ])) break 2 ; // happens eg. in forms set via xajax, which do not upload files
$file [ $part ] = $this -> get_array ( $_FILES [ $name ], $part . $index . ( $multiple ? " [ $i ] " : '' ));
}
if ( ! $multiple ) $file [ 'path' ] = $this -> get_array ( $content_in , substr ( $form_name , 0 , - 1 ) . '_path]' );
$file [ 'ip' ] = $_SERVER [ 'REMOTE_ADDR' ];
// check if we have an upload error
if ( $file [ 'error' ] && $file [ 'name' ] !== '' && ! $file [ 'size' ]) // ignore empty upload boxes
{
self :: set_validation_error ( $form_name . ( $multiple ? '[]' : '' ),
lang ( 'Error uploading file!' ) . " \n " . self :: max_upload_size_message (), '' );
}
if (( string ) $file [ 'name' ] === '' || $file [ 'tmp_name' ] && function_exists ( 'is_uploaded_file' ) && ! is_uploaded_file ( $file [ 'tmp_name' ]))
{
if ( $multiple && ( $file [ 'name' ] === '' || $file [ 'error' ]))
{
continue ; // ignore empty upload box
}
break ;
}
if ( ! $multiple )
{
$value = $file ;
break ;
}
$value [] = $file ;
}
//echo $form_name; _debug_array($value);
// fall-throught
default :
if ( $attr [ 'needed' ] && ! $value )
{
self :: set_validation_error ( $form_name , lang ( 'Field must not be empty !!!' , $value ), '' );
}
self :: set_array ( $content , $form_name , $value );
break ;
}
}
if ( $cname )
{
$content = $content [ $cname ];
}
if ( is_int ( $this -> debug ) && $this -> debug >= 2 || $this -> debug == $this -> name && $this -> name )
{
echo " <p>process_show( $this->name ) end: content = " ; _debug_array ( $content );
if ( count ( self :: $validation_errors ))
{
echo " <p>validation_errors = " ; _debug_array ( self :: $validation_errors );
}
}
return self :: $validation_errors ;
}
/**
* Sets a validation error , to be displayed in the next exec
*
* @ param string $name ( complete ) name of the widget causing the error
* @ param string | boolean $error error - message already translated or false to reset all existing error for given name
* @ param string $cname = null set it to '' , if the name is already a form - name , defaults to self :: $name_vars
*/
static function set_validation_error ( $name , $error , $cname = null )
{
if ( is_null ( $cname )) $cname = self :: $name_vars ;
//echo "<p>self::set_validation_error('$name','$error','$cname');</p>\n";
if ( $cname ) $name = self :: form_name ( $cname , $name );
if ( $error === false )
{
unset ( self :: $validation_errors [ $name ]);
}
else
{
if ( self :: $validation_errors [ $name ])
{
self :: $validation_errors [ $name ] .= ', ' ;
}
self :: $validation_errors [ $name ] .= $error ;
2013-11-04 18:23:58 +01:00
}
}
}