mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-21 23:43:17 +01:00
moving eT2 server-side to api
This commit is contained in:
parent
d8fe729ffd
commit
2f4c727f9d
727
api/src/Etemplate.php
Normal file
727
api/src/Etemplate.php
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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))
|
||||
{
|
@ -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'))))
|
||||
{
|
@ -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))
|
||||
{
|
@ -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)
|
919
api/src/Etemplate/Widget.php
Normal file
919
api/src/Etemplate/Widget.php
Normal 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 .= '['.implode('][',$name_parts).']';
|
||||
$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('[',']'), 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);
|
||||
}
|
||||
}
|
@ -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
|
||||
*
|
||||
@ -50,15 +52,15 @@ class etemplate_widget_ajax_select extends etemplate_widget_menupopup
|
||||
{
|
||||
$form_name = self::form_name($cname, $this->id, $expand);
|
||||
}
|
||||
|
||||
|
||||
// Make sure s, etc. are properly encoded when sent, and not double-encoded
|
||||
$options = (isset(self::$request->sel_options[$form_name]) ? $form_name : $this->id);
|
||||
if(is_array(self::$request->sel_options[$options]))
|
||||
{
|
||||
|
||||
|
||||
// Fix any custom options from application
|
||||
self::fix_encoded_options(self::$request->sel_options[$options],true);
|
||||
|
||||
|
||||
if(!self::$request->sel_options[$options])
|
||||
{
|
||||
unset(self::$request->sel_options[$options]);
|
||||
@ -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'));
|
136
api/src/Etemplate/Widget/Box.php
Normal file
136
api/src/Etemplate/Widget/Box.php
Normal 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'));
|
@ -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'));
|
@ -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'));
|
177
api/src/Etemplate/Widget/Contact.php
Normal file
177
api/src/Etemplate/Widget/Contact.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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,10 +296,10 @@ 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;
|
||||
|
||||
|
||||
case 'link-to':
|
||||
$widget->attrs['only_app'] = $field['type'];
|
||||
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;
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
31
api/src/Etemplate/Widget/Description.php
Normal file
31
api/src/Etemplate/Widget/Description.php
Normal 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';
|
||||
}
|
@ -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
|
||||
*/
|
||||
@ -64,7 +66,7 @@ abstract class etemplate_widget_entry extends etemplate_widget_transformer
|
||||
|
||||
$attrs['type'] = $this->type;
|
||||
$attrs['id'] = $this->id;
|
||||
|
||||
|
||||
$form_name = self::form_name($cname, $this->id);
|
||||
$data_id = $attrs['value'] ? self::form_name($cname, $attrs['value']) : self::form_name($cname, self::ID_PREFIX . $this->id);
|
||||
|
||||
@ -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');
|
@ -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,22 +245,27 @@ 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 {
|
||||
unlink($dir . "/" . $object);
|
||||
}
|
||||
}
|
||||
}
|
||||
reset($objects);
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
rmdir($dir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate input
|
||||
@ -303,4 +316,3 @@ class etemplate_widget_file extends etemplate_widget
|
||||
}
|
||||
}
|
||||
}
|
||||
etemplate_widget::registerWidget('etemplate_widget_file', array('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'));
|
@ -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())
|
||||
@ -83,7 +87,7 @@ class etemplate_widget_grid extends etemplate_widget_box
|
||||
$params[1] = $old_expand;
|
||||
return false; // return
|
||||
}
|
||||
|
||||
|
||||
if ($this->id) $cname = self::form_name($cname, $this->id, $expand);
|
||||
if ($expand['cname'] !== $cname && $cname)
|
||||
{
|
||||
@ -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'));
|
@ -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');
|
@ -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.'; ':'').
|
||||
'">​</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');
|
@ -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'));
|
@ -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
|
||||
@ -30,18 +38,18 @@ class etemplate_widget_itempicker extends etemplate_widget
|
||||
parent::__construct($xml);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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');
|
@ -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'],
|
@ -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'));
|
@ -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'));
|
@ -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'));
|
@ -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)
|
||||
)
|
@ -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";
|
@ -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'));
|
@ -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'));
|
233
api/src/Etemplate/Widget/Transformer.php
Normal file
233
api/src/Etemplate/Widget/Transformer.php
Normal 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!');
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
@ -97,17 +108,17 @@ class etemplate_widget_tree extends etemplate_widget
|
||||
* key of flag if folder is open, default folder is closed
|
||||
*/
|
||||
const OPEN = 'open';
|
||||
|
||||
|
||||
/**
|
||||
* key to check checkbox if exists (in case of three-state checkboxes values can be:0 unchecked- 1 - checked or -1 - unsure)
|
||||
*/
|
||||
const CHECKED = 'checked';
|
||||
|
||||
|
||||
/**
|
||||
* key to instruct the component not to render checkbox for the related item, optional
|
||||
*/
|
||||
const NOCHECKBOX = 'nocheckbox';
|
||||
|
||||
|
||||
/**
|
||||
* Parse and set extra attributes from xml in template object
|
||||
*
|
||||
@ -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;
|
||||
});
|
||||
}
|
@ -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'));
|
@ -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;
|
||||
@ -292,10 +302,10 @@ class etemplate_widget_vfs extends etemplate_widget_file
|
||||
if(!is_array($value)) $value = array();
|
||||
/* Check & skip files that made it asyncronously
|
||||
list($app,$id,$relpath) = explode(':',$this->id,3);
|
||||
//...
|
||||
//...
|
||||
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'));
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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'];
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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'));
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
@ -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!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 )" );
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user