egroupware_official/etemplate/inc/class.etemplate_new.inc.php

355 lines
12 KiB
PHP

<?php
/**
* EGroupware - eTemplate serverside
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-11 by RalfBecker@outdoor-training.de
* @version $Id$
*/
// allow to call direct for tests (see end of class)
if (!isset($GLOBALS['egw_info']))
{
$GLOBALS['egw_info'] = array(
'flags' => array(
'currentapp' => $_REQUEST['sessionid'] ? 'etemplate' : 'login',
'nonavbar' => true,
'debug' => 'etemplate_new',
)
);
include_once '../../header.inc.php';
}
/**
* 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;
/**
* 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);
}
/**
* 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,$content,$sel_options='',$readonlys='',$preserv='',$output_mode=0,$ignore_validation='',$changes='')
{
if (!$this->rel_path) throw new egw_exception_assertion_failed('No (valid) template read!');
// generate new etemplate request object
self::$request = 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->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();
// 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('fillTypeOptions');
$data = array(
'etemplate_exec_id' => self::$request->id(),
'app_header' => $GLOBALS['egw_info']['flags']['app_header'],
'content' => $content,
'sel_options' => self::$request->sel_options,
'readonlys' => $readonlys,
'modifications' => $this->modifications,
'validation_errors' => self::$validation_errors,
);
if (self::$response) // call is within an ajax event / form submit
{
self::$response->generic('et2_load', array(
'url' => $GLOBALS['egw_info']['server']['webserver_url'].$this->rel_path,
'data' => $data,
));
}
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');
egw_framework::validate_file('.','etemplate2','etemplate');
egw_framework::includeCSS('/etemplate/js/test/test.css');
common::egw_header();
if ($output_mode != 2)
{
parse_navbar();
}
echo '
<div id="container"></div>
<script>
var et2 = new etemplate2(document.getElementById("container"), "etemplate_new::ajax_process_content");
et2.load("'.$GLOBALS['egw_info']['server']['webserver_url'].$this->rel_path.'",'.json_encode($data).');
</script>
';
common::egw_footer();
}
}
/**
* 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));
}
$validated = array();
$template->run('validate', array('', $content, &$validated));
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).') validated='.array2string($validated));
return ExecMethod(self::$request->method, self::complete_array_merge(self::$request->preserv, $validated));
}
/**
* Path of template relative to EGW_SERVER_ROOT
*
* @var string
*/
public $rel_path;
public $name;
public $template_set;
public $version;
public $laod_via;
/**
* Reads an eTemplate from filesystem or DB (not yet supported)
*
* @param string $name name of the eTemplate or array with the values for all keys
* @param string $template_set template-set, '' loads the prefered template of the user, 'default' loads the default one '' in the db
* @param string $lang language, '' loads the pref. lang of the user, 'default' loads the default one '' in the db
* @param int $group id of the (primary) group of the user or 0 for none, not used at the moment !!!
* @param string $version version of the eTemplate
* @param mixed $load_via name/array of keys of etemplate to load in order to get $name (only as second try!)
* @return boolean True if a fitting template is found, else False
*
* @ToDo supported customized templates stored in DB
*/
public function read($name,$template_set='default',$lang='default',$group=0,$version='',$load_via='')
{
$this->rel_path = self::relPath($this->name=$name, $this->template_set=$template_set,
$this->version=$version, $this->laod_via = $load_via);
error_log(__METHOD__."('$name', '$template_set', '$lang', $group, '$version', '$load_via') rel_path=".array2string($this->rel_path));
return (boolean)$this->rel_path;
}
/**
* Get template data as array
*
* @return array
*/
public function as_array()
{
return array(
'name' => $this->name,
'template_set' => $this->template_set,
'version' => $this->version,
'load_via' => $this->load_via,
);
}
/**
* Modifications on the instancated template
*
* Get collected here to be send to the server
*/
protected $modifications = array();
/**
* Returns reference to an attribute in a named cell
*
* Currently we always return a reference to an not set value, unless it was set before.
* We do not return a reference to the actual cell, as it get's contructed on client-side!
*
* @param string $name cell-name
* @param string $attr attribute-name
* @return mixed reference to attribute, usually NULL
*/
public function &get_cell_attribute($name,$attr)
{
error_log(__METHOD__."('$name', '$attr')");
return $this->modifications[$name][$attr];
}
/**
* set an attribute in a named cell if val is not NULL else return the attribute
*
* @param string $name cell-name
* @param string $attr attribute-name
* @param mixed $val if not NULL sets attribute else returns it
* @return mixed number of changed cells or False, if none changed
*/
public function &set_cell_attribute($name,$attr,$val)
{
error_log(__METHOD__."('$name', '$attr', ".array2string($val).')');
$attr =& $this->get_cell_attribute($name, $attr);
if (!is_null($val)) $attr = $val;
return $attr;
}
/**
* disables all cells with name == $name
*
* @param sting $name cell-name
* @param boolean $disabled=true disable or enable a cell, default true=disable
* @return mixed number of changed cells or False, if none changed
*/
public function disable_cells($name,$disabled=True)
{
return $this->set_cell_attribute($name,'disabled',$disabled);
}
/**
* merges $old and $new, content of $new has precedence over $old
*
* THIS IS NOT THE SAME AS PHP's functions:
* - array_merge, as it calls itself recursive for values which are arrays.
* - array_merge_recursive accumulates values with the same index and $new does NOT overwrite $old
*
* @param array $old
* @param array $new
* @return array the merged array
*/
public static function complete_array_merge($old,$new)
{
if (is_array($new))
{
if (!is_array($old)) $old = (array) $old;
foreach($new as $k => $v)
{
if (!is_array($v) || !isset($old[$k]) || // no array or a new array
isset($v[0]) && !is_array($v[0]) && isset($v[count($v)-1])) // or no associative array, eg. selecting multiple accounts
{
$old[$k] = $v;
}
else
{
$old[$k] = self::complete_array_merge($old[$k],$v);
}
}
}
return $old;
}
/**
* Debug callback just outputting content
*
* @param array $content=null
*/
public function debug(array $content=null)
{
common::egw_header();
_debug_array($content);
common::egw_footer();
}
}
if ($GLOBALS['egw_info']['flags']['debug'] == 'etemplate_new')
{
$name = isset($_GET['name']) ? $_GET['name'] : 'timesheet.edit';
$template = new etemplate_new();
if (!$template->read($name))
{
header('HTTP-Status: 404 Not Found');
echo "<html><head><title>Not Found</title><body><h1>Not Found</h1><p>The requested eTemplate '$name' was not found!</p></body></html>\n";
exit;
}
$GLOBALS['egw_info']['flags']['app_header'] = $name;
$template->exec('etemplate.etemplate.debug', array(), array(), array(), array(), 2);
}