* @copyright 2002-13 by RalfBecker@outdoor-training.de * @version $Id$ */ /** * New eTemplate serverside contains: * - main server methods like read, exec * - * * Not longer available methods: * - set_(row|column)_attributes modifies template on run-time, was only used internally by etemplate itself * - disable_(row|column) dto. * * @ToDo supported customized templates stored in DB, currently we only support xet files stored in filesystem */ class etemplate_new extends etemplate_widget_template { /** * Are we running as sitemgr module or not * * @public boolean */ public $sitemgr=false; /** * Tell egw framework it's ok to call this */ public $public_functions = array( 'process_exec' => true ); /** * 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='') { $this->sitemgr = isset($GLOBALS['Common_BO']) && is_object($GLOBALS['Common_BO']); if ($name) $this->read($name,$template='default',$lang='default',$group=0,$version='',$load_via); // generate new etemplate request object, if not already existing if(!self::$request) { self::$request = etemplate_request::read(); self::$request->content = array(); } } /** * 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 : ''); } /** * Generates 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,array $content,array $sel_options=null,array $readonlys=null,array $preserv=null,$output_mode=0,$ignore_validation='',array $changes=null) { // Include the etemplate2 javascript code egw_framework::validate_file('.', 'etemplate2', 'etemplate'); if (!$this->rel_path) throw new egw_exception_assertion_failed("No (valid) template '$this->name' found!"); self::$request->output_mode = $output_mode; // let extensions "know" they are run eg. in a popup self::$request->content = $content; self::$request->changes = $changes; self::$request->sel_options = $sel_options ? $sel_options : array(); self::$request->readonlys = $readonlys ? $readonlys : array(); self::$request->preserv = $preserv ? $preserv : array(); self::$request->method = $method; self::$request->ignore_validation = $ignore_validation; if (self::$request->output_mode == -1) self::$request->output_mode = 0; self::$request->template = $this->as_array(); if (empty($this->name)) throw new egw_exception_assertion_failed("Template name is not set '$this->name' !"); // instanciate template to fill self::$request->sel_options for select-* widgets // not sure if we want to handle it this way, thought otherwise we will have a few ajax request for each dialog fetching predefined selectboxes $template = etemplate_widget_template::instance($this->name, $this->template_set, $this->version, $this->laod_via); $template->run('beforeSendToClient', array('', array('cont'=>$content))); // some apps (eg. InfoLog) set app_header only in get_rows depending on filter settings self::$request->app_header = $GLOBALS['egw_info']['flags']['app_header']; // compile required translations translations translation::add_app('etemplate'); $currentapp = $GLOBALS['egw_info']['flags']['currentapp']; $langRequire = array('common' => array(), 'etemplate' => array()); // keep that order foreach(translation::$loaded_apps as $l_app => $lang) { if (!in_array($l_app, array($currentapp, 'custom'))) { $langRequire[$l_app] = array('app' => $l_app, 'lang' => $lang); } } foreach(array($currentapp, 'custom') as $l_app) { if (isset(translation::$loaded_apps[$l_app])) { $langRequire[$l_app] = array('app' => $l_app, 'lang' => translation::$loaded_apps[$l_app]); } } $data = array( 'etemplate_exec_id' => self::$request->id(), 'app_header' => self::$request->app_header, 'content' => self::$request->content, 'sel_options' => self::$request->sel_options, 'readonlys' => self::$request->readonlys, 'modifications' => self::$request->modifications, 'validation_errors' => self::$validation_errors, 'langRequire' => array_values($langRequire), 'currentapp' => $currentapp, ); // Info required to load the etemplate client-side $dom_id = str_replace('.','-',$this->name); $load_array = array( 'name' => $this->name, 'url' => $GLOBALS['egw_info']['server']['webserver_url'].$this->rel_path.'?'.filemtime(EGW_SERVER_ROOT.$this->rel_path), 'data' => $data, 'DOMNodeID' => $dom_id, ); if (self::$response) // call is within an ajax event / form submit { //error_log("Ajax " . __LINE__); self::$response->generic('et2_load', $load_array+egw_framework::get_extra()); } else // first call { // missing dependency, thought egw:uses jquery.jquery.tools does NOT work, maybe we should rename it to jquery-tools // egw_framework::validate_file('jquery','jquery.tools.min'); // Include the jQuery-UI CSS - many more complex widgets use it $theme = 'redmond'; egw_framework::includeCSS("/phpgwapi/js/jquery/jquery-ui/$theme/jquery-ui-1.10.3.custom.css"); // Load our CSS after jQuery-UI, so we can override it egw_framework::includeCSS('/etemplate/templates/default/etemplate2.css'); // 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); } $header = $GLOBALS['egw']->framework->header(array( 'etemplate' => $load_array )); // check if we are in an ajax-exec call from jdots template (or future other tabbed templates) if (isset($GLOBALS['egw']->framework->response)) { //error_log("Ajax " . __LINE__); $GLOBALS['egw']->framework->response->generic("data", array('
')); $GLOBALS['egw']->framework->response->generic('et2_load',$load_array); return; } else { //error_log("NON-Ajax " . __LINE__); echo $header; if ($output_mode != 2) { parse_navbar(); } echo ''; } ob_flush(); // Send any accumulated json responses - after flush to avoid sending the buffer as a response if(egw_json_response::isJSONResponse()) { $response = egw_json_response::get(); echo ''; } } } /** * Process via Ajax submitted content */ static public function ajax_process_content($etemplate_exec_id, array $content) { //error_log(__METHOD__."(".array2string($etemplate_exec_id).', '.array2string($content).")"); self::$request = etemplate_request::read($etemplate_exec_id); //error_log('request='.array2string(self::$request)); self::$response = egw_json_response::get(); if (!($template = self::instance(self::$request->template['name'], self::$request->template['template_set'], self::$request->template['version'], self::$request->template['load_via']))) { throw new egw_exception_wrong_parameter('Can NOT read template '.array2string(self::$request->template)); } // Set current app for validation list($app) = explode('.',self::$request->method); if(!$app) list($app) = explode('::',self::$request->method); if($app) $GLOBALS['egw_info']['flags']['currentapp'] = $app; $validated = array(); $expand = array( 'cont' => &self::$request->content, ); $template->run('validate', array('', $expand, $content, &$validated), true); // $respect_disabled=true: do NOT validate disabled widgets and children if (self::validation_errors(self::$request->ignore_validation)) { error_log(__METHOD__."(,".array2string($content).') validation_errors='.array2string(self::$validation_errors)); self::$response->generic('et2_validation_error', self::$validation_errors); exit; } //error_log(__METHOD__."(,".array2string($content).')'); //error_log(' validated='.array2string($validated)); $content = ExecMethod(self::$request->method, self::complete_array_merge(self::$request->preserv, $validated)); if (isset($GLOBALS['egw_info']['flags']['java_script'])) { // Strip out any script tags $GLOBALS['egw_info']['flags']['java_script'] = preg_replace(array('/(