* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @package etemplate
* @subpackage api
* @version $Id$
*/
use EGroupware\Api;
/**
* creates dialogs / HTML-forms from eTemplate descriptions
*
* Usage example:
*
* $tmpl = new etemplate('app.template.name');
* $tmpl->exec('app.class.callback',$content_to_show);
*
* 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
{
/**
* 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.
*
* @public string
*/
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();
/**
* 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
*/
static public $hooked;
/**
* The following 3 static vars are used to allow eTemplate apps to hook into each other
*
* @var mixed
*/
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
*/
function __construct($name='',$load_via='')
{
// tell framework old eTemplate apps needs eval and inline javascript :(
egw_framework::csp_script_src_attrs(array('unsafe-eval', 'unsafe-inline'));
parent::__construct($name,$load_via);
$this->sitemgr = isset($GLOBALS['Common_BO']) && is_object($GLOBALS['Common_BO']);
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 "
_POST[innerWidth]='$_POST[innerWidth]', innerWidth=$this->innerWidth
\n"; } /** * 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 */ static function location($params='') { egw::redirect_link(is_array($params) ? '/index.php' : $params, is_array($params) ? $params : ''); } /** * 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='') { 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; self::$request = Api\Etemplate\Request::read(); self::$request->output_mode = $output_mode; // let extensions "know" they are run eg. in a popup self::$request->readonlys = $readonlys; self::$request->content = $content; self::$request->changes = $changes; self::$request->sel_options = $sel_options; self::$request->preserv = $preserv; self::$request->method = $method; self::$request->ignore_validation = $ignore_validation; self::$request->name_vars = self::$name_vars; // tell html5 form validation NOT to validate if ($ignore_validation) self::$form_options .= ' novalidate="novalidate"'; if((int) $output_mode == 3) { self::$styles_included[$this->name] = True; return "\n" ."\n\n".html::style($this->style)."\n\n" ."\n".$this->show($content)."\n\n"; } $html = $this->show(self::complete_array_merge($content,$changes),$sel_options,$readonlys,self::$name_vars); 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(), ); 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')) { egw_framework::validate_file('.','app',$app,false); } if (!$this->sitemgr) { $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); $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); egw_framework::validate_file('/etemplate/js/etemplate.js'); //echo ''.__METHOD__."($method,...) etemplate[hooked]=".(int)self::$hooked.", etemplate[hook_app]='".self::$hook_app."', isset(etemplate[content])=".(int)isset(self::$previous_content)."
\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 { if ($GLOBALS['egw_info']['flags']['currentapp'] != 'etemplate') { egw_framework::includeCSS('etemplate', 'app'); } } // saving the etemplate content for other hooked etemplate apps (atm. infolog hooked into addressbook) self::$previous_content =& $html; } //echo ''.__METHOD__."($method,...) after show: sitemgr=$this->sitemgr, hooked=".(int)$hooked.", output_mode=$output_mode
\n"; if (!$this->sitemgr && (int) $output_mode != 1 && (int) $output_mode != -1) // NOT returning html { if (!@self::$hooked) { // 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; if((int) $output_mode != 2) { echo $GLOBALS['egw']->framework->navbar(); } else { echo $GLOBALS['egw']->framework->header(); echo 'uiself::validation_errors('$ignore_validation','$cname') validation_error="; _debug_array(self::$validation_errors); if (!$ignore_validation) return count(self::$validation_errors) > 0; foreach(self::$validation_errors as $name => $error) { if (!self::ignore_validation_match($name, $ignore_validation)) { //echo "
uiself::validation_errors('$ignore_validation','$cname') name='$name' ($error) not ignored!!!
\n"; return true; } //echo "uiself::validation_errors('$ignore_validation','$cname') name='$name' ($error) ignored
\n"; } return false; } /** * Check if given form-name matches ai ignore-validation rule * * @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 */ static function ignore_validation_match($form_name, $ignore_validation, $cname=null) { 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; } /** * 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=