moving eT2 server-side to api

This commit is contained in:
Ralf Becker 2016-03-19 13:06:07 +00:00
parent d8fe729ffd
commit 2f4c727f9d
45 changed files with 2881 additions and 2470 deletions

727
api/src/Etemplate.php Normal file
View File

@ -0,0 +1,727 @@
<?php
/**
* EGroupware - eTemplate serverside
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api;
// explicitly import old not yet ported classes
use egw;
use egw_framework;
use egw_json_response;
use categories; // css
/**
* 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.
*/
class Etemplate 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='')
{
// we do NOT call parent consturctor, as we only want to enherit it's (static) methods
if (false) parent::__construct ($name); // satisfy IDE, as we dont call parent constructor
$this->sitemgr = isset($GLOBALS['Common_BO']) && is_object($GLOBALS['Common_BO']);
if ($name) $this->read($name,$template='default','default',0,'',$load_via);
// generate new etemplate request object, if not already existing
if(!isset(self::$request)) self::$request = Etemplate\Request::read();
}
/**
* 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
* 4 = json response
* @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)
{
$hook_data = $GLOBALS['egw']->hooks->process(
array('hook_location' => 'etemplate2_before_exec') +
array('location_name' => $this->name) +
array('location_object' => &$this) +
$content
);
foreach($hook_data as $extras)
{
if (!$extras) continue;
foreach(isset($extras[0]) ? $extras : array($extras) as $extra)
{
if ($extra['data'] && is_array($extra['data']))
{
$content = array_merge($content, $extra['data']);
}
if ($extra['preserve'] && is_array($extra['preserve']))
{
$preserv = array_merge($preserv, $extra['preserve']);
}
if ($extra['readonlys'] && is_array($extra['readonlys']))
{
$readonlys = array_merge($readonlys, $extra['readonlys']);
}
}
}
unset($hook_data);
// Include the etemplate2 javascript code
egw_framework::validate_file('.', 'etemplate2', 'etemplate');
if (!$this->rel_path) throw new Exception\AssertionFailed("No (valid) template '$this->name' found!");
if ($output_mode == 4)
{
$output_mode = 0;
self::$response = egw_json_response::get();
}
self::$request->output_mode = $output_mode; // let extensions "know" they are run eg. in a popup
self::$request->content = self::$cont = $content;
self::$request->changes = $changes;
self::$request->sel_options = is_array($sel_options) ? self::fix_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 Exception\AssertionFailed("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 = self::instance($this->name, $this->template_set, $this->version, $this->laod_via);
if (!$template) throw new Exception\AssertionFailed("Template $this->name not instanciable! Maybe you forgot to rename template id.");
Translation::add_app('etemplate');
$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
$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, 'etag' => Translation::etag($l_app, $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], 'etag' => Translation::etag($l_app, 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->dom_id);
$load_array = array(
'name' => $this->name,
'url' => self::rel2url($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());
egw_framework::clear_extra(); // to not send/set it twice for multiple etemplates (eg. CRM view)
}
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);
}
// Category styles
categories::css($app);
// set action attribute for autocomplete form tag
// as firefox complains on about:balnk action, thus we have to literaly submit the form to a blank html
$form_action = "about:blank";
if (in_array(Header\UserAgent::type(), array('firefox', 'safari')))
{
$form_action = $GLOBALS['egw_info']['server']['webserver_url'].'/api/src/Etemplate/empty.html';
}
// check if we are in an ajax-exec call from jdots template (or future other tabbed templates)
if (isset($GLOBALS['egw']->framework->response))
{
$content = '<form target="egw_iframe_autocomplete_helper" action="'.$form_action.'" id="'.$dom_id.'" class="et2_container"></form>'."\n".
'<iframe name="egw_iframe_autocomplete_helper" style="width:0;height:0;position: absolute;visibility:hidden;"></iframe>';
// add server-side page-generation times
if($GLOBALS['egw_info']['user']['preferences']['common']['show_generation_time'])
{
$vars = $GLOBALS['egw']->framework->_get_footer();
$content .= "\n".$vars['page_generation_time'];
}
$GLOBALS['egw']->framework->response->generic("data", array($content));
$GLOBALS['egw']->framework->response->generic('et2_load',$load_array+egw_framework::get_extra());
egw_framework::clear_extra(); // to not send/set it twice for multiple etemplates (eg. CRM view)
self::$request = null;
return;
}
// let framework know, if we are a popup or not ('popup' not true, which is allways used by index.php!)
if (!isset($GLOBALS['egw_info']['flags']['nonavbar']) || is_bool($GLOBALS['egw_info']['flags']['nonavbar']))
{
$GLOBALS['egw_info']['flags']['nonavbar'] = $output_mode == 2 ? 'popup' : false;
}
echo $GLOBALS['egw']->framework->header();
if ($output_mode != 2 && !$GLOBALS['egw_info']['flags']['nonavbar'])
{
parse_navbar();
}
else // mark popups as such, by enclosing everything in div#popupMainDiv
{
echo '<div id="popupMainDiv" class="popupMainDiv">'."\n";
}
// Send any accumulated json responses - after flush to avoid sending the buffer as a response
if(egw_json_response::isJSONResponse())
{
$load_array['response'] = egw_json_response::get()->returnResult();
}
// <iframe> and <form> tags added only to get browser autocomplete handling working again
echo '<form target="egw_iframe_autocomplete_helper" action="'.$form_action.'" id="'.$dom_id.'" class="et2_container" data-etemplate="'.
htmlspecialchars(egw_json_response::json_encode($load_array), ENT_COMPAT, Translation::charset(), true).'"></form>'."\n".
'<iframe name="egw_iframe_autocomplete_helper" style="width:0;height:0;position: absolute;visibility:hidden;"></iframe>';
if ($output_mode == 2)
{
echo "\n</div>\n";
echo $GLOBALS['egw']->framework->footer();
}
ob_flush();
}
self::$request = null;
}
/**
* Fix all sel_options, as Etemplate\Widget\Select::beforeSendToClient is not run for auto-repeated stuff not understood by server
*
* @param array $sel_options
* @return array
*/
static protected function fix_sel_options(array $sel_options)
{
foreach($sel_options as &$options)
{
if (!is_array($options)||empty($options)) continue;
foreach($options as $key => $value)
{
if (is_numeric($key) && (!is_array($value) || !isset($value['value'])))
{
Etemplate\Widget\Select::fix_encoded_options($options, true);
break;
}
}
}
return $sel_options;
}
/**
* Process via Ajax submitted content
*
* @param string $etemplate_exec_id
* @param array $_content
* @param boolean $no_validation
* @throws Exception\WrongParameter
*/
static public function ajax_process_content($etemplate_exec_id, array $_content, $no_validation)
{
//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 Exception\WrongParameter('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)
{
Translation::add_app($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 ($no_validation)
{
self::$validation_errors = array();
}
elseif (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;
}
// tell request call to remove request, if it is not modified eg. by call to exec in callback
self::$request->remove_if_not_modified();
foreach($GLOBALS['egw']->hooks->process(array(
'hook_location' => 'etemplate2_before_process',
'location_name' => $template->id,
) + self::complete_array_merge(self::$request->preserv, $validated)) as $extras)
{
if (!$extras) continue;
foreach(isset($extras[0]) ? $extras : array($extras) as $extra)
{
if ($extra['data'] && is_array($extra['data']))
{
$validated = array_merge($validated, $extra['data']);
}
}
}
//error_log(__METHOD__."(,".array2string($content).')');
//error_log(' validated='.array2string($validated));
$content = ExecMethod(self::$request->method, self::complete_array_merge(self::$request->preserv, $validated));
$tcontent = is_array($content) ? $content :
self::complete_array_merge(self::$request->preserv, $validated);
$hook_data = $GLOBALS['egw']->hooks->process(
array(
'hook_location' => 'etemplate2_after_process',
'location_name' => $template->id
) + $tcontent);
unset($tcontent);
if (is_array($content))
{
foreach($hook_data as $extras)
{
if (!$extras) continue;
foreach(isset($extras[0]) ? $extras : array($extras) as $extra) {
if ($extra['data'] && is_array($extra['data'])) {
$content = array_merge($content, $extra['data']);
}
}
}
}
unset($hook_data);
if (isset($GLOBALS['egw_info']['flags']['java_script']))
{
// Strip out any script tags
$GLOBALS['egw_info']['flags']['java_script'] = preg_replace(array('/(<script[^>]*>)([^<]*)/is','/<\/script>/'),array('$2',''),$GLOBALS['egw_info']['flags']['java_script']);
self::$response->script($GLOBALS['egw_info']['flags']['java_script']);
//error_log($app .' added javascript to $GLOBALS[egw_info][flags][java_script] - use egw_json_response->script() instead.');
}
return $content;
}
/**
* Notify server that eT session/request is no longer needed, because user closed window
*
* @param string $_exec_id
*/
static public function ajax_destroy_session($_exec_id)
{
//error_log(__METHOD__."('$_exec_id')");
if (($request = Etemplate\Request::read($_exec_id)))
{
$request->remove_if_not_modified();
unset($request);
}
}
/**
* Process via POST submitted content
*/
static public function process_exec()
{
if (get_magic_quotes_gpc()) $_POST['value'] = stripslashes($_POST['value']);
$content = json_decode($_POST['value'],true);
//error_log(__METHOD__."(".array2string($content).")");
self::$request = Etemplate\Request::read($_POST['etemplate_exec_id']);
if (!($template = self::instance(self::$request->template['name'], self::$request->template['template_set'],
self::$request->template['version'], self::$request->template['load_via'])))
{
throw new Exception\WrongParameter('Can NOT read template '.array2string(self::$request->template));
}
$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));
exit;
}
//error_log(__METHOD__."(,".array2string($content).')');
//error_log(' validated='.array2string($validated));
return ExecMethod(self::$request->method, self::complete_array_merge(self::$request->preserv, $validated));
}
public $name;
public $template_set;
public $version;
public $laod_via;
/**
*
* @var string If the template needs a div named other than the template name, this is it
*/
protected $dom_id;
/**
* 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 =null default try template-set from user and if not found "default"
* @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=null,$lang='default',$group=0,$version='',$load_via='')
{
// For mobile experience try to load custom mobile templates
if (Header\UserAgent::mobile())
{
$template_set = "mobile";
}
unset($lang); unset($group); // not used, but in old signature
$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));
$this->dom_id = $name;
return (boolean)$this->rel_path;
}
/**
* Set the DOM ID for the etemplate div. If not set, it will be generated from the template name.
*
* @param string $new_id
*/
public function set_dom_id($new_id)
{
$this->dom_id = $new_id;
}
/**
* 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,
);
}
/**
* 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
* @deprecated use getElementAttribute($name, $attr)
*/
public function &get_cell_attribute($name,$attr)
{
return self::getElementAttribute($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 reference to attribute
* @deprecated use setElementAttribute($name, $attr, $val)
*/
public function &set_cell_attribute($name,$attr,$val)
{
return self::setElementAttribute($name, $attr, $val);
}
/**
* disables all cells with name == $name
*
* @param sting $name cell-name
* @param boolean $disabled =true disable or enable a cell, default true=disable
* @return reference to attribute
* @deprecated use disableElement($name, $disabled=true)
*/
public function disable_cells($name,$disabled=True)
{
return self::disableElement($name, $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
is_array($v) && count($v) == 0) // Empty array replacing non-empty
{
$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)
{
$GLOBALS['egw']->framework->render(print_r($content, true));
}
/**
* 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.
*
* @param int &$max_upload=null on return max. upload size in byte
* @return string
*/
static function max_upload_size_message(&$max_upload=null)
{
$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').': '.Vfs::hsize($max_upload).
" (php.ini: upload_max_filesize=$upload_max_filesize, post_max_size=$post_max_size)";
}
/**
* Format a number according to user prefs with decimal and thousands separator (later only for readonly)
*
* @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=null,$thousands_separator=null;
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 '';
return number_format(str_replace(' ','',$number),$num_decimal_places,$dec_separator,$readonly ? $thousands_separator : '');
}
/**
* 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;
}
}
// Try to discover all widgets, as names don't always match tags (eg: listbox is in menupopup)
foreach(scandir($dir=__DIR__ . '/Etemplate/Widget') as $filename)
{
if(substr($filname, -4) == '.php')
{
try
{
include_once($dir.$filename);
}
catch(Exception $e)
{
error_log($e->getMessage());
}
}
}
// Use hook to load custom widgets from other apps
$widgets = $GLOBALS['egw']->hooks->process('etemplate2_register_widgets');
foreach($widgets as $app => $list)
{
if (is_array($list))
{
foreach($list as $class)
{
try
{
class_exists($class); // trigger autoloader
}
catch(Exception $e)
{
error_log($e->getMessage());
}
}
}
}

View File

@ -1,16 +1,23 @@
<?php
/**
* eGroupWare - eTemplate request object storing request-data directly in the form itself
* EGroupware - eTemplate request object storing request-data directly in the form itself
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright (c) 2007-15 by Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright (c) 2007-16 by Ralf Becker <RalfBecker@outdoor-training.de>
* @version $Id$
*/
namespace EGroupware\Api\Etemplate;
// explicitly import old not yet ported classes
use egw_framework;
use egw_json_response;
use egw_json_request;
/**
* Class to represent the persitent information of an eTemplate request
*
@ -31,7 +38,7 @@
*
* The request object should be instancated only via the factory method etemplate_request::read($id=null)
*
* $request = etemplate_request::read();
* $request = Api\Etemplate\Request::read();
*
* // add request data
*
@ -39,7 +46,7 @@
*
* b) open or modify an existing request:
*
* if (!($request = etemplate_request::read($id)))
* if (!($request = Api\Etemplate\Request::read($id)))
* {
* // request not found
* }
@ -80,7 +87,7 @@
* @property array $template
* @property string $app_header
*/
class etemplate_request
class Request
{
/**
* here is the request data stored
@ -135,14 +142,14 @@ class etemplate_request
* the sesison to constantly grow).
*
* @param string $id =null
* @return etemplate_request
* @return Request
*/
public static function read($id=null)
{
if (is_null(self::$request_class))
{
// new default to use egw_cache to store requests
self::$request_class = 'etemplate_request_cache';
self::$request_class = __CLASS__.'\\Cache';
/* old default to use request if mcrypt and gzcompress are available and session if not
self::$request_class = check_load_extension('mcrypt') && function_exists('gzcompress') &&
self::init_crypt() ? __CLASS__ : 'etemplate_request_session';
@ -154,7 +161,7 @@ class etemplate_request
}
else
{
$request = new etemplate_request();
$request = new Request();
if (!is_null($id))
{

View File

@ -3,14 +3,19 @@
* eGroupWare - eTemplate request object storing the data in EGroupware instance cache
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright (c) 2014 by Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright (c) 2014-16 by Ralf Becker <RalfBecker@outdoor-training.de>
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Request;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
/**
* Class to represent the persitent information stored on the server for each eTemplate request
*
@ -20,11 +25,11 @@
*
* To enable the use of this handler, you have to set (in etemplate/inc/class.etemplate_request.inc.php):
*
* etemplate_request::$request_class = 'etemplate_request_cache';
* Api\Etemplate\Request::$request_class = 'etemplate_request_cache';
*
* The request object should be instancated only via the factory method etemplate_request::read($id=null)
* The request object should be instancated only via the factory method Api\Etemplate\Request::read($id=null)
*
* $request = etemplate_request::read();
* $request = Api\Etemplate\Request::read();
*
* // add request data
*
@ -32,7 +37,7 @@
*
* b) open or modify an existing request:
*
* if (!($request = etemplate_request::read($id)))
* if (!($request = Api\Etemplate\Request::read($id)))
* {
* // request not found
* }
@ -48,7 +53,7 @@
* For an example look in link_widget::ajax_search()
*/
class etemplate_request_cache extends etemplate_request
class Cache extends Etemplate\Request
{
/**
* Expiration time of 4 hours
@ -71,6 +76,9 @@ class etemplate_request_cache extends etemplate_request
{
$this->id = $_id ? $_id : self::request_id();
//error_log(__METHOD__."($_id) this->id=$this->id");
// hack to quiten IDE Warning for not calling parent::__construct, which we can not!
if (false) parent::__construct();
}
/**
@ -88,16 +96,16 @@ class etemplate_request_cache extends etemplate_request
* Factory method to get a new request object or the one for an existing request
*
* @param string $id =null
* @return etemplate_request|boolean the object or false if $id is not found
* @return Request|boolean the object or false if $id is not found
*/
static function read($id=null)
{
$request = new etemplate_request_cache($id);
$request = new Cache($id);
if (!is_null($id))
{
//error_log(__METHOD__."() reading $id");
if (!($request->data = egw_cache::getTree($GLOBALS['egw_info']['server']['install_id'].'_etemplate', $id)))
if (!($request->data = Api\Cache::getTree($GLOBALS['egw_info']['server']['install_id'].'_etemplate', $id)))
{
error_log("Error reading etemplate request data for id=$id!");
return false;
@ -132,7 +140,7 @@ class etemplate_request_cache extends etemplate_request
if ($this->remove_if_not_modified && !$this->data_modified && isset($this->data['last_saved']))
{
//error_log(__METHOD__."() destroying $this->id");
egw_cache::unsetTree($GLOBALS['egw_info']['server']['install_id'].'_etemplate', $this->id);
Api\Cache::unsetTree($GLOBALS['egw_info']['server']['install_id'].'_etemplate', $this->id);
}
elseif (($this->data_modified ||
// if half of expiration time is over, save it anyway, to restart expiration time
@ -140,7 +148,7 @@ class etemplate_request_cache extends etemplate_request
{
//error_log(__METHOD__."() saving $this->id".($this->data_modified?'':' data NOT modified, just keeping session alife'));
$this->data['last_saved'] = time();
if (!egw_cache::setTree($GLOBALS['egw_info']['server']['install_id'].'_etemplate', $this->id, $this->data,
if (!Api\Cache::setTree($GLOBALS['egw_info']['server']['install_id'].'_etemplate', $this->id, $this->data,
// use bigger one of our own self::EXPIRATION=4h and session lifetime (session.gc_maxlifetime) as expiration time
max(self::EXPIRATION, ini_get('session.gc_maxlifetime'))))
{

View File

@ -1,16 +1,20 @@
<?php
/**
* eGroupWare - eTemplate request object storing the data in the filesystem
* EGroupWare - eTemplate request object storing the data in the filesystem
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright (c) 2009-14 by Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright (c) 2009-16 by Ralf Becker <RalfBecker@outdoor-training.de>
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Request;
use EGroupware\Api\Etemplate;
/**
* Class to represent the persitent information stored on the server for each eTemplate request
*
@ -19,11 +23,11 @@
*
* To enable the use of this handler, you have to set (in etemplate/inc/class.etemplate_request.inc.php):
*
* etemplate_request::$request_class = 'etemplate_request_files';
* Api\Etemplate\Request::$request_class = 'etemplate_request_files';
*
* The request object should be instancated only via the factory method etemplate_request::read($id=null)
* The request object should be instancated only via the factory method Api\Etemplate\Request::read($id=null)
*
* $request = etemplate_request::read();
* $request = Api\Etemplate\Request::read();
*
* // add request data
*
@ -31,7 +35,7 @@
*
* b) open or modify an existing request:
*
* if (!($request = etemplate_request::read($id)))
* if (!($request = Api\Etemplate\Request::read($id)))
* {
* // request not found
* }
@ -46,7 +50,7 @@
*
* For an example look in link_widget::ajax_search()
*/
class etemplate_request_files extends etemplate_request
class Files extends Etemplate\Request
{
/**
* request id
@ -76,6 +80,9 @@ class etemplate_request_files extends etemplate_request
if (!$id) $id = self::request_id();
$this->id = $id;
// hack to quiten IDE Warning for not calling parent::__construct, which we can not!
if (false) parent::__construct();
}
/**
@ -97,7 +104,7 @@ class etemplate_request_files extends etemplate_request
*/
static function read($id=null)
{
$request = new etemplate_request_files($id);
$request = new Files($id);
if (!is_null($id))
{

View File

@ -1,25 +1,30 @@
<?php
/**
* eGroupWare - eTemplate request object storing the data in the session
* EGroupware - eTemplate request object storing the data in the session
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright (c) 2007-14 by Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright (c) 2007-16 by Ralf Becker <RalfBecker@outdoor-training.de>
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Request;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
/**
* Class to represent the persitent information stored on the server for each eTemplate request
*
* The information is stored in the users session, which causes the session to constantly grow.
* We implement here some garbadge collection to remove old requests.
*
* The request object should be instancated only via the factory method etemplate_request::read($id=null)
* The request object should be instancated only via the factory method Api\Etemplate\Request::read($id=null)
*
* $request = etemplate_request::read();
* $request = Api\Etemplate\Request::read();
*
* // add request data
*
@ -27,7 +32,7 @@
*
* b) open or modify an existing request:
*
* if (!($request = etemplate_request::read($id)))
* if (!($request = Api\Etemplate\Request::read($id)))
* {
* // request not found
* }
@ -42,7 +47,7 @@
*
* For an example look in link_widget::ajax_search()
*/
class etemplate_request_session extends etemplate_request
class Session extends Etemplate\Request
{
/**
* request id
@ -61,6 +66,9 @@ class etemplate_request_session extends etemplate_request
if (!$id) $id = self::request_id();
$this->id = $id;
// hack to quiten IDE Warning for not calling parent::__construct, which we can not!
if (false) parent::__construct();
}
/**
@ -82,11 +90,11 @@ class etemplate_request_session extends etemplate_request
*/
static function read($id=null)
{
$request = new etemplate_request_session($id);
$request = new Session($id);
if (!is_null($id))
{
if (!($data = $GLOBALS['egw']->session->appsession($id,'etemplate')))
if (!($data = Api\Cache::getSession('etemplate', $id)))
{
return false; // request not found
}
@ -122,11 +130,11 @@ class etemplate_request_session extends etemplate_request
if ($this->remove_if_not_modified && !$this->data_modified)
{
//error_log(__METHOD__."() destroying $this->id");
egw_cache::unsetSession('etemplate', $this->id);
Api\Cache::unsetSession('etemplate', $this->id);
}
elseif (!$this->destroyed && $this->data_modified)
{
egw_cache::setSession('etemplate', $this->id, $this->data);
Api\Cache::setSession('etemplate', $this->id, $this->data);
}
if (!$this->garbage_collection_done)
{
@ -144,7 +152,7 @@ class etemplate_request_session extends etemplate_request
protected function _php4_request_garbage_collection()
{
// now we are on php4 sessions and do a bit of garbage collection
$appsessions =& $_SESSION[egw_session::EGW_APPSESSION_VAR]['etemplate'];
$appsessions =& $_SESSION[Api\Session::EGW_APPSESSION_VAR]['etemplate'];
$session_used =& $appsessions['session_used'];
if ($this->id)

View File

@ -0,0 +1,919 @@
<?php
/**
* EGroupware - eTemplate widget baseclass
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate;
use EGroupware\Api;
use XMLReader;
use ReflectionMethod;
// explicitly import old not yet ported classes
use egw_json_response;
/**
* eTemplate widget baseclass
*/
class Widget
{
/**
* Widget type
*
* @var string
*/
public $type;
/**
* Widget id
*
* @var string
*/
public $id;
/**
* Widget attributes
*
* @var array
*/
public $attrs = array();
/**
* Children
*
* @var array
*/
protected $children = array();
/**
* (Array of) comma-separated list of legacy options to automatically replace when parsing with set_attrs
*
* @var string|array
*/
protected $legacy_options;
/**
* Request object of the currently created request
*
* It's a static variable as etemplates can contain further etemplates (rendered by a different object)
*
* @var etemplate_request
*/
static protected $request;
/**
* JSON response object, if we run via a JSON request
*
* @var egw_json_response
*/
static protected $response;
/**
* Namespaced content array, used when trying to initialize
*
* This is pretty much a global static variable, used when reading
* a template with the content set. This allows variable expansion
* in the constructor.
*
* @protected $cont
*/
static protected $cont = null;
/**
* Constructor
*
* @param string|XMLReader $xml string with xml or XMLReader positioned on the element to construct
* @throws Api\Exception\WrongParameter
*/
public function __construct($xml)
{
$reader = self::get_reader($xml);
$this->type = $reader->name;
$depth = $reader->depth;
$this->id = $reader->getAttribute('id');
// Update content?
if(self::$cont == null)
self::$cont = is_array(self::$request->content) ? self::$request->content : array();
if($this->id && is_array(self::$cont[$this->id]))
{
$old_cont = self::$cont;
self::$cont = self::$cont[$this->id];
}
// read all attributes
$this->set_attrs($reader);
while($reader->read() && $reader->depth > $depth)
{
if ($reader->nodeType == XMLReader::ELEMENT && $reader->depth > $depth)
{
$this->children[] = self::factory($reader->name, $reader, $reader->getAttribute('id'));
}
}
// Reset content as we leave
if($old_cont) {
self::$cont = $old_cont;
}
}
/**
* Get XMLReader for given xml string
*
* @param string|XMLReader $xml string with xml or XMLReader positioned on an element
* @throws Api\Exception\WrongParameter
*/
protected static function get_reader($xml)
{
if (is_a($xml, 'XMLReader'))
{
$reader = $xml;
}
else
{
$reader = new XMLReader();
if (!$reader->XML($xml))
{
throw new Api\Exception\WrongParameter("Can't parse xml:\n$xml");
}
}
return $reader;
}
/**
* Parse and set extra attributes from xml in template object
*
* Returns a cloned template object, if any attribute needs to be set.
* This is necessary as templates can be used multiple time, so we can not alter the cached template!
*
* @param string|XMLReader $xml
* @param boolean $cloned =true true: object does NOT need to be cloned, false: to set attribute, set them in cloned object
* @return Template current object or clone, if any attribute was set
*/
public function set_attrs($xml, $cloned=true)
{
$reader = self::get_reader($xml);
// check if we have to split legacy options (can be by type)
$legacy_options = $this->legacy_options;
if (is_array($legacy_options))
{
if (!($type = $reader->getAttribute('type')))
{
$type = $this->type;
}
$legacy_options = $legacy_options[$type];
}
// read and set all attributes
$template = $this;
while($reader->moveToNextAttribute())
{
if ($reader->name != 'id' && $template->attr[$reader->name] != $reader->value)
{
if (!$cloned)
{
$template = clone($this);
$cloned = true; // only clone it once, otherwise we loose attributes!
}
// $reader->value is an object and therefore assigned by reference
// this is important to not loose content when validating dynamic generated tabs as in settings!
$template->attrs[$reader->name] = $value = $reader->value;
// expand attributes values, otherwise eg. validation can not use attrs referencing to content
if ($value[0] == '@' || strpos($value, '$cont') !== false)
{
$value = self::expand_name($value, null, null, null, null,
isset(self::$cont) ? self::$cont : self::$request->content);
}
// split legacy options
if ($legacy_options && $reader->name == 'options')
{
$legacy_options = explode(',', $legacy_options);
foreach(self::csv_split($value, count($legacy_options)) as $n => $val)
{
if ($legacy_options[$n] && (string)$val !== '') $template->attrs[$legacy_options[$n]] = $val;
}
}
}
}
// Add in anything in the modification array
if(is_array(self::$request->modifications[$this->id]))
{
$this->attrs = array_merge($this->attrs,self::$request->modifications[$this->id]);
}
return $template;
}
/**
* Split a $delimiter-separated options string, which can contain parts with delimiters enclosed in $enclosure
*
* Examples:
* - csv_split('"1,2,3",2,3') === array('1,2,3','2','3')
* - csv_split('1,2,3',2) === array('1','2,3')
* - csv_split('"1,2,3",2,3',2) === array('1,2,3','2,3')
* - csv_split('"a""b,c",d') === array('a"b,c','d') // to escape enclosures double them!
*
* @param string $str
* @param int $num =null in how many parts to split maximal, parts over this number end up (unseparated) in the last part
* @param string $delimiter =','
* @param string $enclosure ='"'
* @return array
*/
public static function csv_split($str,$num=null,$delimiter=',',$enclosure='"')
{
if (strpos($str,$enclosure) === false)
{
return is_null($num) ? explode($delimiter,$str) : explode($delimiter,$str,$num); // no need to run this more expensive code
}
$parts = explode($delimiter,$str);
for($n = 0; isset($parts[$n]); ++$n)
{
$part =& $parts[$n];
if ($part[0] === $enclosure)
{
while (isset($parts[$n+1]) && substr($part,-1) !== $enclosure)
{
$part .= $delimiter.$parts[++$n];
unset($parts[$n]);
}
$part = substr(str_replace($enclosure.$enclosure,$enclosure,$part),1,-1);
}
}
$parts_renum = array_values($parts); // renumber the parts (in case we had to concat them)
if ($num > 0 && count($parts_renum) > $num)
{
$parts_renum[$num-1] = implode($delimiter,array_slice($parts_renum,$num-1,count($parts_renum)-$num+1));
$parts_renum = array_slice($parts_renum,0,$num);
}
return $parts_renum;
}
/**
* Registry of classes implementing widgets
*
* @var array
*/
static protected $widget_registry = array();
/**
* Register a given class for certain widgets
*
* Registration is only needed if widget (base-)name is not autoloadable,
* eg. class Etemplate\Widget\Template does NOT need to be registered.
*
* @param string $class
* @param string|array $widgets
*/
public static function registerWidget($class, $widgets)
{
if (!is_subclass_of($class, __CLASS__))
{
throw new Api\Exception\WrongParameter(__METHOD__."('$class', ".array2string($widgets).") $class is no subclass of ".__CLASS__.'!');
}
foreach((array)$widgets as $widget)
{
self::$widget_registry[$widget] = $class;
}
}
/**
* Factory method to construct all widgets
*
* @param string $type
* @param string|XMLReader $xml
* @param string $id =null
*/
public static function factory($type, $xml, $id=null)
{
$class_name =& self::$widget_registry[$type];
if (!isset($class_name))
{
list($basetype) = explode('-',$type);
if (//dont think this is used: !class_exists($class_name = 'etemplate_widget_'.str_replace('-','_',$type)) &&
!class_exists($class_name = __CLASS__.'\\'.ucfirst($basetype)) &&
// widgets supplied by application in class ${app}_widget_etemplate or ${app}_${subtype}_widget_etemplate
!(isset($GLOBALS['egw_info']['apps'][$basetype]) &&
(class_exists($class_name = str_replace('-','_',$type).'_etemplate_widget') ||
class_exists($class_name = $basetype.'_etemplate_widget'))))
{
// Try for base type, it's probably better than the root
if(self::$widget_registry[$basetype] && self::$widget_registry[$basetype] != $class_name)
{
$class_name = self::$widget_registry[$basetype];
}
// Look for old widgets that were adapted but not renamed
else if (class_exists($class_name = $basetype.'_widget') && in_array(__CLASS__, class_parents($class_name)))
{
// Side-effects set $class_name
//error_log("Ported old widget: $class_name");
}
else
{
// Fall back to widget class, we can not ignore it, as the widget may contain other widgets
$class_name = __CLASS__;
}
}
}
if(!$xml)
{
if (empty($type)) $type = 'widget';
$xml = "<$type id='$id'/>";
}
//error_log(__METHOD__."('$type', ..., '$id') using $class_name");
// currently only overlays can contain templates, other widgets can only reference to templates via id
if ($type == 'template' && $id && ($template = Widget\Template::instance($id)))
{
// references can set different attributes like: class, span, content (namespace)
return $template->set_attrs($xml, false); // false = need to clone template, if attributs are set!
}
return new $class_name($xml);
}
/**
* Iterate over children to find the one with the given id and optional type
*
* @param string $id
* @param string $type =null
* @return Widget|NULL
*/
public function getElementById($id, $type=null)
{
foreach($this->children as $child)
{
if ($child->id === $id && (is_null($type) || $child->type === $type))
{
return $child;
}
if (($element = $child->getElementById($id, $type)))
{
return $element;
}
}
return null;
}
/**
* Iterate over children to find the one with the given type
*
* @param string $type
* @return array of Widget or empty array
*/
public function getElementsByType($type)
{
$elements = array();
foreach($this->children as $child)
{
if ($child->type === $type)
{
$elements[] = $child;
}
$elements += $child->getElementsByType($type);
}
return $elements;
}
/**
* Run a given method on all children
*
* Default implementation only calls method on itself and run on all children
*
* @param string $method_name
* @param array $params =array('') parameter(s) first parameter has to be the cname, second $expand!
* @param boolean $respect_disabled =false false (default): ignore disabled, true: method is NOT run for disabled widgets AND their children
*/
public function run($method_name, $params=array(''), $respect_disabled=false)
{
// maintain $expand array name-expansion
$cname = $params[0];
$expand =& $params[1];
if ($expand['cname'] && $expand['cname'] !== $cname)
{
$expand['cont'] =& self::get_array(self::$request->content, $cname);
$expand['cname'] = $cname;
}
if ($respect_disabled && ($disabled = $this->attrs['disabled'] && self::check_disabled($this->attrs['disabled'], $expand)))
{
//error_log(__METHOD__."('$method_name', ".array2string($params).', '.array2string($respect_disabled).") $this disabled='{$this->attrs['disabled']}'=".array2string($disabled).": NOT running");
return;
}
if (method_exists($this, $method_name))
{
// Some parameter checking to avoid fatal errors
$call = true;
$method = new ReflectionMethod($this, $method_name);
foreach($method->getParameters() as $index => $param)
{
if(!$param->isOptional() && !array_key_exists($index,$params))
{
error_log("Missing required parameter {$param->getPosition()}: {$param->getName()}");
$call = false;
}
if($param->isArray() && !is_array($params[$index]))
{
error_log("$method_name expects an array for {$param->getPosition()}: {$param->getName()}");
$params[$index] = (array)$params[$index];
}
}
if($call) call_user_func_array(array($this, $method_name), $params);
}
foreach($this->children as $child)
{
// If type has something that can be expanded, we need to expand it so the correct method is run
$this->expand_widget($child, $expand);
$child->run($method_name, $params, $respect_disabled);
}
}
/**
* If a widget's type is expandable, we need to expand it to make sure we have
* the right class before running the method on it
*
* @param Widget $child Widget to check & expand if needed
* @param array& $expand Expansion array
*/
protected function expand_widget(Widget $child, array &$expand)
{
if(strpos($child->attrs['type'], '@') !== false || strpos($child->attrs['type'], '$') !== false)
{
$type = self::expand_name($child->attrs['type'],$expand['c'], $expand['row'], $expand['c_'], $expand['row_'], $expand['cont']);
$id = self::expand_name($child->id,$expand['c'], $expand['row'], $expand['c_'], $expand['row_'], $expand['cont']);
$attrs = $child->attrs;
unset($attrs['type']);
$expanded_child = self::factory($type, false,$id);
$expanded_child->id = $id;
$expanded_child->type = $type;
$expanded_child->attrs = $attrs + array('type' => $type);
$child = $expanded_child;
}
}
/**
* Checks if a grid row or column is disabled
*
* Expression: [!][@]val[=[@]check]
* Parts in square brackets are optional, a ! negates the expression, @val evaluates to $content['val']
* if no =check is given all set non-empty and non-zero strings are true (standard php behavior)
*
* @param string $disabled expression to check, eg. "!@var" for !$content['var']
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @return boolean true if the row/col is disabled or false if not
*/
protected static function check_disabled($disabled, array $expand)
{
if (($not = $disabled[0] == '!'))
{
$disabled = substr($disabled,1);
}
list($value,$check) = $vals = explode('=',$disabled);
// use expand_name to be able to use @ or $
$val = self::expand_name($value, $expand['c'], $expand['row'], $expand['c_'], $expand['row_'], $expand['cont']);
$check_val = self::expand_name($check, $expand['c'], $expand['row'], $expand['c_'], $expand['row_'], $expand['cont']);
$result = count($vals) == 1 ? $val != '' : ($check_val[0] == '/' ? preg_match($check_val,$val) : $val == $check_val);
if ($not) $result = !$result;
//error_log(__METHOD__."('".($not?'!':'')."$disabled' = '$val' ".(count($vals) == 1 ? '' : ($not?'!':'=')."= '$check_val'")." = ".($result?'True':'False'));
return $result;
}
/**
* Regular expression matching a PHP variable in a string, eg.
*
* "replies[$row][reply_message]" should only match $row
* "delete[$row_cont[path]]" should match $row_cont[path]
*/
const PHP_VAR_PREG = '\$[A-Za-z0-9_]+(\[[A-Za-z0-9_]+\])*';
/**
* allows a few variables (eg. row-number) to be used in field-names
*
* This is mainly used for autorepeat, but other use is possible.
* You need to be aware of the rules PHP uses to expand vars in strings, a name
* of "Row$row[length]" will expand to 'Row' as $row is scalar, you need to use
* "Row${row}[length]" instead. Only one indirection is allowd in a string by php !!!
* Out of that reason we have now the variable $row_cont, which is $cont[$row] too.
* Attention !!!
* Using only number as index in field-names causes a lot trouble, as depending
* on the variable type (which php determines itself) you used filling and later
* accessing the array it can by the index or the key of an array element.
* To make it short and clear, use "Row$row" or "$col$row" not "$row" or "$row$col" !!!
*
* @param string $name the name to expand
* @param int $c is the column index starting with 0 (if you have row-headers, data-cells start at 1)
* @param int $row is the row number starting with 0 (if you have col-headers, data-cells start at 1)
* @param int $c_ is the value of the previous template-inclusion,
* eg. the column-headers in the eTemplate-editor are templates itself,
* to show the column-name in the header you can not use $col as it will
* be constant as it is always the same col in the header-template,
* what you want is the value of the previous template-inclusion.
* @param int $row_ is the value of the previous template-inclusion,
* @param array $cont content of the template, you might use it to generate button-names with id values in it:
* "del[$cont[id]]" expands to "del[123]" if $cont = array('id' => 123)
* @return string the expanded name
*/
protected static function expand_name($name,$c,$row,$c_='',$row_='',$cont='')
{
$is_index_in_content = $name[0] == '@';
if (($pos_var=strpos($name,'$')) !== false)
{
if (!$cont)
{
$cont = array();
}
if (!is_numeric($c)) $c = self::chrs2num($c);
$col = self::num2chrs($c-1); // $c-1 to get: 0:'@', 1:'A', ...
$col_ = self::num2chrs($c_-1);
$row_cont = $cont[$row];
$col_row_cont = $cont[$col.$row];
eval('$name = "'.str_replace('"','\\"',$name).'";');
unset($col_, $row_, $row_cont, $col_row_cont); // quiten IDE warning about used vars, they might be used in above eval!
}
if ($is_index_in_content)
{
if ($name[1] == '@' && is_array(self::$request->content))
{
$name = self::get_array(self::$request->content,substr($name,2));
}
elseif(is_array($cont))
{
$name = self::get_array($cont,substr($name,1));
}
else
{
// Content not set expands to ''
$name = '';
}
}
return $name;
}
/**
* generates column-names from index: 'A', 'B', ..., 'AA', 'AB', ..., 'ZZ' (not more!)
*
* @param string $chrs column letter to generate name from 'A' => 1
* @return int the index
*/
static function chrs2num($chrs)
{
$min = ord('A');
$max = ord('Z') - $min + 1;
$num = 1+ord($chrs{0})-$min;
if (strlen($chrs) > 1)
{
$num *= 1 + $max - $min;
$num += 1+ord($chrs{1})-$min;
}
return $num;
}
/**
* generates column-names from index: 'A', 'B', ..., 'AA', 'AB', ..., 'ZZ' (not more!)
*
* @param int $num numerical index to generate name from 1 => 'A'
* @return string the name
*/
static function num2chrs($num)
{
$min = ord('A');
$max = ord('Z') - $min + 1;
if ($num >= $max)
{
$chrs = chr(($num / $max) + $min - 1);
}
$chrs .= chr(($num % $max) + $min);
return $chrs;
}
/**
* Convert object to string
*
* @return string
*/
public function __toString()
{
return '['.get_class($this).'] ' .
$this->type.($this->attrs['type'] && $this->attrs['type'] != $this->type ? '('.$this->attrs['type'].')' : '').'#'.$this->id;
}
/**
* When cloning a widget, we also clone children
*/
public function __clone()
{
foreach($this->children as $child_num => $child) {
$this->children[$child_num] = clone $child;
}
}
/**
* Convert widget (incl. children) to xml
*
* @param string $indent =''
* @return string
*/
public function toXml($indent='')
{
echo "$indent<$this->type";
if ($this->id) echo ' id="'.htmlspecialchars($this->id).'"';
foreach($this->attrs as $name => $value)
{
if ($name == 'options' && $this->legacy_options && (!is_array($this->legacy_options) ||
isset($this->legacy_options[$this->attrs['type'] ? $this->attrs['type'] : $this->type])))
{
continue; // do NOT output already converted legacy options
}
echo ' '.$name.'="'.htmlspecialchars($value).'"';
}
echo ' php-class="'.get_class($this).'"';
if ($this->children)
{
echo ">\n";
foreach($this->children as $child)
{
$child->toXml($indent."\t");
}
echo "$indent</$this->type>\n";
}
else
{
echo " />\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
* @param array $expand =null values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @return string complete form-name
*/
static function form_name($cname,$name,array $expand=null)
{
if ($expand && !empty($name))
{
$name = self::expand_name($name, $expand['c'], $expand['row'], $expand['c_'], $expand['row_'], $expand['cont']);
}
if (count($name_parts = explode('[', $name, 2)) > 1)
{
$name_parts = array_merge(array($name_parts[0]), explode('][', substr($name_parts[1],0,-1)));
}
if (!empty($cname))
{
array_unshift($name_parts,$cname);
}
$form_name = array_shift($name_parts);
if (count($name_parts))
{
// RB: not sure why this business with entity encoding for square brakets, it messes up validation
//$form_name .= '&#x5B;'.implode('&#x5D;&#x5B;',$name_parts).'&#x5D;';
$form_name .= '['.implode('][',$name_parts).']';
}
return $form_name;
}
/**
* return a reference to $arr[$idx]
*
* This works for non-trival indexes like 'a[b][c]' too: it returns &$arr[a][b][c]
* $sub = get_array($arr,'a[b]'); $sub = 'c'; is equivalent to $arr['a']['b'] = 'c';
*
* @param array $arr the array to search, referenz as a referenz gets returned
* @param string $_idx the index, may contain sub-indices like a[b], see example below
* @param boolean $reference_into default False, if True none-existing sub-arrays/-indices get created to be returned as referenz, else False is returned
* @param bool $skip_empty returns false if $idx is not present in $arr
* @return mixed reference to $arr[$idx] or null if $idx is not set and not $reference_into
*/
static function &get_array(&$arr,$_idx,$reference_into=False,$skip_empty=False)
{
if (!is_array($arr))
{
throw new Api\Exception\AssertionFailed(__METHOD__."(\$arr,'$_idx',$reference_into,$skip_empty) \$arr is no array!");
}
if (is_object($_idx)) return false; // given an error in php5.2
// Make sure none of these are left
$idx = str_replace(array('&#x5B;','&#x5D;'), array('[',']'), $_idx);
// Handle things expecting arrays - ends in []
if(substr($idx,-2) == "[]")
{
$idx = substr($idx,0,-2);
}
if (count($idxs = explode('[', $idx, 2)) > 1)
{
$idxs = array_merge(array($idxs[0]), explode('][', substr($idxs[1],0,-1)));
}
$pos = &$arr;
foreach($idxs as $idx)
{
if (!is_array($pos) && (!$reference_into || $reference_into && isset($pos)))
{
//if ($reference_into) error_log(__METHOD__."(".(strlen($s=array2string($arr))>512?substr($s,0,512).'...':$s).", '$idx', ".array2string($reference_into).", ".array2string($skip_empty).") ".function_backtrace());
return null;
}
if($skip_empty && (!is_array($pos) || !isset($pos[$idx]))) return null;
$pos = &$pos[$idx];
}
return $pos;
}
/**
* return a reference to $arr[$idx]
*
* This works for non-trival indexes like 'a[b][c]' too: it returns &$arr[a][b][c]
* $sub = get_array($arr,'a[b]'); $sub = 'c'; is equivalent to $arr['a']['b'] = 'c';
*
* @param array& $_arr the array to search, referenz as a referenz gets returned
* @param string $_idx the index, may contain sub-indices like a[b], see example below
* @param mixed $_value value to set
*/
static function set_array(&$_arr, $_idx, $_value)
{
$ref =& self::get_array($_arr, $_idx, true);
if (true) $ref = $_value;
}
/**
* Checks if a widget is readonly:
* - readonly attribute set
* - $readonlys[__ALL__] set and $readonlys[$form_name] !== false
* - $readonlys[$form_name] evaluates to true
*
* @param string $cname =''
* @param string $form_name =null form_name, to not calculate him again
* @return boolean
*/
public function is_readonly($cname='', $form_name=null)
{
if (!isset($form_name))
{
$expand = array(
'cont' => self::get_array(self::$request->content, $cname),
);
$form_name = self::form_name($cname, $this->id, $expand);
}
$readonly = $this->attrs['readonly'] || self::$request->readonlys[$form_name] ||
self::get_array(self::$request->readonlys,$form_name) === true ||
isset(self::$request->readonlys['__ALL__']) && (
// Exceptions to all
self::$request->readonlys[$form_name] !== false &&
self::get_array(self::$request->readonlys,$form_name) !== false
);
//error_log(__METHOD__."('$cname') this->id='$this->id' --> form_name='$form_name': attrs[readonly]=".array2string($this->attrs['readonly']).", readonlys['$form_name']=".array2string(self::$request->readonlys[$form_name]).", readonlys[$form_name]=".array2string(self::get_array(self::$request->readonlys,$form_name)).", readonlys['__ALL__']=".array2string(self::$request->readonlys['__ALL__'])." returning ".array2string($readonly));
return $readonly;
}
/**
* Validation errors from process_show and the extensions, should be set via etemplate::set_validation_error
*
* @public array form_name => message pairs
*/
static protected $validation_errors = array();
/**
* 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
*/
public static function set_validation_error($name,$error,$cname=null)
{
// not yet used: if (is_null($cname)) $cname = self::$name_vars;
error_log(__METHOD__."('$name','$error','$cname') ".function_backtrace());
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;
}
}
/**
* 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
*/
public static function validation_errors($ignore_validation='',$cname='')
{
// not yet used: if (is_null($cname)) $cname = self::$name_vars;
//echo "<p>uietemplate::validation_errors('$ignore_validation','$cname') validation_error="; _debug_array(self::$validation_errors);
if (!$ignore_validation) return count(self::$validation_errors) > 0;
foreach(array_values(self::$validation_errors) as $name)
{
if ($cname) $name = preg_replace('/^'.$cname.'\[([^\]]+)\](.*)$/','\\1\\2',$name);
// treat $ignoare_validation only as regular expression, if it starts with a slash
if ($ignore_validation[0] == '/' && !preg_match($ignore_validation,$name) ||
$ignore_validation[0] != '/' && $ignore_validation != $name)
{
//echo "<p>uietemplate::validation_errors('$ignore_validation','$cname') name='$name' ($error) not ignored!!!</p>\n";
return true;
}
//echo "<p>uietemplate::validation_errors('$ignore_validation','$cname') name='$name' ($error) ignored</p>\n";
}
return false;
}
/**
* 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 &getElementAttribute($name, $attr)
{
//error_log(__METHOD__."('$name', '$attr')");
return self::$request->modifications[$name][$attr];
}
/**
* Set an attribute in a named cell if val is not NULL else return the attribute
*
* Can be called static, in which case it only sets modifications.
*
* @param string $name cell-name
* @param string $attr attribute-name
* @param mixed $val if not NULL sets attribute else returns it
* @return reference to attribute
*/
public static function &setElementAttribute($name,$attr,$val)
{
//error_log(__METHOD__."('$name', '$attr', ...) request=".get_class(self::$request).", response=".get_class(self::$response).function_backtrace());
$ref =& self::$request->modifications[$name][$attr];
if(self::$request && self::$response && (!isset($this) || $val != $this->attrs[$attr]))
{
// In an AJAX response - automatically add
self::$response->generic('assign',array(
'etemplate_exec_id' => self::$request->id(),
'id' => $name,
'key' => $attr,
'value' => $val
));
// Don't delete it
self::$request->unset_to_process('');
//error_log(__METHOD__."('$name', '$attr', ...) ".function_backtrace());
}
if (isset($this)) $this->attrs[$attr] = $val;
if (!is_null($val)) $ref = $val;
//error_log(__METHOD__."('$name', '$attr', ".array2string($val).')');
return $ref;
}
/**
* disables all cells with name == $name
*
* @param sting $name cell-name
* @param boolean $disabled =true disable or enable a cell, default true=disable
* @return reference to attribute
*/
public function disableElement($name,$disabled=True)
{
return self::setElementAttribute($name, 'disabled', $disabled);
}
}

View File

@ -3,21 +3,23 @@
* EGroupware - eTemplate serverside ajax select widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2015 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
/**
* eTemplate ajax select widget
*
*/
class etemplate_widget_ajax_select extends etemplate_widget_menupopup
class AjaxSelect extends Select
{
/**
* Fill type options in self::$request->sel_options to be used on the client
*
@ -66,5 +68,4 @@ class etemplate_widget_ajax_select extends etemplate_widget_menupopup
}
}
}
etemplate_widget::registerWidget('etemplate_widget_ajax_select', array('ajax_select'));
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\AjaxSelect', array('ajax_select'));

View File

@ -0,0 +1,136 @@
<?php
/**
* EGroupware - eTemplate widget baseclass
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
/**
* *box widgets having an own namespace
*/
class Box extends Etemplate\Widget
{
/**
* (Array of) comma-separated list of legacy options to automatically replace when parsing with set_attrs
*
* @var string|array
*/
protected $legacy_options = array(
'box' => ',cellpadding,cellspacing,keep',
'hbox' => 'cellpadding,cellspacing,keep',
'vbox' => 'cellpadding,cellspacing,keep',
'groupbox' => 'cellpadding,cellspacing,keep',
);
/**
* Run a given method on all children
*
* Reimplemented because grids and boxes can have an own namespace.
* GroupBox has no namespace!
*
* @param string $method_name
* @param array $params =array('') parameter(s) first parameter has to be cname!
* @param boolean $respect_disabled =false false (default): ignore disabled, true: method is NOT run for disabled widgets AND their children
*/
public function run($method_name, $params=array(''), $respect_disabled=false)
{
$cname =& $params[0];
$expand =& $params[1];
$old_cname = $params[0];
$old_expand = $params[1];
if ($this->id && $this->type != 'groupbox') $cname = self::form_name($cname, $this->id, $params[1]);
if ($expand['cname'] !== $cname && $cname)
{
$expand['cont'] =& self::get_array(self::$request->content, $cname);
$expand['cname'] = $cname;
}
if ($respect_disabled && ($disabled = $this->attrs['disabled'] && self::check_disabled($this->attrs['disabled'], $expand)))
{
//error_log(__METHOD__."('$method_name', ".array2string($params).', '.array2string($respect_disabled).") $this disabled='{$this->attrs['disabled']}'=".array2string($disabled).": NOT running");
return;
}
if (method_exists($this, $method_name))
{
call_user_func_array(array($this, $method_name), $params);
}
// Expand children
$columns_disabled = null;
for($n = 0; ; ++$n)
{
if (isset($this->children[$n]))
{
$child =& $this->children[$n];
// If type has something that can be expanded, we need to expand it so the correct method is run
$this->expand_widget($child, $expand);
}
// check if we need to autorepeat last row ($child)
elseif (isset($child) && $child->type == 'box' && $this->need_autorepeat($child, $cname, $expand))
{
// Set row for repeating
$expand['row'] = $n;
// not breaking repeats last row/column ($child)
}
else
{
break;
}
//error_log('Running ' . $method_name . ' on child ' . $n . '(' . $child . ') ['.$expand['row'] . ','.$expand['c'] . ']');
$disabled = $child->run($method_name, $params, $respect_disabled, $columns_disabled) === false;
}
$params[0] = $old_cname;
$params[1] = $old_expand;
return true;
}
/**
* Check if a box child needs autorepeating, because still content left
*
* We only check passed widget and direct children.
*
* @param string $cname
* @param array $expand
*/
private function need_autorepeat(Etemplate\Widget $widget, $cname, array $expand)
{
foreach(array($widget) + $widget->children as $check_widget)
{
$pat = $check_widget->id;
while(($pattern = strstr($pat, '$')))
{
$pat = substr($pattern,$pattern[1] == '{' ? 2 : 1);
$Ok = $pat[0] == 'r' && !(substr($pat,0,2) == 'r_' ||
substr($pat,0,4) == 'row_' && substr($pat,0,8) != 'row_cont');
if ($Ok && ($fname=self::form_name($cname, $check_widget->id, $expand)) &&
// need to break if fname ends in [] as get_array() will ignore it and returns whole array
// for an id like "run[$row_cont[appname]]"
substr($fname, -2) != '[]' &&
($value = self::get_array(self::$request->content, $fname)) !== null) // null = not found (can be false!)
{
//error_log(__METHOD__."($widget,$cname) $this autorepeating row $expand[row] because of $check_widget->id = '$fname' is ".array2string($value));
unset($value);
return true;
}
}
}
return false;
}
}
// register class for layout widgets, which can have an own namespace
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Box', array('box', 'hbox', 'vbox', 'groupbox'));

View File

@ -3,18 +3,22 @@
* EGroupware - eTemplate serverside button widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-11 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
/**
* eTemplate button widget
*/
class etemplate_widget_button extends etemplate_widget
class Button extends Etemplate\Widget
{
/**
* True after submit-validation, if cancel button was pressed
@ -57,7 +61,7 @@ class etemplate_widget_button extends etemplate_widget
)
{
$valid =& self::get_array($validated, $form_name, true);
$valid = is_array($value) ? $value : 'pressed';
if (true) $valid = is_array($value) ? $value : 'pressed';
// recorded pressed button globally, was in the template object before, put now as static on this object
if ($this->type == 'cancel' || $form_name == 'cancel' || substr($form_name,-10) == '[cancel]')
@ -72,4 +76,4 @@ class etemplate_widget_button extends etemplate_widget
}
}
}
etemplate_widget::registerWidget('etemplate_widget_button', array('button','buttononly'));
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Button', array('button','buttononly'));

View File

@ -3,20 +3,24 @@
* EGroupware - eTemplate serverside checkbox widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-11 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
/**
* eTemplate checkbox widget
*
* Multiple checkbox widgets can have the same name ending in [], in which case an array with the selected_value's of the checked boxes get returned.
*/
class etemplate_widget_checkbox extends etemplate_widget
class Checkbox extends Etemplate\Widget
{
/**
* (Array of) comma-separated list of legacy options to automatically replace when parsing with set_attrs
@ -78,7 +82,7 @@ class etemplate_widget_checkbox extends etemplate_widget
}
if ($type == 'radio')
{
$options = etemplate_widget_menupopup::selOptions($form_name, true);
$options = Select::selOptions($form_name, true);
if (in_array($value, $options))
{
$valid = $value;
@ -125,4 +129,4 @@ class etemplate_widget_checkbox extends etemplate_widget
}
}
}
etemplate_widget::registerWidget('etemplate_widget_checkbox', array('checkbox', 'radio'));
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Checkbox', array('checkbox', 'radio'));

View File

@ -0,0 +1,177 @@
<?php
/**
* EGroupware eTemplate Extension - Contact Widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api;
/**
* eTemplate Extension: Contact widget
*
* This widget can be used to fetch fields of a contact specified by contact-id
*/
class Contact extends Entry
{
/**
* Instance of the contacts class
*
* @var Api\Contacts
*/
private $contacts;
/**
* Array with a transformation description, based on attributes to modify.
*
* Exampels:
*
* * 'type' => array('some' => 'other')
* if 'type' attribute equals 'some' replace it with 'other'
*
* * 'type' => array('some' => array('type' => 'other', 'options' => 'otheroption')
* same as above, but additonally set 'options' attr to 'otheroption'
*
* --> leaf element is the action, if previous filters are matched:
* - if leaf is scalar, it just replaces the previous filter value
* - if leaf is an array, it contains assignments for (multiple) attributes: attr => value pairs
*
* * 'type' => array(
* 'some' => array(...),
* 'other' => array(...),
* '__default__' => array(...),
* )
* it's possible to have a list of filters with actions to run, plus a '__default__' which matches all not explicitly named values
*
* * 'value' => array('__callback__' => 'app.class.method' || 'class::method' || 'method')
* run value through a *serverside* callback, eg. reading an entry based on it's given id
*
* * 'value' => array('__js__' => 'function(value) { return value+5; }')
* run value through a *clientside* callback running in the context of the widget
*
* * 'name' => '@name[@options]'
* replace value of 'name' attribute with itself (@name) plus value of options in square brackets
*
* --> attribute name prefixed with @ sign means value of given attribute
*
* @var array
*/
protected static $transformation = array(
'type' => array(
'contact-fields' => array( // contact-fields widget
'sel_options' => array('__callback__' => 'get_contact_fields'),
'type' => 'select',
'no_lang' => true,
'options' => 'None',
),
'contact-template' => array(
'type' => 'template',
'options' => '',
'template' => array('__callback__' => 'parse_template'),
),
'__default__' => array(
'options' => array(
'bday' => array('type' => 'date', 'options' => 'Y-m-d'),
'owner' => array('type' => 'select-account', 'options' => ''),
'modifier' => array('type' => 'select-account', 'options' => ''),
'creator' => array('type' => 'select-account', 'options' => ''),
'modifed' => array('type' => 'date-time', 'options' => ''),
'created' => array('type' => 'date-time', 'options' => ''),
'cat_id' => array('type' => 'select-cat', 'options' => ''),
'__default__' => array('type' => 'label', 'options' => ''),
),
'no_lang' => 1,
),
),
);
/**
* Constructor of the extension
*
* @param string $xml or 'html' for old etemplate
*/
public function __construct($xml)
{
if (is_a($xml, 'XMLReader') || $xml != '' && $xml != 'html')
{
parent::__construct($xml);
}
$this->contacts = $GLOBALS['egw']->contacts;
}
/**
* Legacy support for putting the template name in 'label' param
*
* @param string $template
* @param array $attrs
*/
public function parse_template($template, &$attrs)
{
return sprintf($template ? $template : $attrs['label'], $attrs['value']);
}
/**
* Get all contact-fields
*
* @return array
*/
public function get_contact_fields()
{
ApiTranslation::add_app('addressbook');
$this->contacts->__construct();
$options = $this->contacts->contact_fields;
foreach($this->contacts->customfields as $name => $data)
{
$options['#'.$name] = $data['label'];
}
return $options;
}
public function get_entry($value, array $attrs)
{
return $this->get_contact($value, $attrs);
}
/**
* Get contact data, if $value not already contains them
*
* @param int|string|array $value
* @param array $attrs
* @return array
*/
public function get_contact($value, array $attrs)
{
if (is_array($value) && !(array_key_exists('app',$value) && array_key_exists('id', $value))) return $value;
if(is_array($value) && array_key_exists('app', $value) && array_key_exists('id', $value)) $value = $value['id'];
switch($attrs['type'])
{
case 'contact-account':
case 'contact-template':
if (substr($value,0,8) != 'account:')
{
$value = 'account:'.($attrs['name'] != 'account:' ? $value : $GLOBALS['egw_info']['user']['account_id']);
}
// fall-through
case 'contact-value':
default:
if (substr($value,0,12) == 'addressbook:') $value = substr($value,12); // link-entry syntax
if (!($contact = $this->contacts->read($value)))
{
$contact = array();
}
break;
}
unset($contact['jpegphoto']); // makes no sense to return binary image
//error_log(__METHOD__."('$value') returning ".array2string($contact));
return $contact;
}
}

View File

@ -3,19 +3,22 @@
* EGroupware - eTemplate custom fields widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2011 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api;
/**
* Widgets for custom fields and listing custom fields
*
*/
class etemplate_widget_customfields extends etemplate_widget_transformer
class Customfields extends Transformer
{
/**
@ -123,17 +126,17 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
if(!$app)
{
$app =& $this->setElementAttribute(self::GLOBAL_VALS, 'app', $GLOBALS['egw_info']['flags']['currentapp']);
$customfields =& $this->setElementAttribute(self::GLOBAL_VALS, 'customfields', egw_customfields::get($app));
$customfields =& $this->setElementAttribute(self::GLOBAL_VALS, 'customfields', Api\Storage\Customfields::get($app));
}
// if we are in the etemplate editor or the app has no cf's, load the cf's from the app the tpl belongs too
if ($app && $app != 'stylite' && $app != $GLOBALS['egw_info']['flags']['currentapp'] && !isset($customfields) && (
$GLOBALS['egw_info']['flags']['currentapp'] == 'etemplate' || !$this->attrs['customfields'] ||
etemplate::$hooked
Etemplate::$hooked
) || !isset($customfields))
{
// app changed
$customfields =& egw_customfields::get($app);
$customfields =& Api\Storage\Customfields::get($app);
}
// Filter fields
if($this->attrs['field-names'])
@ -188,7 +191,7 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
$sel_options[$lname] = lang($label);
$fields_with_vals[]=$lname;
}
$link_types = array_intersect_key(egw_link::app_list('query'),egw_link::app_list('title'));
$link_types = array_intersect_key(Api\Link::app_list('query'), Api\Link::app_list('title'));
// Explicitly add in filemanager, which does not support query or title
$link_types['filemanager'] = lang('filemanager');
@ -223,7 +226,7 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
{
if (!empty($data['values']))
{
etemplate_widget_menupopup::fix_encoded_options($data['values']);
Select::fix_encoded_options($data['values']);
}
}
if($fields != $customfields)
@ -240,7 +243,7 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
// Re-format date custom fields from Y-m-d
$field_settings =& self::get_array(self::$request->modifications, "{$this->id}[customfields]",true);
if (true) $field_settings = array();
$link_types = egw_link::app_list();
$link_types = Api\Link::app_list();
foreach($fields as $fname => $field)
{
// Run beforeSendToClient for each field
@ -257,12 +260,12 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
*
* @param string $fname custom field name
* @param array $field custom field data
* @return etemplate_widget
* @return Etemplate\Widget
*/
protected function _widget($fname, array $field)
{
static $link_types = null;
if (!isset($link_types)) $link_types = egw_link::app_list ();
if (!isset($link_types)) $link_types = Api\Link::app_list ();
$type = $field['type'];
// Link-tos needs to change from appname to link-to
@ -293,7 +296,7 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
case 'vfs-upload':
$widget->attrs['path'] = $field['app'] . ':' .
self::expand_name('$cont['.egw_link::get_registry($field['app'],'view_id').']',0,0,0,0,self::$request->content).
self::expand_name('$cont['.Api\Link::get_registry($field['app'],'view_id').']',0,0,0,0,self::$request->content).
':'.$field['label'];
break;
@ -313,7 +316,7 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
case 'radio':
if (count($field['values']) == 1 && isset($field['values']['@']))
{
$field['values'] = egw_customfields::get_options_from_file($field['values']['@']);
$field['values'] = Api\Storage\Customfields::get_options_from_file($field['values']['@']);
}
// keep extra values set by app code, eg. addressbook advanced search
if (is_array(self::$request->sel_options[self::$prefix.$fname]))
@ -329,7 +332,7 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
$options = self::$request->sel_options[self::$prefix.$fname];
if (is_array($options))
{
etemplate_widget_menupopup::fix_encoded_options($options);
Select::fix_encoded_options($options);
self::$request->sel_options[self::$prefix.$fname] = $options;
}
break;

View File

@ -3,16 +3,20 @@
* EGroupware - eTemplate serverside date widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-11 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @author Nathan Gray
* @copyright 2011 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api;
/**
* eTemplate date widget
*
@ -35,7 +39,7 @@
* for the field, the conversion is done as needed understand what the application
* sends, and to give the application what it wants when the form is submitted.
*/
class etemplate_widget_date extends etemplate_widget_transformer
class Date extends Transformer
{
protected static $transformation = array(
'type' => array('date-houronly' => 'select-hour')
@ -76,7 +80,7 @@ class etemplate_widget_date extends etemplate_widget_transformer
* Perform any needed data manipulation on each row
* before sending it to client.
*
* This is used by etemplate_widget_nextmatch on each row to do any needed
* This is used by Nextmatch on each row to do any needed
* adjustments. If not needed, don't implement it.
*
* @param type $cname
@ -91,7 +95,7 @@ class etemplate_widget_date extends etemplate_widget_transformer
$form_name = self::form_name($cname, $this->id, $expand);
$value =& $this->get_array($data, $form_name, true);
$value = $this->format_date($value);
if (true) $value = $this->format_date($value);
}
/**
@ -105,11 +109,11 @@ class etemplate_widget_date extends etemplate_widget_transformer
if ($this->attrs['dataformat'] && !is_numeric($value))
{
$date = date_create_from_format($this->attrs['dataformat'], $value, egw_time::$user_timezone);
$date = Api\DateTime::createFromFormat($this->attrs['dataformat'], $value, Api\DateTime::$user_timezone);
}
else
{
$date = new egw_time($value);
$date = new Api\DateTime($value);
}
if($this->type == 'date-timeonly')
{
@ -160,17 +164,17 @@ class etemplate_widget_date extends etemplate_widget_transformer
}
if($value)
{
$date = new egw_time($value);
$date = new Api\DateTime($value);
}
if (!empty($this->attrs['min']))
{
if(is_numeric($this->attrs['min']))
{
$min = new egw_time(strtotime( $this->attrs['min'] . 'days'));
$min = new Api\DateTime(strtotime( $this->attrs['min'] . 'days'));
}
else
{
$min = new egw_time(strtotime($this->attrs['min']));
$min = new Api\DateTime(strtotime($this->attrs['min']));
}
if($value < $min)
{
@ -185,11 +189,11 @@ class etemplate_widget_date extends etemplate_widget_transformer
{
if(is_numeric($this->attrs['max']))
{
$max = new egw_time(strtotime( $this->attrs['max'] . 'days'));
$max = new Api\DateTime(strtotime( $this->attrs['max'] . 'days'));
}
else
{
$max = new egw_time(strtotime($this->attrs['max']));
$max = new Api\DateTime(strtotime($this->attrs['max']));
}
if($value < $max)
{
@ -219,7 +223,7 @@ class etemplate_widget_date extends etemplate_widget_transformer
// this is not really a user error, but one of the clientside engine
self::set_validation_error($form_name,lang("'%1' is not a valid date !!!", $value).' '.$this->dataformat);
}
//error_log("$this : ($valid)" . egw_time::to($valid));
//error_log("$this : ($valid)" . Api\DateTime::to($valid));
}
}
}

View File

@ -0,0 +1,31 @@
<?php
/**
* EGroupware - eTemplate widget baseclass
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
/**
* Description widget
*
* Reimplemented to set legacy options
*/
class Description extends Etemplate\Widget
{
/**
* (Array of) comma-separated list of legacy options to automatically replace when parsing with set_attrs
*
* @var string|array
*/
protected $legacy_options = 'bold-italic,link,activate_links,label_for,link_target,link_popup_size,link_title';
}

View File

@ -1,25 +1,27 @@
<?php
/**
* EGroupware eTemplate Extension - Entry Widget
* EGroupware eTemplate - Entry Widget
*
* This is the server side for a widget that loads a particular entry from an
* application, and displays the value of a particular field
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage extensions
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @version $Id: class.contact_widget.inc.php 46844 2014-05-07 09:00:59Z ralfbecker $
*/
namespace EGroupware\Api\Etemplate\Widget;
/**
* eTemplate Extension: Entry widget
* eTemplate Entry widget
*
* This widget can be used to fetch fields of any entry specified by its ID.
* The entry is loaded once and shared amoung widget that need it.
*/
abstract class etemplate_widget_entry extends etemplate_widget_transformer
abstract class Entry extends Transformer
{
/**
@ -38,7 +40,7 @@ abstract class etemplate_widget_entry extends etemplate_widget_transformer
/**
* Array with a transformation description, based on attributes to modify.
*
* @see etemplate_widget_transformer::$transformation
* @see Transformer::$transformation
*
* @var array
*/
@ -91,7 +93,7 @@ abstract class etemplate_widget_entry extends etemplate_widget_transformer
// Set the new value so transformer can find it. Use prefix to avoid changing the original value
$new_value =& self::get_array(self::$request->content, self::ID_PREFIX .$this->id, true, false);
$new_value = $data;
if (true) $new_value = $data;
$this->id = self::ID_PREFIX . $this->id . "[{$attrs['field']}]";
$old_type = self::getElementAttribute($this->id, 'type');

View File

@ -3,19 +3,27 @@
* EGroupware - eTemplate serverside file upload widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2011 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
// explicitly import old not yet ported classes
use egw_json_response;
/**
* eTemplate file upload widget
* Uses AJAX to send file(s) to server, and stores for submit
*/
class etemplate_widget_file extends etemplate_widget
class File extends Etemplate\Widget
{
/**
* Constructor
@ -47,12 +55,12 @@ class etemplate_widget_file extends etemplate_widget
$response = egw_json_response::get();
$request_id = str_replace(' ', '+', rawurldecode($_REQUEST['request_id']));
$widget_id = $_REQUEST['widget_id'];
if(!self::$request = etemplate_request::read($request_id)) {
if(!self::$request = Etemplate\Request::read($request_id)) {
$response->error("Could not read session");
return;
}
if (!($template = etemplate_widget_template::instance(self::$request->template['name'], self::$request->template['template_set'],
if (!($template = Template::instance(self::$request->template['name'], self::$request->template['template_set'],
self::$request->template['version'], self::$request->template['load_via'])))
{
// Can't use callback
@ -167,7 +175,7 @@ class etemplate_widget_file extends etemplate_widget
$temp_name = basename($file['tmp_name']);
$file_data[$temp_name] = array(
// Use egw_vfs to avoid UTF8 / non-ascii issues
'name' => egw_vfs::basename($file['name']),
'name' => Api\Vfs::basename($file['name']),
'type' => $file['type']
);
}
@ -237,19 +245,24 @@ class etemplate_widget_file extends etemplate_widget
* @param string $dir - directory path
* @link http://php.net/manual/en/function.rmdir.php
*/
private static function rrmdir($dir) {
if (is_dir($dir)) {
$objects = scandir($dir);
foreach ($objects as $object) {
if ($object != "." && $object != "..") {
if (filetype($dir . "/" . $object) == "dir") {
rrmdir($dir . "/" . $object);
} else {
private static function rrmdir($dir)
{
if (is_dir($dir))
{
foreach (scandir($dir) as $object)
{
if ($object != "." && $object != "..")
{
if (filetype($dir . "/" . $object) == "dir")
{
self::rrmdir($dir . "/" . $object);
}
else
{
unlink($dir . "/" . $object);
}
}
}
reset($objects);
rmdir($dir);
}
}
@ -303,4 +316,3 @@ class etemplate_widget_file extends etemplate_widget
}
}
}
etemplate_widget::registerWidget('etemplate_widget_file', array('file'));

View File

@ -3,14 +3,16 @@
* EGroupware - eTemplate serverside gantt widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2014 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
egw_framework::includeCSS('/phpgwapi/js/dhtmlxGantt/codebase/dhtmlxgantt.css');
/**
@ -18,7 +20,7 @@ egw_framework::includeCSS('/phpgwapi/js/dhtmlxGantt/codebase/dhtmlxgantt.css');
*
* The Gantt widget accepts children, and uses them as simple filters
*/
class etemplate_widget_gantt extends etemplate_widget_box
class Gantt extends Box
{
// No legacy options
protected $legacy_options = array();
@ -34,6 +36,8 @@ class etemplate_widget_gantt extends etemplate_widget_box
*/
public function validate($cname, array $expand, array $content, &$validated=array())
{
unset($expand); // not used, but required by function signature
$value = self::get_array($content, $cname);
$validated[$cname] = array(
'action' => $value['action'],
@ -42,5 +46,3 @@ class etemplate_widget_gantt extends etemplate_widget_box
}
}
// register class for layout widgets, which can have an own namespace
//etemplate_widget::registerWidget('etemplate_widget_box', array('box', 'hbox', 'vbox', 'groupbox'));

View File

@ -3,14 +3,18 @@
* EGroupware - eTemplate serverside grid widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-14 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
/**
* eTemplate grid widget incl. row(s) and column(s)
*
@ -36,12 +40,12 @@
* </rows>
* </grid>
*/
class etemplate_widget_grid extends etemplate_widget_box
class Grid extends Box
{
/**
* (Array of) comma-separated list of legacy options to automatically replace when parsing with set_attrs
*
* Not used for grid, just need to be set to unset array of etemplate_widget_box
* Not used for grid, just need to be set to unset array of extended Box
*
* @var string|array
*/
@ -57,8 +61,8 @@ class etemplate_widget_grid extends etemplate_widget_box
* - as a grid can contain other grid's as direct child, we have to backup and initialise $columns_disabled in grid run!
*
* @param string $method_name
* @param array $params=array('') parameter(s) first parameter has to be the cname, second $expand!
* @param boolean $respect_disabled=false false (default): ignore disabled, true: method is NOT run for disabled widgets AND their children
* @param array $params =array('') parameter(s) first parameter has to be the cname, second $expand!
* @param boolean $respect_disabled =false false (default): ignore disabled, true: method is NOT run for disabled widgets AND their children
* @param array $columns_disabled=array() disabled columns
*/
public function run($method_name, $params=array(''), $respect_disabled=false, &$columns_disabled=array())
@ -176,9 +180,9 @@ class etemplate_widget_grid extends etemplate_widget_box
foreach(array_merge(array($direct_child), $n ? array() : $direct_child->children) as $child)
{
$pat = $child->id;
while(($pat = strstr($pat, '$')))
while(($patstr = strstr($pat, '$')))
{
$pat = substr($pat,$pat[1] == '{' ? 2 : 1);
$pat = substr($patstr,$patstr[1] == '{' ? 2 : 1);
switch ($this->type)
{
@ -201,6 +205,7 @@ class etemplate_widget_grid extends etemplate_widget_box
($value = self::get_array(self::$request->content,$fname)) !== null) // null = not found (can be false!)
{
//error_log(__METHOD__."('$cname', ) $this autorepeating row $expand[row] because of $child->id = '$fname' is ".array2string($value));
unset($value);
return true;
}
}
@ -210,4 +215,4 @@ class etemplate_widget_grid extends etemplate_widget_box
return false;
}
}
etemplate_widget::registerWidget('etemplate_widget_grid', array('grid', 'rows', 'row', 'columns', 'column'));
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Grid', array('grid', 'rows', 'row', 'columns', 'column'));

View File

@ -1,22 +1,26 @@
<?php
/**
* eGroupWare eTemplate2 - History log server-side
* EGroupware eTemplate2 - History log server-side
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2012 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
/**
* eTemplate history log widget displays a list of changes to the current record.
* The widget is encapsulated, and only needs the record's ID, and a map of
* fields:widgets for display
*/
class etemplate_widget_historylog extends etemplate_widget
class HistoryLog extends Etemplate\Widget
{
/**
@ -83,7 +87,8 @@ class etemplate_widget_historylog extends etemplate_widget
$form_name = self::form_name($cname, $this->id, $expand);
$value = self::get_array($content, $form_name);
$valid =& self::get_array($validated, $form_name, true);
$valid = $value;
if (true) $valid = $value;
return true;
}
}
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\HistoryLog', 'historylog');

View File

@ -3,18 +3,23 @@
* EGroupware - eTemplate serverside htmlarea widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-11 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
/**
* eTemplate htmlarea widget
*/
class etemplate_widget_htmlarea extends etemplate_widget
class HtmlArea extends Etemplate\Widget
{
protected $legacy_options = 'mode,height,width,expand_toolbar,base_href';
@ -32,12 +37,12 @@ class etemplate_widget_htmlarea extends etemplate_widget
{
$form_name = self::form_name($cname, $this->id);
$config = egw_ckeditor_config::get_ckeditor_config_array($this->attrs['mode'], $this->attrs['height'],
$config = Api\Html\CkEditorConfig::get_ckeditor_config_array($this->attrs['mode'], $this->attrs['height'],
$this->attrs['expand_toolbar'],$this->attrs['base_href']
);
// User preferences
$font = $GLOBALS['egw_info']['user']['preferences']['common']['rte_font'];
$font_size = egw_ckeditor_config::font_size_from_prefs();
$font_size = Api\Html\CkEditorConfig::font_size_from_prefs();
$font_span = '<span style="width: 100%; display: inline; '.
($font?'font-family:'.$font.'; ':'').($font_size?'font-size:'.$font_size.'; ':'').
'">&#8203;</span>';
@ -70,10 +75,11 @@ class etemplate_widget_htmlarea extends etemplate_widget
// only purify for html, mode "ascii" is NO html and content get lost!
if ($this->attrs['mode'] != 'ascii')
{
$value = html::purify($value, $this->attrs['validation_rules']);
$value = Api\Html::purify($value, $this->attrs['validation_rules']);
}
$valid =& self::get_array($validated, $form_name, true);
if (true) $valid = $value;
}
}
}
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\HtmlArea', 'htmlarea');

View File

@ -3,19 +3,25 @@
* EGroupware - eTemplate serverside image widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2011 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
/**
* eTemplate image widget
*
* Displays image from URL, vfs, or finds by name
*/
class etemplate_widget_image extends etemplate_widget
class Image extends Etemplate\Widget
{
/**
* Fill type options in self::$request->sel_options to be used on the client
@ -36,7 +42,7 @@ class etemplate_widget_image extends etemplate_widget
$img = $image;
list($app) = explode('.',$form_name);
}
$src = common::find_image($app, $img);
$src = Api\Image::find($app, $img);
if(!$this->id)
{
// self::setElementAttribute($this->attrs['src'], 'id', $this->attrs['src']);
@ -44,4 +50,3 @@ class etemplate_widget_image extends etemplate_widget
self::setElementAttribute($this->attrs['src'], 'src', $src);
}
}
etemplate_widget::registerWidget('etemplate_widget_image', array('image'));

View File

@ -3,20 +3,28 @@
* EGroupware - eTemplate serverside itempicker widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @author Christian Binder <christian@jaytraxx.de>
* @copyright 2002-11 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @copyright 2012 by Christian Binder <christian@jaytraxx.de>
* @version $Id: class.etemplate_widget_itempicker.inc.php 36221 2011-08-20 10:27:38Z jaytraxx $
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
// explicitly import old not yet ported classes
use egw_json_response;
/**
* eTemplate itempicker widget
*/
class etemplate_widget_itempicker extends etemplate_widget
class ItemPicker extends Etemplate\Widget
{
/**
* Constructor
@ -35,13 +43,13 @@ class etemplate_widget_itempicker extends etemplate_widget
* Find items that match the given parameters
* using the egw_link class
*/
public static function ajax_item_search($app, $type, $pattern, $options=array()) {
public static function ajax_item_search($app, $type, $pattern, $options=array())
{
$options['type'] = $type ? $type : $options['type'];
$items = egw_link::query($app, $pattern, $options);
$items = Api\Link::query($app, $pattern, $options);
$response = egw_json_response::get();
$response->data($items);
}
}
etemplate_widget::registerWidget('etemplate_widget_itempicker', array('itempicker'));
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\ItemPicker', 'itempicker');

View File

@ -3,19 +3,28 @@
* EGroupware - eTemplate serverside of linking widgets
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2011 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
// explicitly import old not yet ported classes
use egw_json_response;
use common; // egw_exit
/**
* eTemplate link widgets
* Deals with creation and display of links between entries in various participating egw applications
*/
class etemplate_widget_link extends etemplate_widget
class Link extends Etemplate\Widget
{
public $public_functions = array(
@ -28,7 +37,7 @@ class etemplate_widget_link extends etemplate_widget
* Constructor
*
* @param string|XMLReader $xml string with xml or XMLReader positioned on the element to construct
* @throws egw_exception_wrong_parameter
* @throws Api\Exception\WrongParameter
*/
public function __construct($xml = '')
{
@ -66,8 +75,9 @@ class etemplate_widget_link extends etemplate_widget
if($value && !is_array($value) && !$this->attrs['only_app'])
{
// Try to explode
if(count(explode(':',$value)) < 2) {
throw new egw_exception_wrong_parameter("Wrong value sent to $this, needs to be an array. ".array2string($value));
if(count(explode(':',$value)) < 2)
{
throw new Api\Exception\WrongParameter("Wrong value sent to $this, needs to be an array. ".array2string($value));
}
list($app, $id) = explode(':', $value,2);
$value = array('app' => $app, 'id' => $id);
@ -84,7 +94,7 @@ class etemplate_widget_link extends etemplate_widget
if($attrs['type'] == 'link-list') {
$app = $value['to_app'];
$id = $value['to_id'];
$links = egw_link::get_links($app,$id,'','link_lastmod DESC',true, $value['show_deleted']);
$links = Api\Link::get_links($app,$id,'','link_lastmod DESC',true, $value['show_deleted']);
foreach($links as $link) {
$value[] = $link;
}
@ -97,7 +107,7 @@ class etemplate_widget_link extends etemplate_widget
public static function ajax_link_search($app, $type, $pattern, $options=array()) {
$options['type'] = $type ? $type : $options['type'];
if(!$options['num_rows']) $options['num_rows'] = 1000;
$links = egw_link::query($app, $pattern, $options);
$links = Api\Link::query($app, $pattern, $options);
$linksc = array_combine(array_map(create_function('$k', 'return (string)" ".$k;'), array_keys($links)), $links);
$response = egw_json_response::get();
$response->data($linksc);
@ -112,7 +122,7 @@ class etemplate_widget_link extends etemplate_widget
*/
public static function ajax_link_title($app,$id)
{
$title = egw_link::title($app, $id);
$title = Api\Link::title($app, $id);
//error_log(__METHOD__."('$app', '$id') = ".array2string($title));
egw_json_response::get()->data($title);
}
@ -130,7 +140,7 @@ class etemplate_widget_link extends etemplate_widget
{
if(count($ids))
{
$response[$app] = egw_link::titles($app, $ids);
$response[$app] = Api\Link::titles($app, $ids);
}
else
{
@ -143,10 +153,11 @@ class etemplate_widget_link extends etemplate_widget
/**
* Create links
*/
public static function ajax_link($app, $id, Array $links) {
public static function ajax_link($app, $id, Array $links)
{
// Files need to know full path in tmp directory
foreach($links as $key => $link) {
if($link['app'] == egw_link::VFS_APPNAME) {
if($link['app'] == Api\Link::VFS_APPNAME) {
if (is_dir($GLOBALS['egw_info']['server']['temp_dir']) && is_writable($GLOBALS['egw_info']['server']['temp_dir']))
{
$path = $GLOBALS['egw_info']['server']['temp_dir'] . '/' . $link['id'];
@ -159,38 +170,38 @@ class etemplate_widget_link extends etemplate_widget
$links[$key]['id'] = $link;
}
}
$result = egw_link::link($app, $id, $links);
$result = Api\Link::link($app, $id, $links);
$response = egw_json_response::get();
$response->data(is_array($id) ? $id : $result !== false);
}
public function ajax_link_list($value) {
public function ajax_link_list($value)
{
$app = $value['to_app'];
$id = $value['to_id'];
$links = egw_link::get_links($app,$id,$value['only_app'],'link_lastmod DESC',true, $value['show_deleted']);
$links = Api\Link::get_links($app,$id,$value['only_app'],'link_lastmod DESC',true, $value['show_deleted']);
foreach($links as &$link)
{
$link['title'] = egw_link::title($link['app'],$link['id'],$link);
if ($link['app'] == egw_link::VFS_APPNAME)
$link['title'] = Api\Link::title($link['app'],$link['id'],$link);
if ($link['app'] == Api\Link::VFS_APPNAME)
{
$link['target'] = '_blank';
$link['label'] = 'Delete';
$link['help'] = lang('Delete this file');
$link['title'] = egw_vfs::decodePath($link['title']);
$link['icon'] = egw_link::vfs_path($link['app2'],$link['id2'],$link['id'],true);
$link['download_url'] = egw_vfs::download_url($link['icon']);
$link['title'] = Api\Vfs::decodePath($link['title']);
$link['icon'] = Api\Link::vfs_path($link['app2'],$link['id2'],$link['id'],true);
$link['download_url'] = Api\Vfs::download_url($link['icon']);
// Make links to directories load in filemanager
if($link['type'] == 'httpd/unix-directory')
if($link['type'] == Api\Vfs::DIR_MIME_TYPE)
{
$link['target'] = 'filemanager';
}
}
else
{
$link['icon'] = egw_link::get_registry($link['app'], 'icon');
$link['icon'] = Api\Link::get_registry($link['app'], 'icon');
$link['label'] = 'Unlink';
$link['help'] = lang('Remove this link (not the entry itself)');
}
@ -209,18 +220,18 @@ class etemplate_widget_link extends etemplate_widget
$result = false;
if((int)$link_id > 0)
{
solink::update_remark((int)$link_id, $comment);
Api\Link::update_remark((int)$link_id, $comment);
$result = true;
}
else
{
$link = egw_link::get_link((int)$link_id);
if($link && $link['app'] == egw_link::VFS_APPNAME)
$link = Api\Link::get_link((int)$link_id);
if($link && $link['app'] == Api\Link::VFS_APPNAME)
{
$files = egw_link::list_attached($link['app2'],$link['id2']);
$files = Api\Link::list_attached($link['app2'],$link['id2']);
$file = $files[(int)$link_id];
$path = egw_link::vfs_path($link['app2'],$link['id2'],$file['id']);
$result = egw_vfs::proppatch($path, array(array('name' => 'comment', 'val' => $comment)));
$path = Api\Link::vfs_path($link['app2'],$link['id2'],$file['id']);
$result = Api\Vfs::proppatch($path, array(array('name' => 'comment', 'val' => $comment)));
}
}
$response = egw_json_response::get();
@ -239,14 +250,16 @@ class etemplate_widget_link extends etemplate_widget
}
if(!is_array($files)) $files = array($files);
foreach($files as $target) {
egw_link::link_file($app, $id, $target);
foreach($files as $target)
{
Api\Link::link_file($app, $id, $target);
}
}
public function ajax_delete($value) {
public function ajax_delete($value)
{
$response = egw_json_response::get();
$response->data(egw_link::unlink($value));
$response->data(Api\Link::unlink($value));
}
/**
@ -261,17 +274,17 @@ class etemplate_widget_link extends etemplate_widget
{
$app = $_GET['app'];
$id = $_GET['id'];
if(egw_link::file_access($app, $id))
if(Api\Link::file_access($app, $id))
{
$app_path = egw_link::vfs_path($app,$id,'',true);
$app_path = Api\Link::vfs_path($app,$id,'',true);
// Pass the files linked, not the entry path
$files = egw_vfs::find($app_path);
$files = Api\Vfs::find($app_path);
if($files[0] == $app_path)
{
array_shift($files);
}
egw_vfs::download_zip($files, egw_link::title($app, $id));
Api\Vfs::download_zip($files, Api\Link::title($app, $id));
common::egw_exit();
}
}
@ -299,7 +312,7 @@ class etemplate_widget_link extends etemplate_widget
{
$value = $value_in =& self::get_array($content, $form_name);
// keep values added into request by other ajax-functions, eg. files draged into htmlarea (etemplate_widget_vfs)
// keep values added into request by other ajax-functions, eg. files draged into htmlarea (Vfs)
if ((!$value || !$value['to_id']) && is_array($expand['cont'][$this->id]) && !empty($expand['cont'][$this->id]['to_id']))
{
$value['to_id'] = $expand['cont'][$this->id]['to_id'];
@ -318,7 +331,7 @@ class etemplate_widget_link extends etemplate_widget
// Do we have enough information to link automatically?
if(is_array($value) && $value['to_id'])
{
egw_link::link($value['to_app'], $value['to_id'], $link['app'], $link['id']);
Api\Link::link($value['to_app'], $value['to_id'], $link['app'], $link['id']);
}
else
{
@ -345,7 +358,7 @@ class etemplate_widget_link extends etemplate_widget
{
if(!is_array($value['to_id'])) $value['to_id'] = array();
$value['to_id'][] = array(
'app' => egw_link::VFS_APPNAME,
'app' => Api\Link::VFS_APPNAME,
'id' => array(
'name' => $attrs['name'],
'type' => $attrs['type'],

View File

@ -3,14 +3,25 @@
* EGroupware - eTemplate serverside implementation of the nextmatch widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-11 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
// explicitly import old not yet ported classes
use egw_json_response;
use egw;
use egw_framework; // includeCSS
use categories;
/**
* eTemplate serverside implementation of the nextmatch widget
*
@ -78,7 +89,7 @@
* 'placeholder' => // I String Optional text to display in the empty row placeholder. If not provided, it's "No matches found."
* 'placeholder_actions' => // I Array Optional list of actions allowed on the placeholder. If not provided, it's ["add"].
*/
class etemplate_widget_nextmatch extends etemplate_widget
class Nextmatch extends Etemplate\Widget
{
public function __construct($xml='')
{
@ -198,15 +209,15 @@ class etemplate_widget_nextmatch extends etemplate_widget
{
$value['options-cat_id'][''] = lang('All categories');
}
$value['options-cat_id'] += etemplate_widget_menupopup::typeOptions('select-cat', ',,'.$cat_app,$no_lang=true,false,$value['cat_id']);
etemplate_widget_menupopup::fix_encoded_options($value['options-cat_id']);
$value['options-cat_id'] += Select::typeOptions('select-cat', ',,'.$cat_app,$no_lang=true,false,$value['cat_id']);
Select::fix_encoded_options($value['options-cat_id']);
}
// Favorite group for admins
if($GLOBALS['egw_info']['apps']['admin'] && $value['favorites'])
{
self::$request->sel_options[$form_name]['favorite']['group'] = array('all' => lang('All users')) +
etemplate_widget_menupopup::typeOptions('select-account',',groups');
Select::typeOptions('select-account',',groups');
}
foreach($value as $name => &$_value)
{
@ -217,7 +228,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
{
self::$request->sel_options[$select] = array();
}
etemplate_widget_menupopup::fix_encoded_options($_value, TRUE);
Select::fix_encoded_options($_value, TRUE);
self::$request->sel_options[$select] += $_value;
// The client doesn't need them in content, but we can't unset them because
// some apps don't send them on re-load, pulling them from the session
@ -274,7 +285,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
static public function ajax_get_rows($exec_id, array $queriedRange, array $filters = array(), $form_name='nm',
array $knownUids=null, $lastModified=null)
{
self::$request = etemplate_request::read($exec_id);
self::$request = Etemplate\Request::read($exec_id);
// fix for somehow empty etemplate request content
if (!is_array(self::$request->content))
{
@ -289,7 +300,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
}
// Validate filters
if (($template = etemplate_widget_template::instance(self::$request->template['name'], self::$request->template['template_set'],
if (($template = Template::instance(self::$request->template['name'], self::$request->template['template_set'],
self::$request->template['version'], self::$request->template['load_via'])))
{
$template = $template->getElementById($form_name, strpos($form_name, 'history') === 0 ? 'historylog' : 'nextmatch');
@ -336,7 +347,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
if($app)
{
$GLOBALS['egw_info']['flags']['currentapp'] = $app;
translation::add_app($app);
Api\Translation::add_app($app);
}
// If specific data requested, just do that
if (($row_id = $value['row_id']) && $queriedRange['refresh'])
@ -346,7 +357,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
}
$rows = $result['data'] = $result['order'] = array();
$result['total'] = self::call_get_rows($value, $rows, $result['readonlys'], null, null, $template);
$result['lastModification'] = egw_time::to('now', 'ts')-1;
$result['lastModification'] = Api\DateTime::to('now', 'ts')-1;
if (isset($GLOBALS['egw_info']['flags']['app_header']) && self::$request->app_header != $GLOBALS['egw_info']['flags']['app_header'])
{
@ -371,11 +382,11 @@ class etemplate_widget_nextmatch extends etemplate_widget
$modified = $row[$row_modified];
if (isset($modified) && !(is_int($modified) || is_string($modified) && is_numeric($modified)))
{
$modified = egw_time::to(str_replace('Z', '', $modified), 'ts');
$modified = Api\DateTime::to(str_replace('Z', '', $modified), 'ts');
}
// check if we need to send the data
//error_log("$id Known: " . (array_search($id, $knownUids) !== false ? 'Yes' : 'No') . ' Modified: ' . egw_time::to($row[$row_modified]) . ' > ' . egw_time::to($lastModified).'? ' . ($row[$row_modified] > $lastModified ? 'Yes' : 'No'));
//error_log("$id Known: " . (array_search($id, $knownUids) !== false ? 'Yes' : 'No') . ' Modified: ' . Api\DateTime::to($row[$row_modified]) . ' > ' . Api\DateTime::to($lastModified).'? ' . ($row[$row_modified] > $lastModified ? 'Yes' : 'No'));
if (!$row_id || !$knownUids || ($kUkey = array_search($id, $knownUids)) === false ||
!$lastModified || !isset($modified) || $modified > $lastModified)
{
@ -392,7 +403,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
{
foreach($row as &$options)
{
etemplate_widget_menupopup::fix_encoded_options($options,true);
Select::fix_encoded_options($options,true);
}
}
$result['rows'][$n] = $row;
@ -536,10 +547,10 @@ class etemplate_widget_nextmatch extends etemplate_widget
* @param array &$readonlys =null
* @param object $obj =null (internal)
* @param string|array $method =null (internal)
* @param etemplate_widget $widget =null instanciated nextmatch widget to let it's widgets transform each row
* @param Etemplate\Widget $widget =null instanciated nextmatch widget to let it's widgets transform each row
* @return int|boolean total items found of false on error ($value['get_rows'] not callable)
*/
private static function call_get_rows(array &$value,array &$rows,array &$readonlys=null,$obj=null,$method=null, etemplate_widget $widget=null)
private static function call_get_rows(array &$value,array &$rows,array &$readonlys=null,$obj=null,$method=null, Etemplate\Widget $widget=null)
{
if (is_null($method)) $method = $value['get_rows'];
@ -597,7 +608,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
$row_template = $widget->getElementById($widget->attrs['template']);
if(!$row_template)
{
$row_template = etemplate_widget_template::instance($widget->attrs['template']);
$row_template = Template::instance($widget->attrs['template']);
}
// Try to find just the repeating part
@ -637,7 +648,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
$_row = array(1 => &$row);
$repeating_row->run('set_row_value', array('',array('row' => 1), &$_row), true);
}
else if (!$widget || get_class($widget) != 'etemplate_widget_historylog')
else if (!$widget || get_class($widget) != __NAMESPACE__.'\\HistoryLog')
{
// Fallback based on widget names
//error_log(self::$request->template['name'] . ' had to fallback to run_beforeSendToClient() because it could not find the row');
@ -651,7 +662,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
{
foreach($row as &$options)
{
etemplate_widget_menupopup::fix_encoded_options($options, true);
Select::fix_encoded_options($options, true);
}
}
$rows[$n] = $row;
@ -681,7 +692,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
(is_int($value) || is_string($value) && is_numeric($value)) &&
($value > 21000000 || $value < 19000000))
{
$value = egw_time::to($value, 'Y-m-d\TH:i:s\Z');
$value = Api\DateTime::to($value, 'Y-m-d\TH:i:s\Z');
}
}
return $row;
@ -694,7 +705,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
*/
private static function get_timestamps()
{
return egw_cache::getTree(__CLASS__, 'timestamps', function()
return Api\Cache::getTree(__CLASS__, 'timestamps', function()
{
$timestamps = array();
foreach(scandir(EGW_SERVER_ROOT) as $app)
@ -1112,7 +1123,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
{
if($this->attrs[$sub_template])
{
$row_template = etemplate_widget_template::instance($this->attrs[$sub_template]);
$row_template = Template::instance($this->attrs[$sub_template]);
$row_template->run($method_name, $params, $respect_disabled);
}
}
@ -1143,31 +1154,31 @@ class etemplate_widget_nextmatch extends etemplate_widget
{
unset($row_ids, $type); // not used, but required by function signature
throw new Exception('Not yet implemented');
throw new Api\Exception('Not yet implemented');
}
}
// Registration needs to go here, otherwise customfields won't be loaded until some other cf shows up
etemplate_widget::registerWidget('etemplate_widget_customfields', array('nextmatch-customfields'));
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Customfields', array('nextmatch-customfields'));
/**
* Extend selectbox so select options get parsed properly before being sent to client
*/
class etemplate_widget_nextmatch_filterheader extends etemplate_widget_menupopup
class NextmatchFilterHeader extends Select
{
}
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\NextmatchFilterHeader', array('nextmatch-filterheader'));
/**
* Extend selectbox and change type so proper users / groups get loaded, according to preferences
*/
class etemplate_widget_nextmatch_accountfilter extends etemplate_widget_menupopup
class NextmatchAccountFilter extends Select
{
/**
* Parse and set extra attributes from xml in template object
*
* @param string|XMLReader $xml
* @param boolean $cloned =true true: object does NOT need to be cloned, false: to set attribute, set them in cloned object
* @return etemplate_widget_template current object or clone, if any attribute was set
*/
public function set_attrs($xml, $cloned=true)
{
@ -1176,11 +1187,12 @@ class etemplate_widget_nextmatch_accountfilter extends etemplate_widget_menupopu
$this->attrs['type'] = 'select-account';
}
}
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\NextmatchAccountFilter', array('nextmatch-accountfilter'));
/**
* A filter widget that fakes another (select) widget and turns it into a nextmatch filter widget.
*/
class etemplate_widget_nextmatch_customfilter extends etemplate_widget_transformer
class NextmatchCustomFilter extends Transformer
{
protected $legacy_options = 'type,widget_options';
@ -1202,7 +1214,7 @@ class etemplate_widget_nextmatch_customfilter extends etemplate_widget_transform
list($type) = explode('-',$this->attrs['type']);
if($type == 'select')
{
if(in_array($this->attrs['type'], etemplate_widget_menupopup::$cached_types))
if(in_array($this->attrs['type'], Select::$cached_types))
{
$widget_type = $this->attrs['type'];
}
@ -1222,4 +1234,4 @@ class etemplate_widget_nextmatch_customfilter extends etemplate_widget_transform
parent::beforeSendToClient($cname, $expand);
}
}
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\NextmatchCustomFilter', array('nextmatch-customfilter'));

View File

@ -3,20 +3,30 @@
* EGroupware - eTemplate serverside select widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-14 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
// explicitly import old not yet ported classes
use categories;
use calendar_timezones;
use egw_json_response;
/**
* eTemplate select widget
*
* @todo unavailable cats (eg. private cats of an other user) need to be preserved!
*/
class etemplate_widget_menupopup extends etemplate_widget
class Select extends Etemplate\Widget
{
/**
* If the selectbox has this many rows, give it a search box automatically
@ -85,7 +95,7 @@ class etemplate_widget_menupopup extends etemplate_widget
*
* @param string|XMLReader $xml
* @param boolean $cloned =true true: object does NOT need to be cloned, false: to set attribute, set them in cloned object
* @return etemplate_widget_template current object or clone, if any attribute was set
* @return Template current object or clone, if any attribute was set
* @todo Use legacy_attributes instead of leaving it to typeOptions method to parse them
*/
public function set_attrs($xml, $cloned=true)
@ -127,11 +137,11 @@ class etemplate_widget_menupopup extends etemplate_widget
{
$value = $value_in = self::get_array($content, $form_name);
$allowed = self::selOptions($form_name, true); // true = return array of option-values
$allowed2 = self::selOptions($form_name, true); // true = return array of option-values
$type_options = self::typeOptions($this,
// typeOptions thinks # of rows is the first thing in options
($this->attrs['rows'] && strpos($this->attrs['options'], $this->attrs['rows']) !== 0 ? $this->attrs['rows'].','.$this->attrs['options'] : $this->attrs['options']));
$allowed = array_merge($allowed,array_keys($type_options));
$allowed = array_merge($allowed2,array_keys($type_options));
if (!$this->attrs['multiple'] || !($this->attrs['options'] > 1)) $allowed[] = '';
@ -467,7 +477,7 @@ class etemplate_widget_menupopup extends etemplate_widget
/**
* Fetch options for certain select-box types
*
* @param string|etemplate_widget_menupopup $widget_type Type of widget, or actual widget to get attributes since $legacy_options are legacy
* @param string|Select $widget_type Type of widget, or actual widget to get attributes since $legacy_options are legacy
* @param string $_legacy_options options string of widget
* @param boolean $no_lang =false initial value of no_lang attribute (some types set it to true)
* @param boolean $readonly =false for readonly we dont need to fetch all options, only the one for value
@ -717,7 +727,7 @@ class etemplate_widget_menupopup extends etemplate_widget
break;
case 'select-lang':
$options = translation::list_langs();
$options = Api\Translation::list_langs();
$no_lang = True;
break;
@ -732,7 +742,7 @@ class etemplate_widget_menupopup extends etemplate_widget
}
else
{
$options = $type ? egw_time::getTimezones() : egw_time::getUserTimezones($value);
$options = $type ? Api\DateTime::getTimezones() : Api\DateTime::getUserTimezones($value);
}
break;
}
@ -825,7 +835,7 @@ class etemplate_widget_menupopup extends etemplate_widget
$info .= $acc['account_lid'];
break;
default: // use the phpgw default
$info = $GLOBALS['egw']->common->display_fullname($acc['account_lid'],
$info = Api\Accounts::format_username($acc['account_lid'],
$acc['account_firstname'],$acc['account_lastname']);
break;
}
@ -855,5 +865,4 @@ class etemplate_widget_menupopup extends etemplate_widget
$response->data($options);
}
}
etemplate_widget::registerWidget('etemplate_widget_menupopup', array('selectbox','listbox','select','menupopup'));
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Select', array('selectbox', 'listbox', 'select', 'menupopup'));

View File

@ -3,14 +3,18 @@
* EGroupware - eTemplate serverside Tabs widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2013 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
/**
* eTemplate Tabs widget stacks multiple sub-templates and lets you switch between them
*
@ -25,7 +29,7 @@
* + id: optinal namespace (content attribute of template)
* + add_tabs: true(default) add to given tabs to template, false replace tabs in template
*/
class etemplate_widget_tabbox extends etemplate_widget
class Tabbox extends Etemplate\Widget
{
/**
* Run a given method on all children
@ -60,10 +64,10 @@ class etemplate_widget_tabbox extends etemplate_widget
//$this->tabs = array();
foreach($tabs as &$tab)
{
$template= clone etemplate_widget_template::instance($tab['template']);
$template= clone Template::instance($tab['template']);
if($tab['id']) $template->attrs['content'] = $tab['id'];
$this->children[1]->children[] = $template;
$tab['url'] = etemplate_widget_template::rel2url($template->rel_path);
$tab['url'] = Template::rel2url($template->rel_path);
//$this->tabs[] = $tab;
unset($template);
}
@ -112,4 +116,3 @@ class etemplate_widget_tabbox extends etemplate_widget
}
}
}
etemplate_widget::registerWidget('etemplate_widget_tabbox', array('tabbox'));

View File

@ -3,18 +3,28 @@
* EGroupware - eTemplate serverside of tag list widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2013 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
// explicitly import old not yet ported classes
use egw_json_request;
use common;
use mail_compose;
/**
* eTemplate tag list widget
*/
class etemplate_widget_taglist extends etemplate_widget
class Taglist extends Etemplate\Widget
{
/**
* Constructor
@ -38,7 +48,8 @@ class etemplate_widget_taglist extends etemplate_widget
* Find entries that match query parameter (from link system) and format them
* as the widget expects, a list of {id: ..., label: ...} objects
*/
public static function ajax_search() {
public static function ajax_search()
{
$app = $_REQUEST['app'];
$type = $_REQUEST['type'];
$query = $_REQUEST['query'];
@ -50,12 +61,12 @@ class etemplate_widget_taglist extends etemplate_widget
if($query)
{
$options['account_type'] = $_REQUEST['account_type'];
$links = accounts::link_query($query, $options);
$links = Api\Accounts::link_query($query, $options);
}
}
else
{
$links = egw_link::query($app, $query, $options);
$links = Api\Link::query($app, $query, $options);
}
$results = array();
foreach($links as $id => $name)
@ -75,7 +86,8 @@ class etemplate_widget_taglist extends etemplate_widget
*
* Uses the mail application if available, or addressbook
*/
public static function ajax_email() {
public static function ajax_email()
{
// If no mail app access, use link system -> addressbook
if(!$GLOBALS['egw_info']['apps']['mail'])
{
@ -103,7 +115,7 @@ class etemplate_widget_taglist extends etemplate_widget
if (!$this->is_readonly($cname, $form_name))
{
$value = $value_in = self::get_array($content, $form_name);
$allowed = etemplate_widget_menupopup::selOptions($form_name);
$allowed = Select::selOptions($form_name);
foreach((array) $value as $key => $val)
{
@ -120,7 +132,7 @@ class etemplate_widget_taglist extends etemplate_widget
self::set_validation_error($form_name,lang("'%1' is NOT allowed ('%2')!",$val,implode("','",array_keys($lists))),'');
}
}
else if($this->type == 'taglist-email' && !preg_match(etemplate_widget_url::EMAIL_PREG, $val) &&
else if($this->type == 'taglist-email' && !preg_match(Url::EMAIL_PREG, $val) &&
// Allow merge placeholders. Might be a better way to do this though.
!preg_match('/{{.+}}|\$\$.+\$\$/',$val)
)

View File

@ -3,15 +3,24 @@
* EGroupware - eTemplate serverside template widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
// allow to call direct for tests (see end of class)
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
use XMLReader;
// explicitly import old not yet ported classes
use egw; // link
/* allow to call direct for tests (see end of class)
if (!isset($GLOBALS['egw_info']))
{
$GLOBALS['egw_info'] = array(
@ -21,12 +30,12 @@ if (!isset($GLOBALS['egw_info']))
)
);
include_once '../../header.inc.php';
}
} */
/**
* eTemplate widget baseclass
*/
class etemplate_widget_template extends etemplate_widget
class Template extends Etemplate\Widget
{
/**
* Cache of already read templates
@ -49,12 +58,11 @@ class etemplate_widget_template extends etemplate_widget
* @param string $template_set =null default try template-set from user and if not found "default"
* @param string $version =''
* @param string $load_via ='' use given template to load $name
* @todo Reading customized templates from database
* @return etemplate_widget_template|boolean false if not found
* @return Template|boolean false if not found
*/
public static function instance($_name, $template_set=null, $version='', $load_via='')
{
if (html::$ua_mobile)
if (Api\Header\UserAgent::mobile())
{
$template_set = "mobile";
}
@ -107,7 +115,7 @@ class etemplate_widget_template extends etemplate_widget
{
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'template')
{
$template = new etemplate_widget_template($reader);
$template = new Template($reader);
$template->rel_path = $path;
//echo $template->id; _debug_array($template);
@ -148,7 +156,7 @@ class etemplate_widget_template extends etemplate_widget
$template_path = '/'.$app.'/templates/'.$template_set.'/'.$rest.'.xet';
$default_path = '/'.$app.'/templates/default/'.$rest.'.xet';
foreach(array(egw_vfs::PREFIX.self::VFS_TEMPLATE_PATH, EGW_SERVER_ROOT) as $prefix)
foreach(array(Api\Vfs::PREFIX.self::VFS_TEMPLATE_PATH, EGW_SERVER_ROOT) as $prefix)
{
if (file_exists($prefix.$template_path))
{
@ -201,7 +209,7 @@ class etemplate_widget_template extends etemplate_widget
}
else
{
$url = egw_vfs::download_url($path);
$url = Api\Vfs::download_url($path);
if ($url[0] == '/') $url = egw::link($url);
@ -238,7 +246,7 @@ class etemplate_widget_template extends etemplate_widget
//error_log("$this running $method_name() cname: {$this->id} -> expand_name: $expand_name");
if($expand_name && $expand_name != $this->id)
{
if (($row_template = etemplate_widget_template::instance($expand_name)))
if (($row_template = self::instance($expand_name)))
{
$row_template->run($method_name, $params, $respect_disabled);
}
@ -269,7 +277,7 @@ class etemplate_widget_template extends etemplate_widget
if ($GLOBALS['egw_info']['flags']['debug'] == 'etemplate_widget_template')
{
$name = isset($_GET['name']) ? $_GET['name'] : 'timesheet.edit';
if (!($template = etemplate_widget_template::instance($name)))
if (!($template = Template::instance($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";

View File

@ -3,14 +3,19 @@
* EGroupware - eTemplate serverside textbox widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-13 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use XMLReader;
/**
* eTemplate textbox widget with following sub-types:
* - textbox with optional multiline="true" and rows="123"
@ -21,7 +26,7 @@
* - passwd (passwords are never send back to client, instead a number of asterisks is send and replaced again!)
* sub-types are either passed to constructor or set via 'type' attribute!
*/
class etemplate_widget_textbox extends etemplate_widget
class Textbox extends Etemplate\Widget
{
/**
* Constructor
@ -49,8 +54,8 @@ class etemplate_widget_textbox extends etemplate_widget
* Reimplemented to handle legacy read-only by setting size < 0
*
* @param string|XMLReader $xml
* @param boolean $cloned=true true: object does NOT need to be cloned, false: to set attribute, set them in cloned object
* @return etemplate_widget_template current object or clone, if any attribute was set
* @param boolean $cloned =true true: object does NOT need to be cloned, false: to set attribute, set them in cloned object
* @return Template current object or clone, if any attribute was set
*/
public function set_attrs($xml, $cloned=true)
{
@ -192,4 +197,4 @@ class etemplate_widget_textbox extends etemplate_widget
}
}
}
etemplate_widget::registerWidget('etemplate_widget_textbox', array('textbox','text','int','integer','float','passwd','hidden','colorpicker','hidden'));
Etemplate\Widget::registerWidget(__NAMESPACE__.'\\Textbox', array('textbox','text','int','integer','float','passwd','hidden','colorpicker','hidden'));

View File

@ -9,10 +9,14 @@
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
/**
* eTemplate button widget
*/
class etemplate_widget_toolbar extends etemplate_widget
class Toolbar extends Etemplate\Widget
{
/**
* Validate toolbar
@ -37,4 +41,3 @@ class etemplate_widget_toolbar extends etemplate_widget
}
}
}
etemplate_widget::registerWidget('etemplate_widget_toolbar', array('toolbar'));

View File

@ -0,0 +1,233 @@
<?php
/**
* EGroupware - eTemplate serverside base widget, to define new widgets using a transformation out of existing widgets
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
/**
* eTemplate serverside base widget, to define new widgets using a transformation out of existing widgets
*/
abstract class Transformer extends Etemplate\Widget
{
/**
* Array with a transformation description, based on attributes to modify.
*
* Exampels:
*
* * 'type' => array('some' => 'other')
* if 'type' attribute equals 'some' replace it with 'other'
*
* * 'type' => array('some' => array('type' => 'other', 'options' => 'otheroption')
* same as above, but additonally set 'options' attr to 'otheroption'
*
* --> leaf element is the action, if previous filters are matched:
* - if leaf is scalar, it just replaces the previous filter value
* - if leaf is an array, it contains assignments for (multiple) attributes: attr => value pairs
*
* * 'type' => array(
* 'some' => array(...),
* 'other' => array(...),
* '__default__' => array(...),
* )
* it's possible to have a list of filters with actions to run, plus a '__default__' which matches all not explicitly named values
*
* * 'value' => array('__callback__' => 'app.class.method' || 'class::method' || 'method')
* run value through a *serverside* callback, eg. reading an entry based on it's given id
* callback signature: mixed function(mixed $attr[, array $attrs])
*
* * 'value' => array('__js__' => 'function(value) { return value+5; }')
* run value through a *clientside* callback running in the context of the widget
*
* * 'name' => '@name[@options]'
* replace value of 'name' attribute with itself (@name) plus value of options in square brackets
* * 'value' => '@value[@options]'
* replace value array with value for key taken from value of options attribute
*
* --> attribute name prefixed with @ sign means value of given attribute
*
* @var array
*/
protected static $transformation = array();
/**
* Switching debug messages to error_log on/off
*
* @var boolean
*/
const DEBUG = false;
/**
* Fill type options in self::$request->sel_options to be used on the client
*
* @param string $cname
*/
public function beforeSendToClient($cname, array $expand=array())
{
$attrs = $this->attrs;
$form_name = self::form_name($cname, $this->id);
if (empty($this->id))
{
error_log(__METHOD__."() $this has no id!");
return;
}
$attrs['value'] = $value =& self::get_array(self::$request->content, $form_name, false, true);
$attrs['type'] = $this->type;
$attrs['id'] = $this->id;
$unmodified = $attrs;
// run the transformation
foreach(static::$transformation as $filter => $data)
{
$this->action($filter, $data, $attrs);
}
//echo $this; _debug_array($unmodified); _debug_array($attrs); _debug_array(array_diff_assoc($attrs, $unmodified));
// compute the difference and send it to the client as modifications
$type_changed = false;
foreach(array_diff_assoc($attrs, $unmodified) as $attr => $val)
{
switch($attr)
{
case 'value':
if ($val != $value)
{
$value = $val; // $value is reference to self::$request->content
}
break;
case 'sel_options':
self::$request->sel_options[$form_name] = $val;
break;
case 'type': // not an attribute in etemplate2
$type_changed = true;
if($val == 'template')
{
// If the widget has been transformed into a template, we
// also need to try and instanciate & parse the template too
$transformed_template = Template::instance($attrs['template']);
if($transformed_template)
{
$this->expand_widget($transformed_template, $expand);
$transformed_template->run('beforeSendToClient',array($cname,$expand));
}
$type_changed = false;
}
default:
self::setElementAttribute($form_name, $attr, $val);
break;
}
}
if($type_changed)
{
// Run the new widget type's beforeSendToClient
$expanded_child = self::factory($attrs['type'], false,$this->id);
$expanded_child->id = $this->id;
$expanded_child->type = $attrs['type'];
$expanded_child->attrs = $attrs;
$expanded_child->run('beforeSendToClient',array($cname,$expand));
}
}
/**
* Recursively run given action(s) on an attribute value
*
* @param string $attr attribute concerned
* @param int|string|array $action action to run
* @param array &$attrs attributes
* @throws Api\Exception\WrongParameter if $action is of wrong type
*/
protected function action($attr, $action, array &$attrs)
{
if (self::DEBUG) error_log(__METHOD__."('$attr', ".array2string($action).')');
// action is an assignment
if (is_scalar($action) || is_null($action))
{
// check if assignment contains placeholders --> replace them
if (strpos($action, '@') !== false)
{
$replace = array();
foreach($attrs as $a => $v)
{
if (is_scalar($v) || is_null($v)) $replace['@'.$a] = $v;
}
$action = strtr($action, $replace);
// now replace with non-scalar value, eg. if values is an array: "@value", "@value[key] or "@value[@key]"
if (($a = strstr($action, '@')))
{
$action = self::get_array($attrs, substr($a,1));
}
}
$attrs[$attr] = $action;
if (self::DEBUG) error_log(__METHOD__."('$attr', ".array2string($action).") attrs['$attr'] = ".array2string($action).', attrs='.array2string($attrs));
}
// action is a serverside callback
elseif(is_array($action) && isset($action['__callback__']))
{
if (!is_string(($callback = $action['__callback__'])))
{
throw new Api\Exception\WrongParameter(__METHOD__."('$attr', ".array2string($action).', '.array2string($attrs).') wrong datatype for callback!');
}
if (method_exists($this, $callback))
{
$attrs[$attr] = $this->$callback($attrs[$attr], $attrs);
}
elseif(count(explode('.', $callback)) == 3)
{
$attrs[$attr] = ExecMethod($callback, $attrs[$attr], $attrs);
}
elseif (is_callable($callback, false))
{
$attrs[$attr] = call_user_func($callback, $attrs[$attr], $attrs);
}
else
{
throw new Api\Exception\WrongParameter(__METHOD__."('$attr', ".array2string($action).', '.array2string($attrs).') wrong datatype for callback!');
}
}
// action is a clientside callback
elseif(is_array($action) && isset($action['__js__']))
{
// nothing to do here
}
// TODO: Might be a better way to handle when value to be set is an array
elseif(is_array($action) && $attr == 'sel_options')
{
$attrs[$attr] = $action;
}
// action is a switch --> check cases
elseif(is_array($action))
{
// case matches --> run all actions
if (isset($action[$attrs[$attr]]) || !isset($action[$attrs[$attr]]) && isset($action['__default__']))
{
$actions = isset($action[$attrs[$attr]]) ? $action[$attrs[$attr]] : $action['__default__'];
if(!is_array($actions))
{
$attrs[$attr] = $actions;
$actions = array($attr => $actions);
}
if (self::DEBUG) error_log(__METHOD__."(attr='$attr', action=".array2string($action).") attrs['$attr']=='{$attrs[$attr]}' --> running actions");
foreach($actions as $attr => $action)
{
$this->action($attr, $action, $attrs);
}
}
}
else
{
throw new Api\Exception\WrongParameter(__METHOD__."(attr='$attr', action=".array2string($action).', attrs='.array2string($attrs).') wrong datatype for action!');
}
}
}

View File

@ -3,14 +3,25 @@
* EGroupware - eTemplate serverside tree widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @package api
* @subpackage etemplate
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2012 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
// explicitly import old not yet ported classes
use egw_json_request;
use common; // egw_exit
use categories;
use egw_framework;
egw_framework::includeCSS('/phpgwapi/js/dhtmlxtree/codebase/dhtmlXTree.css');
/**
@ -20,7 +31,7 @@ egw_framework::includeCSS('/phpgwapi/js/dhtmlxtree/codebase/dhtmlXTree.css');
*
* Example initialisation of tree via $sel_options array:
*
* use \etemplate_widget_tree as tree;
* use Api\Etemplate\Widget\Tree as tree;
*
* $sel_options['tree'] = array(
* tree::ID => 0, tree::CHILDREN => array( // ID of root has to be 0!
@ -51,11 +62,11 @@ egw_framework::includeCSS('/phpgwapi/js/dhtmlxtree/codebase/dhtmlXTree.css');
* - you can use attribute "std_images" to supply different standard images from default
* [ "leaf.gif", "folderOpen.gif", "folderClosed.gif" ]
* - images can also be specified as standard "app/image" string, client-side will convert them to url relativ to image_path
* - json autoloading uses identical data-structur and should use etemplate_widget_tree::send_quote_json($data)
* - json autoloading uses identical data-structur and should use Api\Etemplate\Widget\Tree::send_quote_json($data)
* to send data to client, as it takes care of html-encoding of node text
* - if autoloading is enabled, you have to validate returned results yourself, as widget does not know (all) valid id's
*/
class etemplate_widget_tree extends etemplate_widget
class Tree extends Etemplate\Widget
{
/**
* key for id of node, has to be unique, eg. a path, nummerical id is allowed too
@ -115,7 +126,7 @@ class etemplate_widget_tree extends etemplate_widget
*
* @param string|XMLReader $xml
* @param boolean $cloned =true true: object does NOT need to be cloned, false: to set attribute, set them in cloned object
* @return etemplate_widget_template current object or clone, if any attribute was set
* @return Etempalte\Widget current object or clone, if any attribute was set
*/
public function set_attrs($xml, $cloned=true)
{
@ -157,7 +168,7 @@ class etemplate_widget_tree extends etemplate_widget
*/
public static function htmlencode_node(array $item)
{
$item['text'] = html::htmlspecialchars($item['text']);
$item['text'] = Api\Html::htmlspecialchars($item['text']);
if (isset($item['item']) && is_array($item['item']))
{
@ -198,7 +209,8 @@ class etemplate_widget_tree extends etemplate_widget
*/
public static function in_cats($id, array $cats)
{
return (boolean)array_filter($cats, function($cat) use($id){
return (boolean)array_filter($cats, function($cat) use($id)
{
return $cat['id'] == $id;
});
}

View File

@ -8,15 +8,19 @@
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @author Nathan Gray
* @copyright 2002-14 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @copyright 2012 Nathan Gray
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
/**
* eTemplate URL widget handles URLs, emails & phone numbers
*/
class etemplate_widget_url extends etemplate_widget
class Url extends Etemplate\Widget
{
/**
* Regexes for validating email addresses incl. email in angle-brackets eg.
@ -110,9 +114,8 @@ class etemplate_widget_url extends etemplate_widget
break;
}
}
$valid = $value;
if (true) $valid = $value;
//error_log(__METHOD__."() $form_name: ".array2string($value_in).' --> '.array2string($value));
}
}
}
etemplate_widget::registerWidget('etemplate_widget_url', array('url'));

View File

@ -11,17 +11,27 @@
* @version $Id$
*/
namespace EGroupware\Api\Etemplate\Widget;
use EGroupware\Api\Etemplate;
use EGroupware\Api;
// explicitly import old not yet ported classes
use egw_json_request;
use common; // egw_exit
use egw; // link
/**
* eTemplate VFS widget
* Deals with the Virtual File System
*/
class etemplate_widget_vfs extends etemplate_widget_file
class Vfs extends File
{
// Legacy option for vfs-upload
protected $legacy_options = "mime";
public function __construct($xml='') {
public function __construct($xml='')
{
if($xml) parent::__construct($xml);
}
@ -58,46 +68,46 @@ class etemplate_widget_vfs extends etemplate_widget_file
}
}
$value =& self::get_array(self::$request->content, $form_name, true);
$path = egw_link::vfs_path($app,$id,'',true);
$path = Api\Link::vfs_path($app,$id,'',true);
if (!empty($relpath)) $path .= '/'.$relpath;
if (true) $value = array();
// Single file, already existing
if (substr($path,-1) != '/' && egw_vfs::file_exists($path) && !egw_vfs::is_dir($path))
if (substr($path,-1) != '/' && Api\Vfs::file_exists($path) && !Api\Vfs::is_dir($path))
{
$file = egw_vfs::stat($path);
$file = Api\Vfs::stat($path);
$file['path'] = $path;
$file['name'] = egw_vfs::basename($file['path']);
$file['mime'] = egw_vfs::mime_content_type($file['path']);
$file['name'] = Api\Vfs::basename($file['path']);
$file['mime'] = Api\Vfs::mime_content_type($file['path']);
$value = array($file);
}
// Single file, missing extension in path
else if (substr($path, -1) != '/' && !egw_vfs::file_exists($path) && $relpath && substr($relpath,-4,1) !== '.')
else if (substr($path, -1) != '/' && !Api\Vfs::file_exists($path) && $relpath && substr($relpath,-4,1) !== '.')
{
$find = egw_vfs::find(substr($path,0, - strlen($relpath)), array(
$find = Api\Vfs::find(substr($path,0, - strlen($relpath)), array(
'type' => 'f',
'maxdepth' => 1,
'name' => $relpath . '*'
));
foreach($find as $file)
{
$file_info = egw_vfs::stat($file);
$file_info = Api\Vfs::stat($file);
$file_info['path'] = $file;
$file_info['name'] = egw_vfs::basename($file_info['path']);
$file_info['mime'] = egw_vfs::mime_content_type($file_info['path']);
$file_info['name'] = Api\Vfs::basename($file_info['path']);
$file_info['mime'] = Api\Vfs::mime_content_type($file_info['path']);
$value[] = $file_info;
}
}
else if (substr($path, -1) == '/' && egw_vfs::is_dir($path))
else if (substr($path, -1) == '/' && Api\Vfs::is_dir($path))
{
$scan = egw_vfs::scandir($path);
$scan = Api\Vfs::scandir($path);
foreach($scan as $file)
{
$file_info = egw_vfs::stat("$path$file");
$file_info = Api\Vfs::stat("$path$file");
$file_info['path'] = "$path$file";
$file_info['name'] = egw_vfs::basename($file_info['path']);
$file_info['mime'] = egw_vfs::mime_content_type($file_info['path']);
$file_info['name'] = Api\Vfs::basename($file_info['path']);
$file_info['mime'] = Api\Vfs::mime_content_type($file_info['path']);
$value[] = $file_info;
}
}
@ -121,11 +131,11 @@ class etemplate_widget_vfs extends etemplate_widget_file
{
$request_id = urldecode($_REQUEST['request_id']);
$widget_id = $_REQUEST['widget_id'];
if(!self::$request = etemplate_request::read($request_id))
if(!self::$request = Etemplate\Request::read($request_id))
{
$error = lang("Could not read session");
}
elseif (!($template = etemplate_widget_template::instance(self::$request->template['name'], self::$request->template['template_set'],
elseif (!($template = Template::instance(self::$request->template['name'], self::$request->template['template_set'],
self::$request->template['version'], self::$request->template['load_via'])))
{
// Can't use callback
@ -143,10 +153,10 @@ class etemplate_widget_vfs extends etemplate_widget_file
// store temp. vfs-path like links to be able to move it to correct location after entry is stored
if (!$data['to_id'] || is_array($data['to_id']))
{
egw_link::link($data['to_app'], $data['to_id'], egw_link::VFS_APPNAME, array(
Api\Link::link($data['to_app'], $data['to_id'], Api\Link::VFS_APPNAME, array(
'name' => $_FILES['upload']['name'],
'type' => $_FILES['upload']['type'],
'tmp_name' => egw_vfs::PREFIX.$path,
'tmp_name' => Api\Vfs::PREFIX.$path,
));
self::$request->content = array_merge(self::$request->content, array($widget_id => $data));
}
@ -156,8 +166,8 @@ class etemplate_widget_vfs extends etemplate_widget_file
$file = array(
"uploaded" => (int)empty($error),
"fileName" => html::htmlspecialchars($_FILES['upload']['name']),
"url" => egw::link(egw_vfs::download_url($path)),
"fileName" => Api\Html::htmlspecialchars($_FILES['upload']['name']),
"url" => egw::link(Api\Vfs::download_url($path)),
"error" => array(
"message" => $error,
)
@ -184,10 +194,10 @@ class etemplate_widget_vfs extends etemplate_widget_file
foreach($links as $link)
{
$matches = null;
if (is_array($link) && preg_match('|^'.preg_quote(egw_vfs::PREFIX,'|').'('.preg_quote(self::get_temp_dir($app, ''), '|').'[^/]+)/|', $link['id']['tmp_name'], $matches))
if (is_array($link) && preg_match('|^'.preg_quote(Api\Vfs::PREFIX,'|').'('.preg_quote(self::get_temp_dir($app, ''), '|').'[^/]+)/|', $link['id']['tmp_name'], $matches))
{
$replace[substr($link['id']['tmp_name'], strlen(egw_vfs::PREFIX))] =
egw_link::vfs_path($app, $id, egw_vfs::basename($link['id']['tmp_name']), true);
$replace[substr($link['id']['tmp_name'], strlen(Api\Vfs::PREFIX))] =
Api\Link::vfs_path($app, $id, Api\Vfs::basename($link['id']['tmp_name']), true);
if (!in_array($matches[1], $remove_dir)) $remove_dir[] = $matches[1];
}
@ -198,7 +208,7 @@ class etemplate_widget_vfs extends etemplate_widget_file
// remove all dirs
foreach($remove_dir as $dir)
{
egw_vfs::remove($dir);
Api\Vfs::remove($dir);
}
}
return isset($old) && $old != $html;
@ -240,21 +250,21 @@ class etemplate_widget_vfs extends etemplate_widget_file
{
// add extension to path
$parts = explode('.',$filename);
if (($extension = array_pop($parts)) && mime_magic::ext2mime($extension)) // really an extension --> add it to path
if (($extension = array_pop($parts)) && Api\MimeMagic::ext2mime($extension)) // really an extension --> add it to path
{
$path .= '.'.$extension;
}
}
else // multiple upload with dir given (trailing slash)
{
$path .= egw_vfs::encodePathComponent($filename);
$path .= Api\Vfs::encodePathComponent($filename);
}
if (!egw_vfs::file_exists($dir = egw_vfs::dirname($path)) && !egw_vfs::mkdir($dir,null,STREAM_MKDIR_RECURSIVE))
if (!Api\Vfs::file_exists($dir = Api\Vfs::dirname($path)) && !Api\Vfs::mkdir($dir,null,STREAM_MKDIR_RECURSIVE))
{
self::set_validation_error($name,lang('Error create parent directory %1!',egw_vfs::decodePath($dir)));
self::set_validation_error($name,lang('Error create parent directory %1!',Api\Vfs::decodePath($dir)));
return false;
}
if (!copy($file['tmp_name'],egw_vfs::PREFIX.$path))
if (!copy($file['tmp_name'],Api\Vfs::PREFIX.$path))
{
self::set_validation_error($name,lang('Error copying uploaded file to vfs!'));
return false;
@ -295,7 +305,7 @@ class etemplate_widget_vfs extends etemplate_widget_file
//...
foreach($value as $tmp => $file)
{
if(egw_vfs::file_exists(self::get_vfs_path($id) . $relpath)) {}
if(Api\Vfs::file_exists(self::get_vfs_path($id) . $relpath)) {}
}*/
parent::validate($cname, $content, $validated);
break;
@ -317,10 +327,9 @@ class etemplate_widget_vfs extends etemplate_widget_file
}
else
{
$path = egw_link::vfs_path($app,$id,'',true);
$path = Api\Link::vfs_path($app,$id,'',true);
}
if (!empty($relpath)) $path .= '/'.$relpath;
return $path;
}
}
etemplate_widget::registerWidget('etemplate_widget_vfs', array('vfs-upload'));

View File

@ -922,7 +922,7 @@ class Base
// check if a db-internal name conversation necessary
if (!is_int($col) && ($c = array_search($col,$this->db_cols)))
{
$col = $c;
$col = $this->table_name . '.' . $c;
}
if(is_int($col))
{
@ -930,11 +930,11 @@ class Base
}
elseif ($val === "!''")
{
$db_filter[] = $this->table_name . '.' .$col." != ''";
$db_filter[] = $col." != ''";
}
else
{
$db_filter[$this->table_name . '.' .$col] = $val;
$db_filter[$col] = $val;
}
}
}

View File

@ -10,6 +10,8 @@
* @version $Id$
*/
use EGroupware\Api;
/**
* AJAX Select Widget
*
@ -357,7 +359,7 @@ class ajax_select_widget
$query['field_name'] = $base_id;
// Check for a provided list of values
if($request = etemplate_request::read($etemplate_id)) {
if(($request = Api\Etemplate\Request::read($etemplate_id))) {
$extension_data = $request->extension_data[$base_id];
if(is_array($extension_data) && $extension_data['values']) {
self::$static_values[$base_id] = $extension_data['values'];

View File

@ -4,13 +4,15 @@
*
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-11 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @version $Id$
*/
use EGroupware\Api;
/**
* Business Object for eTemplates, extending the Storage Object
*/
@ -64,7 +66,7 @@ class boetemplate extends soetemplate
*
* It's a static variable as etemplates can contain further etemplates (rendered by a different object)
*
* @var etemplate_request
* @var Api\Etemplate\Request
*/
static public $request;
@ -103,8 +105,8 @@ class boetemplate extends soetemplate
*
* @param string $disabled expression to check, eg. "!@var" for !$content['var']
* @param array $content the content-array in the context of the grid
* @param int $row=null to be able to use $row or $row_content in value of checks
* @param int $c=null to be able to use $row or $row_content in value of checks
* @param int $row =null to be able to use $row or $row_content in value of checks
* @param int $c =null to be able to use $row or $row_content in value of checks
* @return boolean true if the row/col is disabled or false if not
*/
protected function check_disabled($disabled,$content,$row=null,$c=null)
@ -346,7 +348,7 @@ class boetemplate extends soetemplate
* disables all cells with name == $name
*
* @param sting $name cell-name
* @param boolean $disabled=true disable or enable a cell, default true=disable
* @param boolean $disabled =true disable or enable a cell, default true=disable
* @return mixed number of changed cells or False, if none changed
*/
function disableElement($name,$disabled=True)
@ -388,7 +390,7 @@ class boetemplate extends soetemplate
* disables all cells with name == $name
*
* @param sting $name cell-name
* @param boolean $disabled=true disable or enable a cell, default true=disable
* @param boolean $disabled =true disable or enable a cell, default true=disable
* @return reference to attribute
* @deprecated use disableElement($name, $disabled=true)
*/
@ -405,7 +407,7 @@ class boetemplate extends soetemplate
* @param string $class name of css class (without the leading '.') or '' for no class
* @param string $valign alignment (top,middle,bottom) or '' for none
* @param boolean $disabled True or expression or False to disable or enable the row (Only the number 0 means dont change the attribute !!!)
* @param string $path='/0' default is the first widget in the tree of children
* @param string $path ='/0' default is the first widget in the tree of children
* @return false if $path is no grid or array(height,class,valign,disabled) otherwise
*/
function set_row_attributes($n,$height=0,$class=0,$valign=0,$disabled=0,$path='/0')
@ -432,8 +434,8 @@ class boetemplate extends soetemplate
* disables row $n
*
* @param int $n numerical row-number starting with 1 (!)
* @param boolean $enable=false can be used to re-enable a row if set to True
* @param string $path='/0' default is the first widget in the tree of children
* @param boolean $enable =false can be used to re-enable a row if set to True
* @param string $path ='/0' default is the first widget in the tree of children
*/
function disable_row($n,$enable=False,$path='/0')
{
@ -445,8 +447,8 @@ class boetemplate extends soetemplate
*
* @param int|string $c numerical column-number starting with 0 (!), or the char-code starting with 'A'
* @param string $width percent or pixel or '' for no height
* @param mixed $disabled=0 True or expression or False to disable or enable the column (Only the number 0 means dont change the attribute !!!)
* @param string $path='/0' default is the first widget in the tree of children
* @param mixed $disabled =0 True or expression or False to disable or enable the column (Only the number 0 means dont change the attribute !!!)
* @param string $path ='/0' default is the first widget in the tree of children
* @return false if $path specifies no grid or array(width,disabled) otherwise
*/
function set_column_attributes($c,$width=0,$disabled=0,$path='/0')
@ -473,7 +475,7 @@ class boetemplate extends soetemplate
*
* @param int|string $c numerical column-number starting with 0 (!), or the char-code starting with 'A'
* @param boolean $enable can be used to re-enable a column if set to True
* @param string $path='/0' default is the first widget in the tree of children
* @param string $path ='/0' default is the first widget in the tree of children
*/
function disable_column($c,$enable=False,$path='/0')
{
@ -782,7 +784,7 @@ class boetemplate extends soetemplate
* For the 3. Column in the 2. row of a grid which is the only widget in the children-tree it is eg.: "/0/2C"
*
* @param string $path path in the widget tree
* @param int $ancestor=0 0: widget itself, 1: parent, 2: grand-parent, ...
* @param int $ancestor =0 0: widget itself, 1: parent, 2: grand-parent, ...
* @return array referenz to the widget spezified or null, if it's not found
*/
function &get_widget_by_path($path,$ancestor=0)
@ -838,9 +840,9 @@ class boetemplate extends soetemplate
* - csv_split('"a""b,c",d') === array('a"b,c','d') // to escape enclosures double them!
*
* @param string $str
* @param int $num=null in how many parts to split maximal, parts over this number end up (unseparated) in the last part
* @param string $delimiter=','
* @param string $enclosure='"'
* @param int $num =null in how many parts to split maximal, parts over this number end up (unseparated) in the last part
* @param string $delimiter =','
* @param string $enclosure ='"'
* @return array
*/
static function csv_split($str,$num=null,$delimiter=',',$enclosure='"')
@ -908,8 +910,8 @@ class boetemplate extends soetemplate
/**
* stores the etemplate in the cache in egw_info
*
* @param boetemplate $tpl=null required parameter for static use!
* @param boolean $only_update_older=false true only update cache, if it contains an older template
* @param boetemplate $tpl =null required parameter for static use!
* @param boolean $only_update_older =false true only update cache, if it contains an older template
*/
public /*static*/ function store_in_cache(boetemplate $tpl=null, $only_update_older=false)
{

View File

@ -10,12 +10,14 @@
* @version $Id$
*/
use EGroupware\Api\Etemplate\Widget\Contact;
/**
* eTemplate Extension: Contact widget
*
* This widget can be used to fetch fields of a contact specified by contact-id
*/
class contact_widget extends etemplate_widget_entry
class contact_widget extends Contact
{
/**
* exported methods of this class
@ -38,158 +40,6 @@ class contact_widget extends etemplate_widget_entry
'contact-template' => 'Account template',
'contact-fields' => 'Contact fields',
);
/**
* Instance of the contacts class
*
* @var contacts
*/
private $contacts;
/**
* Array with a transformation description, based on attributes to modify.
*
* Exampels:
*
* * 'type' => array('some' => 'other')
* if 'type' attribute equals 'some' replace it with 'other'
*
* * 'type' => array('some' => array('type' => 'other', 'options' => 'otheroption')
* same as above, but additonally set 'options' attr to 'otheroption'
*
* --> leaf element is the action, if previous filters are matched:
* - if leaf is scalar, it just replaces the previous filter value
* - if leaf is an array, it contains assignments for (multiple) attributes: attr => value pairs
*
* * 'type' => array(
* 'some' => array(...),
* 'other' => array(...),
* '__default__' => array(...),
* )
* it's possible to have a list of filters with actions to run, plus a '__default__' which matches all not explicitly named values
*
* * 'value' => array('__callback__' => 'app.class.method' || 'class::method' || 'method')
* run value through a *serverside* callback, eg. reading an entry based on it's given id
*
* * 'value' => array('__js__' => 'function(value) { return value+5; }')
* run value through a *clientside* callback running in the context of the widget
*
* * 'name' => '@name[@options]'
* replace value of 'name' attribute with itself (@name) plus value of options in square brackets
*
* --> attribute name prefixed with @ sign means value of given attribute
*
* @var array
*/
protected static $transformation = array(
'type' => array(
'contact-fields' => array( // contact-fields widget
'sel_options' => array('__callback__' => 'get_contact_fields'),
'type' => 'select',
'no_lang' => true,
'options' => 'None',
),
'contact-template' => array(
'type' => 'template',
'options' => '',
'template' => array('__callback__' => 'parse_template'),
),
'__default__' => array(
'options' => array(
'bday' => array('type' => 'date', 'options' => 'Y-m-d'),
'owner' => array('type' => 'select-account', 'options' => ''),
'modifier' => array('type' => 'select-account', 'options' => ''),
'creator' => array('type' => 'select-account', 'options' => ''),
'modifed' => array('type' => 'date-time', 'options' => ''),
'created' => array('type' => 'date-time', 'options' => ''),
'cat_id' => array('type' => 'select-cat', 'options' => ''),
'__default__' => array('type' => 'label', 'options' => ''),
),
'no_lang' => 1,
),
),
);
/**
* Constructor of the extension
*
* @param string $xml or 'html' for old etemplate
*/
public function __construct($xml)
{
if (is_a($xml, 'XMLReader') || $xml != '' && $xml != 'html')
{
parent::__construct($xml);
}
$this->contacts = $GLOBALS['egw']->contacts;
}
/**
* Legacy support for putting the template name in 'label' param
* @param string $label
* @param array $attrs
*/
public function parse_template($template, &$attrs)
{
return sprintf($template ? $template : $attrs['label'], $attrs['value']);
}
/**
* Get all contact-fields
*
* @return array
*/
public function get_contact_fields()
{
translation::add_app('addressbook');
$this->contacts->__construct();
$options = $this->contacts->contact_fields;
foreach($this->contacts->customfields as $name => $data)
{
$options['#'.$name] = $data['label'];
}
return $options;
}
public function get_entry($value, array $attrs)
{
return $this->get_contact($value, $attrs);
}
/**
* Get contact data, if $value not already contains them
*
* @param int|string|array $value
* @param array $attrs
* @return array
*/
public function get_contact($value, array $attrs)
{
$field = $attrs['field'] ? $attrs['field'] : '';
if (is_array($value) && !(array_key_exists('app',$value) && array_key_exists('id', $value))) return $value;
if(is_array($value) && array_key_exists('app', $value) && array_key_exists('id', $value)) $value = $value['id'];
switch($attrs['type'])
{
case 'contact-account':
case 'contact-template':
if (substr($value,0,8) != 'account:')
{
$value = 'account:'.($attrs['name'] != 'account:' ? $value : $GLOBALS['egw_info']['user']['account_id']);
}
// fall-through
case 'contact-value':
default:
if (substr($value,0,12) == 'addressbook:') $value = substr($value,12); // link-entry syntax
if (!($contact = $this->contacts->read($value)))
{
$contact = array();
}
break;
}
unset($contact['jpegphoto']); // makes no sense to return binary image
//error_log(__METHOD__."('$value') returning ".array2string($contact));
return $contact;
}
/**
* pre-processing of the extension
@ -206,6 +56,8 @@ class contact_widget extends etemplate_widget_entry
*/
function pre_process($name,&$value,&$cell,&$readonlys,&$extension_data,&$tmpl)
{
unset($readonlys, $extension_data); // not used, but required by function signature
//echo "<p>contact_widget::pre_process('$name','$value',".print_r($cell,true).",...)</p>\n";
switch($type = $cell['type'])
{
@ -285,5 +137,3 @@ class contact_widget extends etemplate_widget_entry
return True; // extra label ok
}
}
// register widgets for etemplate2
etemplate_widget::registerWidget('contact_widget',array('contact-value', 'contact-account', 'contact-template', 'contact-fields'));

View File

@ -5,12 +5,14 @@
* @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
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @package etemplate
* @subpackage api
* @version $Id$
*/
use EGroupware\Api;
/**
* creates dialogs / HTML-forms from eTemplate descriptions
*
@ -132,7 +134,7 @@ class etemplate extends boetemplate
* 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
* @param string|array $load_via with keys of other etemplate to load in order to get $name
*/
function __construct($name='',$load_via='')
{
@ -160,7 +162,7 @@ class etemplate extends boetemplate
* 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
* @param string|array $params url or array with get-params incl. menuaction
*/
static function location($params='')
{
@ -231,7 +233,7 @@ class etemplate extends boetemplate
}
self::$name_forms[] = self::$name_form;
self::$request = etemplate_request::read();
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;
@ -356,8 +358,8 @@ class etemplate extends boetemplate
/**
* 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
* @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)
@ -381,8 +383,8 @@ class etemplate extends boetemplate
/**
* 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 $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
*/
@ -415,7 +417,7 @@ class etemplate extends boetemplate
if(!$exec) $exec = $_POST;
//echo "process_exec: _POST ="; _debug_array($_POST);
if (!$etemplate_exec_id || !(self::$request = etemplate_request::read($etemplate_exec_id)))
if (!$etemplate_exec_id || !(self::$request = Api\Etemplate\Request::read($etemplate_exec_id)))
{
if ($this->sitemgr) return false;
//echo "uitemplate::process_exec() id='$_POST[etemplate_exec_id]' invalid session-data !!!"; _debug_array($_SESSION);
@ -612,7 +614,7 @@ class etemplate extends boetemplate
function process_values2url()
{
//echo "process_exec: _GET ="; _debug_array($_GET);
if (!$_GET['etemplate_exec_id'] || !($request = etemplate_request::read($_GET['etemplate_exec_id'])))
if (!$_GET['etemplate_exec_id'] || !($request = Api\Etemplate\Request::read($_GET['etemplate_exec_id'])))
{
return false;
}
@ -705,7 +707,7 @@ class etemplate extends boetemplate
*
* For multiple cats, the first with a color is used
*
* @param int/string $cats multiple comma-separated cat_id's
* @param int|string $cats multiple comma-separated cat_id's
* @return string
*/
static function cats2color($cats)
@ -2009,8 +2011,8 @@ class etemplate extends boetemplate
* (If no id is directly supplied internally.)
*
* @param string $form_name
* @param string $name=null
* @param string $id=null
* @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)
@ -2038,8 +2040,8 @@ class etemplate extends boetemplate
* --> 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
* @param int $num_decimal_places =2
* @param boolean $readonly =true
* @return string
*/
static public function number_format($number,$num_decimal_places=2,$readonly=true)
@ -2067,7 +2069,7 @@ class etemplate extends boetemplate
*
* @param array $cell
* @param string $name
* @param array $content=array();
* @param array $content =array();
* @return array
*/
function _sel_options($cell,$name,$content=array())
@ -2235,8 +2237,8 @@ class etemplate extends boetemplate
* @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
* @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')
@ -2484,7 +2486,7 @@ class etemplate extends boetemplate
*
* @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
* @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)
{

View File

@ -11,6 +11,8 @@
* @version $Id$
*/
use EGroupware\Api;
/**
* New eTemplate serverside contains:
* - main server methods like read, exec
@ -20,708 +22,6 @@
* - 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
* @deprecated use Api\Etemplate
*/
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='')
{
// we do NOT call parent consturctor, as we only want to enherit it's (static) methods
if (false) parent::__construct ($name); // satisfy IDE, as we dont call parent constructor
$this->sitemgr = isset($GLOBALS['Common_BO']) && is_object($GLOBALS['Common_BO']);
if ($name) $this->read($name,$template='default','default',0,'',$load_via);
// generate new etemplate request object, if not already existing
if(!isset(self::$request)) self::$request = etemplate_request::read();
}
/**
* 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
* 4 = json response
* @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)
{
$hook_data = $GLOBALS['egw']->hooks->process(
array('hook_location' => 'etemplate2_before_exec') +
array('location_name' => $this->name) +
array('location_object' => &$this) +
$content
);
foreach($hook_data as $extras)
{
if (!$extras) continue;
foreach(isset($extras[0]) ? $extras : array($extras) as $extra)
{
if ($extra['data'] && is_array($extra['data']))
{
$content = array_merge($content, $extra['data']);
}
if ($extra['preserve'] && is_array($extra['preserve']))
{
$preserv = array_merge($preserv, $extra['preserve']);
}
if ($extra['readonlys'] && is_array($extra['readonlys']))
{
$readonlys = array_merge($readonlys, $extra['readonlys']);
}
}
}
unset($hook_data);
// 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!");
if ($output_mode == 4)
{
$output_mode = 0;
self::$response = egw_json_response::get();
}
self::$request->output_mode = $output_mode; // let extensions "know" they are run eg. in a popup
self::$request->content = self::$cont = $content;
self::$request->changes = $changes;
self::$request->sel_options = is_array($sel_options) ? self::fix_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);
if (!$template) throw new egw_exception_assertion_failed("Template $this->name not instanciable! Maybe you forgot to rename template id.");
translation::add_app('etemplate');
$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
$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, 'etag' => translation::etag($l_app, $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], 'etag' => translation::etag($l_app, 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->dom_id);
$load_array = array(
'name' => $this->name,
'url' => etemplate_widget_template::rel2url($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());
egw_framework::clear_extra(); // to not send/set it twice for multiple etemplates (eg. CRM view)
}
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);
}
// Category styles
categories::css($app);
// set action attribute for autocomplete form tag
// as firefox complains on about:balnk action, thus we have to literaly submit the form to a blank html
$form_action = "about:blank";
if (html::$user_agent == 'firefox' || html::$user_agent == 'safari') $form_action = $GLOBALS['egw_info']['server']['webserver_url'].'/etemplate/empty.html';
// check if we are in an ajax-exec call from jdots template (or future other tabbed templates)
if (isset($GLOBALS['egw']->framework->response))
{
$content = '<form target="egw_iframe_autocomplete_helper" action="'.$form_action.'" id="'.$dom_id.'" class="et2_container"></form>'."\n".
'<iframe name="egw_iframe_autocomplete_helper" style="width:0;height:0;position: absolute;visibility:hidden;"></iframe>';
// add server-side page-generation times
if($GLOBALS['egw_info']['user']['preferences']['common']['show_generation_time'])
{
$vars = $GLOBALS['egw']->framework->_get_footer();
$content .= "\n".$vars['page_generation_time'];
}
$GLOBALS['egw']->framework->response->generic("data", array($content));
$GLOBALS['egw']->framework->response->generic('et2_load',$load_array+egw_framework::get_extra());
egw_framework::clear_extra(); // to not send/set it twice for multiple etemplates (eg. CRM view)
self::$request = null;
return;
}
// let framework know, if we are a popup or not ('popup' not true, which is allways used by index.php!)
if (!isset($GLOBALS['egw_info']['flags']['nonavbar']) || is_bool($GLOBALS['egw_info']['flags']['nonavbar']))
{
$GLOBALS['egw_info']['flags']['nonavbar'] = $output_mode == 2 ? 'popup' : false;
}
echo $GLOBALS['egw']->framework->header();
if ($output_mode != 2 && !$GLOBALS['egw_info']['flags']['nonavbar'])
{
parse_navbar();
}
else // mark popups as such, by enclosing everything in div#popupMainDiv
{
echo '<div id="popupMainDiv" class="popupMainDiv">'."\n";
}
// Send any accumulated json responses - after flush to avoid sending the buffer as a response
if(egw_json_response::isJSONResponse())
{
$load_array['response'] = egw_json_response::get()->returnResult();
}
// <iframe> and <form> tags added only to get browser autocomplete handling working again
echo '<form target="egw_iframe_autocomplete_helper" action="'.$form_action.'" id="'.$dom_id.'" class="et2_container" data-etemplate="'.html::htmlspecialchars(egw_json_response::json_encode($load_array), true).'"></form>'."\n".
'<iframe name="egw_iframe_autocomplete_helper" style="width:0;height:0;position: absolute;visibility:hidden;"></iframe>';
if ($output_mode == 2)
{
echo "\n</div>\n";
echo $GLOBALS['egw']->framework->footer();
}
ob_flush();
}
self::$request = null;
}
/**
* Fix all sel_options, as etemplate_widget_menupopup::beforeSendToClient is not run for auto-repeated stuff not understood by server
*
* @param array $sel_options
* @return array
*/
static protected function fix_sel_options(array $sel_options)
{
foreach($sel_options as &$options)
{
if (!is_array($options)||empty($options)) continue;
foreach($options as $key => $value)
{
if (is_numeric($key) && (!is_array($value) || !isset($value['value'])))
{
etemplate_widget_menupopup::fix_encoded_options($options, true);
break;
}
}
}
return $sel_options;
}
/**
* Process via Ajax submitted content
*
* @param string $etemplate_exec_id
* @param array $_content
* @param boolean $no_validation
* @throws egw_exception_wrong_parameter
*/
static public function ajax_process_content($etemplate_exec_id, array $_content, $no_validation)
{
//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)
{
translation::add_app($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 ($no_validation)
{
self::$validation_errors = array();
}
elseif (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;
}
// tell request call to remove request, if it is not modified eg. by call to exec in callback
self::$request->remove_if_not_modified();
foreach($GLOBALS['egw']->hooks->process(array(
'hook_location' => 'etemplate2_before_process',
'location_name' => $template->id,
) + self::complete_array_merge(self::$request->preserv, $validated)) as $extras)
{
if (!$extras) continue;
foreach(isset($extras[0]) ? $extras : array($extras) as $extra)
{
if ($extra['data'] && is_array($extra['data']))
{
$validated = array_merge($validated, $extra['data']);
}
}
}
//error_log(__METHOD__."(,".array2string($content).')');
//error_log(' validated='.array2string($validated));
$content = ExecMethod(self::$request->method, self::complete_array_merge(self::$request->preserv, $validated));
$tcontent = is_array($content) ? $content :
self::complete_array_merge(self::$request->preserv, $validated);
$hook_data = $GLOBALS['egw']->hooks->process(
array(
'hook_location' => 'etemplate2_after_process',
'location_name' => $template->id
) + $tcontent);
unset($tcontent);
if (is_array($content))
{
foreach($hook_data as $extras)
{
if (!$extras) continue;
foreach(isset($extras[0]) ? $extras : array($extras) as $extra) {
if ($extra['data'] && is_array($extra['data'])) {
$content = array_merge($content, $extra['data']);
}
}
}
}
unset($hook_data);
if (isset($GLOBALS['egw_info']['flags']['java_script']))
{
// Strip out any script tags
$GLOBALS['egw_info']['flags']['java_script'] = preg_replace(array('/(<script[^>]*>)([^<]*)/is','/<\/script>/'),array('$2',''),$GLOBALS['egw_info']['flags']['java_script']);
self::$response->script($GLOBALS['egw_info']['flags']['java_script']);
//error_log($app .' added javascript to $GLOBALS[egw_info][flags][java_script] - use egw_json_response->script() instead.');
}
return $content;
}
/**
* Notify server that eT session/request is no longer needed, because user closed window
*
* @param string $_exec_id
*/
static public function ajax_destroy_session($_exec_id)
{
//error_log(__METHOD__."('$_exec_id')");
if (($request = etemplate_request::read($_exec_id)))
{
$request->remove_if_not_modified();
unset($request);
}
}
/**
* Process via POST submitted content
*/
static public function process_exec()
{
if (get_magic_quotes_gpc()) $_POST['value'] = stripslashes($_POST['value']);
$content = json_decode($_POST['value'],true);
if($content == null && $_POST['exec'])
{
// Old etemplate submit
error_log("Old etemplate submitted");
return ExecMethod('etemplate.etemplate.process_exec');
}
//error_log(__METHOD__."(".array2string($content).")");
self::$request = etemplate_request::read($_POST['etemplate_exec_id']);
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();
$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));
exit;
}
//error_log(__METHOD__."(,".array2string($content).')');
//error_log(' validated='.array2string($validated));
return ExecMethod(self::$request->method, self::complete_array_merge(self::$request->preserv, $validated));
}
public $name;
public $template_set;
public $version;
public $laod_via;
/**
*
* @var string If the template needs a div named other than the template name, this is it
*/
protected $dom_id;
/**
* 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 =null default try template-set from user and if not found "default"
* @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=null,$lang='default',$group=0,$version='',$load_via='')
{
// For mobile experience try to load custom mobile templates
if (html::$ua_mobile)
{
$template_set = "mobile";
}
unset($lang); unset($group); // not used, but in old signature
$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));
$this->dom_id = $name;
return (boolean)$this->rel_path;
}
/**
* Set the DOM ID for the etemplate div. If not set, it will be generated from the template name.
*
* @param string $new_id
*/
public function set_dom_id($new_id)
{
$this->dom_id = $new_id;
}
/**
* 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,
);
}
/**
* 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
* @deprecated use getElementAttribute($name, $attr)
*/
public function &get_cell_attribute($name,$attr)
{
return self::getElementAttribute($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 reference to attribute
* @deprecated use setElementAttribute($name, $attr, $val)
*/
public function &set_cell_attribute($name,$attr,$val)
{
return self::setElementAttribute($name, $attr, $val);
}
/**
* disables all cells with name == $name
*
* @param sting $name cell-name
* @param boolean $disabled =true disable or enable a cell, default true=disable
* @return reference to attribute
* @deprecated use disableElement($name, $disabled=true)
*/
public function disable_cells($name,$disabled=True)
{
return self::disableElement($name, $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
is_array($v) && count($v) == 0) // Empty array replacing non-empty
{
$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();
}
/**
* 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.
*
* @param int &$max_upload=null on return max. upload size in byte
* @return string
*/
static function max_upload_size_message(&$max_upload=null)
{
$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)";
}
/**
* Format a number according to user prefs with decimal and thousands separator (later only for readonly)
*
* @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=null,$thousands_separator=null;
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 '';
return number_format(str_replace(' ','',$number),$num_decimal_places,$dec_separator,$readonly ? $thousands_separator : '');
}
/**
* 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;
}
}
// Try to discover all widgets, as names don't always match tags (eg: listbox is in menupopup)
$files = scandir(EGW_INCLUDE_ROOT . '/etemplate/inc');
foreach($files as $filename)
{
if(strpos($filename, 'class.etemplate_widget') === 0)
{
try
{
include_once($filename);
}
catch(Exception $e)
{
error_log($e->getMessage());
}
}
}
// Use hook to load custom widgets from other apps
$widgets = $GLOBALS['egw']->hooks->process('etemplate2_register_widgets');
foreach($widgets as $app => $list)
{
if (is_array($list))
{
foreach($list as $class)
{
try
{
class_exists($class); // trigger autoloader
}
catch(Exception $e)
{
error_log($e->getMessage());
}
}
}
}
class etemplate_new extends Api\Etemplate {}

File diff suppressed because it is too large Load Diff

View File

@ -7,62 +7,19 @@
* @subpackage api
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-11 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
use EGroupware\Api\Etemplate\Widget\Transformer;
/**
* eTemplate serverside base widget, to define new widgets using a transformation out of existing widgets
*
* @deprecated use Api\Etemplate\Widget\Transformer
*/
abstract class etemplate_widget_transformer extends etemplate_widget
abstract class etemplate_widget_transformer extends Transformer
{
/**
* Array with a transformation description, based on attributes to modify.
*
* Exampels:
*
* * 'type' => array('some' => 'other')
* if 'type' attribute equals 'some' replace it with 'other'
*
* * 'type' => array('some' => array('type' => 'other', 'options' => 'otheroption')
* same as above, but additonally set 'options' attr to 'otheroption'
*
* --> leaf element is the action, if previous filters are matched:
* - if leaf is scalar, it just replaces the previous filter value
* - if leaf is an array, it contains assignments for (multiple) attributes: attr => value pairs
*
* * 'type' => array(
* 'some' => array(...),
* 'other' => array(...),
* '__default__' => array(...),
* )
* it's possible to have a list of filters with actions to run, plus a '__default__' which matches all not explicitly named values
*
* * 'value' => array('__callback__' => 'app.class.method' || 'class::method' || 'method')
* run value through a *serverside* callback, eg. reading an entry based on it's given id
* callback signature: mixed function(mixed $attr[, array $attrs])
*
* * 'value' => array('__js__' => 'function(value) { return value+5; }')
* run value through a *clientside* callback running in the context of the widget
*
* * 'name' => '@name[@options]'
* replace value of 'name' attribute with itself (@name) plus value of options in square brackets
* * 'value' => '@value[@options]'
* replace value array with value for key taken from value of options attribute
*
* --> attribute name prefixed with @ sign means value of given attribute
*
* @var array
*/
protected static $transformation = array();
/**
* Switching debug messages to error_log on/off
*
* @var boolean
*/
const DEBUG = false;
/**
* Rendering transformer widget serverside as an old etemplate extension
*
@ -78,6 +35,8 @@ abstract class etemplate_widget_transformer extends etemplate_widget
*/
public function pre_process($name,&$value,&$cell,&$readonlys,&$extension_data,&$tmpl)
{
unset($readonlys, $extension_data, $tmpl); // not used but required by function signature
$_value = $value;
$_cell = $cell;
$cell['value'] =& $value;
@ -93,167 +52,4 @@ abstract class etemplate_widget_transformer extends etemplate_widget
error_log(__METHOD__."('$name', ".(is_array($_value)?$_value['id']:$_value).", ".array2string($_cell).", ...) transformed to ".array2string($cell)." and value=".array2string($value));
return true;
}
/**
* Fill type options in self::$request->sel_options to be used on the client
*
* @param string $cname
*/
public function beforeSendToClient($cname, array $expand=array())
{
$attrs = $this->attrs;
$form_name = self::form_name($cname, $this->id);
if (empty($this->id))
{
error_log(__METHOD__."() $this has no id!");
return;
}
$attrs['value'] = $value =& self::get_array(self::$request->content, $form_name, false, true);
$attrs['type'] = $this->type;
$attrs['id'] = $this->id;
$unmodified = $attrs;
// run the transformation
foreach(static::$transformation as $filter => $data)
{
$this->action($filter, $data, $attrs);
}
//echo $this; _debug_array($unmodified); _debug_array($attrs); _debug_array(array_diff_assoc($attrs, $unmodified));
// compute the difference and send it to the client as modifications
$type_changed = false;
foreach(array_diff_assoc($attrs, $unmodified) as $attr => $val)
{
switch($attr)
{
case 'value':
if ($val != $value)
{
$value = $val; // $value is reference to self::$request->content
}
break;
case 'sel_options':
self::$request->sel_options[$form_name] = $val;
break;
case 'type': // not an attribute in etemplate2
$type_changed = true;
if($val == 'template')
{
// If the widget has been transformed into a template, we
// also need to try and instanciate & parse the template too
$transformed_template = etemplate_widget_template::instance($attrs['template']);
if($transformed_template)
{
$this->expand_widget($transformed_template, $expand);
$transformed_template->run('beforeSendToClient',array($cname,$expand));
}
$type_changed = false;
}
default:
self::setElementAttribute($form_name, $attr, $val);
break;
}
}
if($type_changed)
{
// Run the new widget type's beforeSendToClient
$expanded_child = self::factory($attrs['type'], false,$this->id);
$expanded_child->id = $this->id;
$expanded_child->type = $attrs['type'];
$expanded_child->attrs = $attrs;
$expanded_child->run('beforeSendToClient',array($cname,$expand));
}
}
/**
* Recursively run given action(s) on an attribute value
*
* @param string $attr attribute concerned
* @param int|string|array $action action to run
* @param array &$attrs attributes
* @throws egw_exception_wrong_parameter if $action is of wrong type
*/
protected function action($attr, $action, array &$attrs)
{
if (self::DEBUG) error_log(__METHOD__."('$attr', ".array2string($action).')');
// action is an assignment
if (is_scalar($action) || is_null($action))
{
// check if assignment contains placeholders --> replace them
if (strpos($action, '@') !== false)
{
$replace = array();
foreach($attrs as $a => $v)
{
if (is_scalar($v) || is_null($v)) $replace['@'.$a] = $v;
}
$action = strtr($action, $replace);
// now replace with non-scalar value, eg. if values is an array: "@value", "@value[key] or "@value[@key]"
if (($a = strstr($action, '@')))
{
$action = self::get_array($attrs, substr($a,1));
}
}
$attrs[$attr] = $action;
if (self::DEBUG) error_log(__METHOD__."('$attr', ".array2string($action).") attrs['$attr'] = ".array2string($action).', attrs='.array2string($attrs));
}
// action is a serverside callback
elseif(is_array($action) && isset($action['__callback__']))
{
if (!is_string(($callback = $action['__callback__'])))
{
throw new egw_exception_wrong_parameter(__METHOD__."('$attr', ".array2string($action).', '.array2string($attrs).') wrong datatype for callback!');
}
if (method_exists($this, $callback))
{
$attrs[$attr] = $this->$callback($attrs[$attr], $attrs);
}
elseif(count(explode('.', $callback)) == 3)
{
$attrs[$attr] = ExecMethod($callback, $attrs[$attr], $attrs);
}
elseif (is_callable($callback, false))
{
$attrs[$attr] = call_user_func($callback, $attrs[$attr], $attrs);
}
else
{
throw new egw_exception_wrong_parameter(__METHOD__."('$attr', ".array2string($action).', '.array2string($attrs).') wrong datatype for callback!');
}
}
// action is a clientside callback
elseif(is_array($action) && isset($action['__js__']))
{
// nothing to do here
}
// TODO: Might be a better way to handle when value to be set is an array
elseif(is_array($action) && $attr == 'sel_options')
{
$attrs[$attr] = $action;
}
// action is a switch --> check cases
elseif(is_array($action))
{
// case matches --> run all actions
if (isset($action[$attrs[$attr]]) || !isset($action[$attrs[$attr]]) && isset($action['__default__']))
{
$actions = isset($action[$attrs[$attr]]) ? $action[$attrs[$attr]] : $action['__default__'];
if(!is_array($actions))
{
$attrs[$attr] = $actions;
$actions = array($attr => $actions);
}
if (self::DEBUG) error_log(__METHOD__."(attr='$attr', action=".array2string($action).") attrs['$attr']=='{$attrs[$attr]}' --> running actions");
foreach($actions as $attr => $action)
{
$this->action($attr, $action, $attrs);
}
}
}
else
{
throw new egw_exception_wrong_parameter(__METHOD__."(attr='$attr', action=".array2string($action).', attrs='.array2string($attrs).') wrong datatype for action!');
}
}
}

View File

@ -7,10 +7,12 @@
* @subpackage extensions
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @copyright 2002-10 by RalfBecker@outdoor-training.de
* @copyright 2002-16 by RalfBecker@outdoor-training.de
* @version $Id$
*/
use EGroupware\Api;
/**
* eTemplate Extension: several widgets as user-interface for the link-class
*
@ -763,7 +765,7 @@ class link_widget
$search = $extra_array;
}
// open request
if ($etemplate_exec_id) $request = etemplate_request::read($etemplate_exec_id);
if ($etemplate_exec_id) $request = Api\Etemplate\Request::read($etemplate_exec_id);
$response = new xajaxResponse();
$options = array();
@ -844,7 +846,7 @@ class link_widget
static function ajax_get_types($app,$id_res,$etemplate_exec_id)
{
// open request
if ($etemplate_exec_id) $request = etemplate_request::read($etemplate_exec_id);
if ($etemplate_exec_id) $request = Api\Etemplate\Request::read($etemplate_exec_id);
$response = new xajaxResponse();
//$args = func_get_args(); $response->addAlert("link_widget::ajax_search('".implode("',\n'",$args)."')\n calling link->query( $app , $search )" );

View File

@ -161,7 +161,7 @@ class soetemplate
*
* @param string $type type of the widget
* @param string $name name of widget
* @param array $attributes=null array with further attributes
* @param array $attributes =null array with further attributes
* @return array the cell
*/
static function empty_cell($type='label',$name='',$attributes=null)
@ -1102,9 +1102,9 @@ class soetemplate
*
* Please note: as call_user_func_array does not return references, methods ($func is an array) can not either!!!
*
* @param string/array $func function to use or array($obj,'method')
* @param string|array $func function to use or array($obj,'method')
* @param mixed &$extra extra parameter passed to function
* @param string $path='/' start-path
* @param string $path ='/' start-path
* @return mixed return-value of func or null if nothing returned at all
*/
function &widget_tree_walk($func,&$extra,$path='/')
@ -1146,7 +1146,7 @@ class soetemplate
* a further widget with children.
*
* @param array $widget the widget(-tree) the function should be applied too
* @param string/array $func function to use or array($obj,'method')
* @param string|array $func function to use or array($obj,'method')
* @param mixed &$extra extra parameter passed to function
* @param string $path path of widget in the widget-tree
* @return mixed return-value of func or null if nothing returned at all