fixed server-side valdation of autorepeated rows/columns

had to change signature of validate function to get information for autorepeating through
removed entity-encoding of square brackets, as they mess up validiation (havnt found any negative effects so far)
This commit is contained in:
Ralf Becker 2012-05-03 14:17:47 +00:00
parent a4b71c7d3a
commit 0d66dd98b7
17 changed files with 312 additions and 133 deletions

View File

@ -215,7 +215,10 @@ class etemplate_new extends etemplate_widget_template
throw new egw_exception_wrong_parameter('Can NOT read template '.array2string(self::$request->template)); throw new egw_exception_wrong_parameter('Can NOT read template '.array2string(self::$request->template));
} }
$validated = array(); $validated = array();
$template->run('validate', array('', $content, &$validated), true); // $respect_disabled=true: do NOT validate disabled widgets and children $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)) if (self::validation_errors(self::$request->ignore_validation))
{ {
@ -251,7 +254,10 @@ class etemplate_new extends etemplate_widget_template
translation::add_app($GLOBALS['egw_info']['flags']['currentapp']); translation::add_app($GLOBALS['egw_info']['flags']['currentapp']);
$validated = array(); $validated = array();
$template->run('validate', array('', $content, &$validated), true); // $respect_disabled=true: do NOT validate disabled widgets and children $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)) if (self::validation_errors(self::$request->ignore_validation))
{ {
error_log(__METHOD__."(,".array2string($content).') validation_errors='.array2string(self::$validation_errors)); error_log(__METHOD__."(,".array2string($content).') validation_errors='.array2string(self::$validation_errors));

View File

@ -155,7 +155,11 @@ class etemplate_widget
{ {
if ($reader->name != 'id' && $template->attr[$reader->name] != $reader->value) if ($reader->name != 'id' && $template->attr[$reader->name] != $reader->value)
{ {
if (!$cloned) $template = clone($this); if (!$cloned)
{
$template = clone($this);
$cloned = true; // only clone it once, otherwise we loose attributes!
}
$template->attrs[$reader->name] = $reader->value; $template->attrs[$reader->name] = $reader->value;
// split legacy options // split legacy options
@ -326,16 +330,23 @@ class etemplate_widget
* Default implementation only calls method on itself and run on all children * Default implementation only calls method on itself and run on all children
* *
* @param string $method_name * @param string $method_name
* @param array $params=array('') parameter(s) first parameter has to be the cname! * @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 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) public function run($method_name, $params=array(''), $respect_disabled=false)
{ {
// maintain $expand array name-expansion
$cname = $params[0];
$expand =& $params[1];
if ($expand['cname'] !== $cname)
{
$expand['cont'] =& self::get_array(self::$request->content, $cname);
$expand['cname'] = $cname;
}
if ($respect_disabled && ($disabled = $this->attrs['disabled'])) if ($respect_disabled && ($disabled = $this->attrs['disabled']))
{ {
// check if disabled contains @ or ! // check if disabled contains @ or !
$cname = $params[0]; $disabled = self::check_disabled($disabled, $expand);
$disabled = self::check_disabled($disabled, $cname ? self::get_array(self::$request->content, $cname) : self::$request->content);
if ($disabled) if ($disabled)
{ {
error_log(__METHOD__."('$method_name', ".array2string($params).', '.array2string($respect_disabled).") $this disabled='{$this->attrs['disabled']}'='$disabled': NOT running"); error_log(__METHOD__."('$method_name', ".array2string($params).', '.array2string($respect_disabled).") $this disabled='{$this->attrs['disabled']}'='$disabled': NOT running");
@ -360,12 +371,10 @@ class etemplate_widget
* if no =check is given all set non-empty and non-zero strings are true (standard php behavior) * 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 string $disabled expression to check, eg. "!@var" for !$content['var']
* @param array $content the content-array in the context of the grid * @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @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 * @return boolean true if the row/col is disabled or false if not
*/ */
protected static function check_disabled($disabled,$content,$row=null,$c=null) protected static function check_disabled($disabled, array $expland)
{ {
if ($not = $disabled[0] == '!') if ($not = $disabled[0] == '!')
{ {
@ -374,8 +383,8 @@ class etemplate_widget
list($val,$check_val) = $vals = explode('=',$disabled); list($val,$check_val) = $vals = explode('=',$disabled);
// use expand_name to be able to use @ or $ // use expand_name to be able to use @ or $
$val = self::expand_name($val,$c,$row,'','',$content); $val = self::expand_name($val,$expand['c'], $expand['row'], $expand['c_'], $expand['row_'], $expand['cont']);
$check_val = self::expand_name($check_val,$c,$row,'','',$content); $check_val = self::expand_name($check_val,$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); $result = count($vals) == 1 ? $val != '' : ($check_val[0] == '/' ? preg_match($check_val,$val) : $val == $check_val);
if ($not) $result = !$result; if ($not) $result = !$result;
@ -428,11 +437,13 @@ class etemplate_widget
$cont = array(); $cont = array();
} }
if (!is_numeric($c)) $c = boetemplate::chrs2num($c); if (!is_numeric($c)) $c = boetemplate::chrs2num($c);
$col = boetemplate::num2chrs($c-1); // $c-1 to get: 0:'@', 1:'A', ... $col = self::num2chrs($c-1); // $c-1 to get: 0:'@', 1:'A', ...
$col_ = boetemplate::num2chrs($c_-1); $col_ = self::num2chrs($c_-1);
$row_cont = $cont[$row]; $row_cont = $cont[$row];
$col_row_cont = $cont[$col.$row]; $col_row_cont = $cont[$col.$row];
/* RB: dont think any of this is needed in eTemplate2, as this escaping probably needs to be done on clientside anyway
// check if name is enclosed in single quotes as argument eg. to an event handler or // check if name is enclosed in single quotes as argument eg. to an event handler or
// variable name is contained in quotes and curly brackets, eg. "'{$cont[nm][path]}'" or // variable name is contained in quotes and curly brackets, eg. "'{$cont[nm][path]}'" or
// used as name for a button like "delete[$row_cont[something]]" --> quote contained quotes (' or ") // used as name for a button like "delete[$row_cont[something]]" --> quote contained quotes (' or ")
@ -471,7 +482,7 @@ class etemplate_widget
$value = str_replace('&',urlencode('&'),$value); $value = str_replace('&',urlencode('&'),$value);
$name = str_replace($matches[3],$value,$name); $name = str_replace($matches[3],$value,$name);
} }
} }*/
eval('$name = "'.str_replace('"','\\"',$name).'";'); eval('$name = "'.str_replace('"','\\"',$name).'";');
} }
if ($is_index_in_content) if ($is_index_in_content)
@ -485,10 +496,30 @@ class etemplate_widget
$name = self::get_array($cont,substr($name,1)); $name = self::get_array($cont,substr($name,1));
} }
} }
$name = str_replace(array('[',']'),array('[',']'),$name); // RB: not sure why this business with entity encoding for square brakets, it messes up validation
//$name = str_replace(array('[',']'),array('[',']'),$name);
return $name; return $name;
} }
/**
* 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 * Convert object to string
* *
@ -543,10 +574,15 @@ class etemplate_widget
* *
* @param string $cname basename * @param string $cname basename
* @param string $name name * @param string $name name
* @param array $expand=null values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @return string complete form-name * @return string complete form-name
*/ */
static function form_name($cname,$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) if (count($name_parts = explode('[', $name, 2)) > 1)
{ {
$name_parts = array_merge(array($name_parts[0]), explode('][', substr($name_parts[1],0,-1))); $name_parts = array_merge(array($name_parts[0]), explode('][', substr($name_parts[1],0,-1)));
@ -558,7 +594,9 @@ class etemplate_widget
$form_name = array_shift($name_parts); $form_name = array_shift($name_parts);
if (count($name_parts)) if (count($name_parts))
{ {
$form_name .= '['.implode('][',$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 $form_name;
} }
@ -615,12 +653,19 @@ class etemplate_widget
* - $readonlys[__ALL__] set and $readonlys[$form_name] !== false * - $readonlys[__ALL__] set and $readonlys[$form_name] !== false
* - $readonlys[$form_name] evaluates to true * - $readonlys[$form_name] evaluates to true
* *
* @param string $cname=''
* @param string $form_name=null form_name, to not calculate him again
* @return boolean * @return boolean
*/ */
public function is_readonly($cname='') public function is_readonly($cname='', $form_name=null)
{ {
$form_name = self::form_name($cname, $this->id); 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] || $readonly = $this->attrs['readonly'] || self::$request->readonlys[$form_name] ||
isset(self::$request->readonlys['__ALL__']) && self::$request->readonlys[$form_name] !== false; isset(self::$request->readonlys['__ALL__']) && self::$request->readonlys[$form_name] !== false;
@ -767,7 +812,7 @@ class etemplate_widget_box extends etemplate_widget
{ {
$cname =& $params[0]; $cname =& $params[0];
$old_cname = $params[0]; $old_cname = $params[0];
if ($this->id) $cname = self::form_name($cname, $this->id); if ($this->id) $cname = self::form_name($cname, $this->id, $params[1]);
parent::run($method_name, $params, $respect_disabled); parent::run($method_name, $params, $respect_disabled);
$params[0] = $old_cname; $params[0] = $old_cname;

View File

@ -17,20 +17,23 @@
class etemplate_widget_button extends etemplate_widget class etemplate_widget_button extends etemplate_widget
{ {
/** /**
* Validate input * Validate buttons
* *
* Readonly buttons can NOT be pressed * Readonly buttons can NOT be pressed!
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
* @return boolean true if no validation error, false otherwise * @return boolean true if no validation error, false otherwise
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
$form_name = self::form_name($cname, $this->id); $form_name = self::form_name($cname, $this->id, $expand);
//error_log(__METHOD__."('$cname', ".array2string($expand).", ...) $this: get_array(\$content, '$form_name')=".array2string(self::get_array($content, $form_name)));
if (self::get_array($content, $form_name) && !$this->is_readonly($cname)) // need to check === true, as get_array() ignores a "[]" postfix and returns array() eg. for a not existing $row_cont[id] in "delete[$row_cont[id]]"
if (!$this->is_readonly($cname, $form_name) && self::get_array($content, $form_name) === true)
{ {
$valid =& self::get_array($validated, $form_name, true); $valid =& self::get_array($validated, $form_name, true);
$valid = 'pressed'; // that's what it was in old etemplate $valid = 'pressed'; // that's what it was in old etemplate

View File

@ -35,20 +35,21 @@ class etemplate_widget_checkbox extends etemplate_widget
* Same is true for the radio buttons of a radio-group sharing the same name. * Same is true for the radio buttons of a radio-group sharing the same name.
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
* @return boolean true if no validation error, false otherwise * @return boolean true if no validation error, false otherwise
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
$form_name = self::form_name($cname, $this->id); $form_name = self::form_name($cname, $this->id, $expand);
if (($multiple = substr($form_name, -2) == '[]') && $this->type == 'checkbox') if (($multiple = substr($form_name, -2) == '[]') && $this->type == 'checkbox')
{ {
$form_name = substr($form_name, 0, -2); $form_name = substr($form_name, 0, -2);
} }
if (!$this->is_readonly($cname)) if (!$this->is_readonly($cname, $form_name))
{ {
$value = self::get_array($content, $form_name); $value = self::get_array($content, $form_name);
$valid =& self::get_array($validated, $form_name, true); $valid =& self::get_array($validated, $form_name, true);

View File

@ -50,7 +50,7 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
* @var $prefix string Prefix for every custiomfield name returned in $content (# for general (admin) customfields) * @var $prefix string Prefix for every custiomfield name returned in $content (# for general (admin) customfields)
*/ */
protected static $prefix = '#'; protected static $prefix = '#';
// Make settings available globally // Make settings available globally
const GLOBAL_VALS = '~custom_fields~'; const GLOBAL_VALS = '~custom_fields~';
@ -210,24 +210,26 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
* - int and float get casted to their type * - int and float get casted to their type
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
if (!$this->is_readonly($cname)) if ($this->id)
{ {
if($this->id) $form_name = self::form_name($cname, $this->id, $expand);
{ }
$form_name = self::form_name($cname, $this->id); else
} {
else $form_name = self::GLOBAL_ID;
{ }
$form_name = self::GLOBAL_ID;
}
if (!$this->is_readonly($cname, $form_name))
{
$value_in = self::get_array($content, $form_name); $value_in = self::get_array($content, $form_name);
if(is_array($value_in)) { if(is_array($value_in))
{
foreach($value_in as $field => $value) foreach($value_in as $field => $value)
{ {
if ((string)$value === '' && $this->attrs['needed']) if ((string)$value === '' && $this->attrs['needed'])
@ -235,7 +237,7 @@ class etemplate_widget_customfields extends etemplate_widget_transformer
self::set_validation_error($form_name,lang('Field must not be empty !!!'),''); self::set_validation_error($form_name,lang('Field must not be empty !!!'),'');
} }
$valid =& self::get_array($validated, $this->id ? $form_name : $field, true); $valid =& self::get_array($validated, $this->id ? $form_name : $field, true);
$valid = is_array($value) ? implode(',',$value) : $value; $valid = is_array($value) ? implode(',',$value) : $value;
error_log(__METHOD__."() $form_name $field: ".array2string($value).' --> '.array2string($value)); error_log(__METHOD__."() $form_name $field: ".array2string($value).' --> '.array2string($value));
} }

View File

@ -47,16 +47,17 @@ class etemplate_widget_date extends etemplate_widget_transformer
* Validate input * Validate input
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
* @return boolean true if no validation error, false otherwise * @return boolean true if no validation error, false otherwise
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
if (!$this->is_readonly($cname) && $this->type != 'date-since') // date-since is always readonly $form_name = self::form_name($cname, $this->id, $expand);
{
$form_name = self::form_name($cname, $this->id);
if (!$this->is_readonly($cname, $form_name) && $this->type != 'date-since') // date-since is always readonly
{
$value = self::get_array($content, $form_name); $value = self::get_array($content, $form_name);
$valid =& self::get_array($validated, $form_name, true); $valid =& self::get_array($validated, $form_name, true);

View File

@ -17,8 +17,13 @@
*/ */
class etemplate_widget_file extends etemplate_widget class etemplate_widget_file extends etemplate_widget
{ {
/**
public function __construct($xml='') { * Constructor
*
* @param string $xml
*/
public function __construct($xml='')
{
if($xml) parent::__construct($xml); if($xml) parent::__construct($xml);
// Legacy multiple - id ends in [] // Legacy multiple - id ends in []
@ -29,15 +34,15 @@ class etemplate_widget_file extends etemplate_widget
} }
/** /**
* Ajax callback to receive an incoming file * Ajax callback to receive an incoming file
* *
* The incoming file is moved from its temporary location (otherwise server will delete it) and * The incoming file is moved from its temporary location (otherwise server will delete it) and
* the file information is stored into the widget's value. When the form is submitted, the information for all * the file information is stored into the widget's value. When the form is submitted, the information for all
* files uploaded is available in the returned $content array. Because files are uploaded asynchronously, * files uploaded is available in the returned $content array. Because files are uploaded asynchronously,
* submission should be quick. * submission should be quick.
* *
* @note Currently, no attempt is made to clean up files automatically. * @note Currently, no attempt is made to clean up files automatically.
*/ */
public static function ajax_upload() { public static function ajax_upload() {
$response = egw_json_response::get(); $response = egw_json_response::get();
$request_id = str_replace(' ', '+', rawurldecode($_REQUEST['request_id'])); $request_id = str_replace(' ', '+', rawurldecode($_REQUEST['request_id']));
@ -48,11 +53,11 @@ class etemplate_widget_file extends etemplate_widget
} }
if (!($template = etemplate_widget_template::instance(self::$request->template['name'], self::$request->template['template_set'], if (!($template = etemplate_widget_template::instance(self::$request->template['name'], self::$request->template['template_set'],
self::$request->template['version'], self::$request->template['load_via']))) self::$request->template['version'], self::$request->template['load_via'])))
{ {
// Can't use callback // Can't use callback
error_log("Could not get template for file upload, callback skipped"); error_log("Could not get template for file upload, callback skipped");
} }
$file_data = array(); $file_data = array();
@ -146,42 +151,47 @@ class etemplate_widget_file extends etemplate_widget
* Merge any already uploaded files into the content array * Merge any already uploaded files into the content array
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
$form_name = self::form_name($cname, $this->id); $form_name = self::form_name($cname, $this->id, $expand);
$value = $value_in = self::get_array($content, $form_name);
$valid =& self::get_array($validated, $form_name, true);
if(!is_array($value)) $value = array(); if (!$this->is_readonly($cname, $form_name))
// Incoming values indexed by temp name
if($value[0]) $value = $value[0];
foreach($value as $tmp => $file)
{ {
if (is_dir($GLOBALS['egw_info']['server']['temp_dir']) && is_writable($GLOBALS['egw_info']['server']['temp_dir'])) $value = $value_in = self::get_array($content, $form_name);
{ $valid =& self::get_array($validated, $form_name, true);
$path = $GLOBALS['egw_info']['server']['temp_dir'].'/'.$tmp;
}
else
{
$path = $tmp.'+';
}
$stat = stat($path);
$valid[] = array(
'name' => $file['name'],
'type' => $file['type'],
'tmp_name' => $path,
'error' => UPLOAD_ERR_OK, // Always OK if we get this far
'size' => $stat['size'],
'ip' => $_SERVER['REMOTE_ADDR'], // Assume it's the same as for when it was uploaded...
);
}
if($valid && !$this->attrs['multiple']) $valid = $valid[0]; if(!is_array($value)) $value = array();
// Incoming values indexed by temp name
if($value[0]) $value = $value[0];
foreach($value as $tmp => $file)
{
if (is_dir($GLOBALS['egw_info']['server']['temp_dir']) && is_writable($GLOBALS['egw_info']['server']['temp_dir']))
{
$path = $GLOBALS['egw_info']['server']['temp_dir'].'/'.$tmp;
}
else
{
$path = $tmp.'+';
}
$stat = stat($path);
$valid[] = array(
'name' => $file['name'],
'type' => $file['type'],
'tmp_name' => $path,
'error' => UPLOAD_ERR_OK, // Always OK if we get this far
'size' => $stat['size'],
'ip' => $_SERVER['REMOTE_ADDR'], // Assume it's the same as for when it was uploaded...
);
}
if($valid && !$this->attrs['multiple']) $valid = $valid[0];
}
} }
} }
etemplate_widget::registerWidget('etemplate_widget_file', array('file')); etemplate_widget::registerWidget('etemplate_widget_file', array('file'));

View File

@ -13,6 +13,28 @@
/** /**
* eTemplate grid widget incl. row(s) and column(s) * eTemplate grid widget incl. row(s) and column(s)
*
* Example of a grid containing 3 columns and 2 rows (last one autorepeated)
*
* <grid>
* <columns>
* <column>
* <column width="75%">
* <column disabled="@no_actions">
* </columns>
* <rows>
* <row>
* <description value="ID" />
* <description value="Text" />
* <description value="Action" />
* <row>
* <row>
* <description id="${row}[id]" />
* <textbox id="${row}[text]" />
* <button id="delete[$row_cont[id]]" value="Delete" />
* </row>
* </rows>
* </grid>
*/ */
class etemplate_widget_grid extends etemplate_widget_box class etemplate_widget_grid extends etemplate_widget_box
{ {
@ -29,29 +51,39 @@ class etemplate_widget_grid extends etemplate_widget_box
* Run a given method on all children * Run a given method on all children
* *
* Reimplemented to implement column based disabling: * Reimplemented to implement column based disabling:
* - added 4th var-parameter to hold key of disabled column * - added 5th var-parameter to hold key of disabled column
* - if a column is disabled run returns false and key get added by columns run to $columns_disabled * - if a column is disabled run returns false and key get added by columns run to $columns_disabled
* - row run method checks now for each child (arbitrary widget) if it the column's key is included in $columns_disabled * - row run method checks now for each child (arbitrary widget) if it the column's key is included in $columns_disabled
* - as a grid can contain other grid's as direct child, we have to backup and initialise $columns_disabled in grid run! * - 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 string $method_name
* @param array $params=array('') parameter(s) first parameter has to be the cname! * @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 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 * @param array $columns_disabled=array() disabled columns
*/ */
public function run($method_name, $params=array(''), $respect_disabled=false, &$columns_disabled=array()) public function run($method_name, $params=array(''), $respect_disabled=false, &$columns_disabled=array())
{ {
// maintain $expand array name-expansion
$cname =& $params[0];
$expand =& $params[1];
$old_cname = $params[0];
if ($this->id) $cname = self::form_name($cname, $this->id, $expand);
if ($expand['cname'] !== $cname)
{
$expand['cont'] =& self::get_array(self::$request->content, $cname);
$expand['cname'] = $cname;
}
// as a grid can contain other grid's as direct child, we have to backup and initialise $columns_disabled // as a grid can contain other grid's as direct child, we have to backup and initialise $columns_disabled
if ($this->type == 'grid') if ($this->type == 'grid')
{ {
$backup_columns_disabled = $columns_disabled; $backup_columns_disabled = $columns_disabled;
$columns_disabled = array(); $columns_disabled = array();
} }
if ($respect_disabled && ($disabled = $this->attrs['disabled'])) if ($respect_disabled && ($disabled = $this->attrs['disabled']))
{ {
// check if disabled contains @ or ! // check if disabled contains @ or !
$cname = $params[0]; $disabled = self::check_disabled($disabled, $expand);
$disabled = self::check_disabled($disabled, $cname ? self::get_array(self::$request->content, $cname) : self::$request->content);
if ($disabled) if ($disabled)
{ {
error_log(__METHOD__."('$method_name', ".array2string($params).', '.array2string($respect_disabled).") $this disabled='{$this->attrs['disabled']}'='$disabled': NOT running"); error_log(__METHOD__."('$method_name', ".array2string($params).', '.array2string($respect_disabled).") $this disabled='{$this->attrs['disabled']}'='$disabled': NOT running");
@ -62,8 +94,32 @@ class etemplate_widget_grid extends etemplate_widget_box
{ {
call_user_func_array(array($this, $method_name), $params); call_user_func_array(array($this, $method_name), $params);
} }
foreach($this->children as $n => $child) //foreach($this->children as $n => $child)
for($n = 0; ; ++$n)
{ {
// maintain $expand array name-expansion
switch($this->type)
{
case 'rows':
$expand['row'] = $n;
break;
case 'columns':
$expand['c'] = $n;
break;
}
if (isset($this->children[$n]))
{
$child = $this->children[$n];
}
// check if we need to autorepeat last row ($child)
elseif (isset($child) && ($this->type == 'rows' || $this->type == 'columns') && $child->need_autorepeat($cname, $expand))
{
// not breaking repeats last row/column ($child)
}
else
{
break;
}
if ($this->type == 'row') if ($this->type == 'row')
{ {
if (in_array($n, $columns_disabled)) if (in_array($n, $columns_disabled))
@ -83,7 +139,53 @@ class etemplate_widget_grid extends etemplate_widget_box
{ {
$columns_disabled = $backup_columns_disabled; $columns_disabled = $backup_columns_disabled;
} }
$params[0] = $old_cname;
return true; return true;
} }
/**
* Check if a row or column needs autorepeating, because still content left
*
* We only check direct children and grandchildren of first child (eg. box in first column)!
*
* @param string $cname
* @param array $expand
*/
private function need_autorepeat($cname, array $expand)
{
// check id's of children
foreach($this->children as $n => $direct_child)
{
foreach(array_merge(array($direct_child), $n ? array() : $direct_child->children) as $child)
{
$pat = $child->id;
while(($pat = strstr($pat, '$')))
{
$pat = substr($pat,$pat[1] == '{' ? 2 : 1);
switch ($this->type)
{
case 'column':
$Ok = $pat[0] == 'c' && !(substr($pat,0,4) == 'cont' || substr($pat,0,2) == 'c_' ||
substr($pat,0,4) == 'col_');
break;
case 'row':
$Ok = $pat[0] == 'r' && !(substr($pat,0,2) == 'r_' || substr($pat,0,4) == 'row_');
break;
default:
return false;
}
if ($Ok && ($value = self::get_array(self::$request->content,
$fname=self::form_name($cname, $child->id, $expand))) !== false && isset($value))
{
//error_log(__METHOD__."('$method_name', ) $this autorepeating row $expand[row] because of $child->id = '$fname' is ".array2string($value));
return true;
}
}
}
}
return false;
}
} }
etemplate_widget::registerWidget('etemplate_widget_grid', array('grid', 'rows', 'row', 'columns', 'column')); etemplate_widget::registerWidget('etemplate_widget_grid', array('grid', 'rows', 'row', 'columns', 'column'));

View File

@ -22,15 +22,16 @@ class etemplate_widget_htmlarea extends etemplate_widget
* Input is run throught HTMLpurifier, to make sure users can NOT enter javascript or other nasty stuff (XSS!). * Input is run throught HTMLpurifier, to make sure users can NOT enter javascript or other nasty stuff (XSS!).
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
* @return boolean true if no validation error, false otherwise * @return boolean true if no validation error, false otherwise
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
$form_name = self::form_name($cname, $this->id); $form_name = self::form_name($cname, $this->id, $expand);
if (!$this->is_readonly($cname)) if (!$this->is_readonly($cname, $form_name))
{ {
$value = self::get_array($content, $form_name); $value = self::get_array($content, $form_name);
$valid =& self::get_array($validated, $form_name, true); $valid =& self::get_array($validated, $form_name, true);

View File

@ -179,7 +179,7 @@ class etemplate_widget_link extends etemplate_widget
$link['help'] = lang('Remove this link (not the entry itself)'); $link['help'] = lang('Remove this link (not the entry itself)');
} }
} }
$response = egw_json_response::get(); $response = egw_json_response::get();
// Strip keys, unneeded and cause index problems on the client side // Strip keys, unneeded and cause index problems on the client side
$response->data(array_values($links)); $response->data(array_values($links));
@ -201,15 +201,16 @@ class etemplate_widget_link extends etemplate_widget
* - int and float get casted to their type * - int and float get casted to their type
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
if (!$this->is_readonly($cname)) $form_name = self::form_name($cname, $this->id, $expand);
{
$form_name = self::form_name($cname, $this->id);
if (!$this->is_readonly($cname, $form_name))
{
$value = $value_in =& self::get_array($content, $form_name); $value = $value_in =& self::get_array($content, $form_name);
// Look for files // Look for files

View File

@ -65,15 +65,16 @@ class etemplate_widget_menupopup extends etemplate_widget
* Validate input * Validate input
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
$form_name = self::form_name($cname, $this->id); $form_name = self::form_name($cname, $this->id, $expand);
$ok = true; $ok = true;
if (!$this->is_readonly($cname)) if (!$this->is_readonly($cname, $form_name))
{ {
$value = $value_in = self::get_array($content, $form_name); $value = $value_in = self::get_array($content, $form_name);
@ -131,7 +132,7 @@ class etemplate_widget_menupopup extends etemplate_widget
self::setElementAttribute($form_name, 'no_lang', $no_lang); self::setElementAttribute($form_name, 'no_lang', $no_lang);
} }
} }
// Make sure &nbsp;s, etc. are properly encoded when sent, and not double-encoded // Make sure &nbsp;s, etc. are properly encoded when sent, and not double-encoded
foreach(self::$request->sel_options[$form_name] as &$label) foreach(self::$request->sel_options[$form_name] as &$label)
{ {

View File

@ -677,12 +677,13 @@ class etemplate_widget_nextmatch extends etemplate_widget
* - int and float get casted to their type * - int and float get casted to their type
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
$form_name = self::form_name($cname, $this->id); $form_name = self::form_name($cname, $this->id, $expand);
$value = self::get_array($content, $form_name); $value = self::get_array($content, $form_name);
// On client, rows does not get its own namespace, but all apps are expecting it // On client, rows does not get its own namespace, but all apps are expecting it
@ -720,7 +721,7 @@ class etemplate_widget_nextmatch extends etemplate_widget
* Reimplemented to add namespace, and make sure row template gets included * Reimplemented to add namespace, and make sure row template gets included
* *
* @param string $method_name * @param string $method_name
* @param array $params=array('') parameter(s) first parameter has to be cname! * @param array $params=array('') parameter(s) first parameter has to be cname, second $expand!
* @param boolean $respect_disabled=false false (default): ignore disabled, true: method is NOT run for disabled widgets AND their children * @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) public function run($method_name, $params=array(''), $respect_disabled=false)
@ -728,10 +729,12 @@ class etemplate_widget_nextmatch extends etemplate_widget
$old_param0 = $params[0]; $old_param0 = $params[0];
$cname =& $params[0]; $cname =& $params[0];
// Need this check or the headers will get involved too // Need this check or the headers will get involved too
if($this->type == 'nextmatch') { if($this->type == 'nextmatch')
{
if ($this->id) $cname = self::form_name($cname, $this->id, $params[1]);
parent::run($method_name, $params, $respect_disabled); parent::run($method_name, $params, $respect_disabled);
if ($this->id) $cname = self::form_name($cname, $this->id);
if($this->attrs['template']) if ($this->attrs['template'])
{ {
$row_template = etemplate_widget_template::instance($this->attrs['template']); $row_template = etemplate_widget_template::instance($this->attrs['template']);
$row_template->run($method_name, $params, $respect_disabled); $row_template->run($method_name, $params, $respect_disabled);

View File

@ -72,10 +72,11 @@ class etemplate_widget_projectmanager extends etemplate_widget_transformer
* @todo * @todo
* @param string $cname current namespace * @param string $cname current namespace
* @param array $content * @param array $content
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
* @return boolean true if no validation error, false otherwise * @return boolean true if no validation error, false otherwise
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
} }
} }

View File

@ -122,17 +122,14 @@ class etemplate_widget_template extends etemplate_widget
* *
* Reimplemented because templates can have an own namespace specified in attrs[content], NOT id! * Reimplemented because templates can have an own namespace specified in attrs[content], NOT id!
* *
* @param array $content * @param string $method_name
* @param array &$validated=array() validated content * @param array $params=array('') parameter(s) first parameter has to be cname, second $expand!
* @param string $cname='' current namespace
* @param boolean $respect_disabled=false false (default): ignore disabled, true: method is NOT run for disabled widgets AND their children * @param boolean $respect_disabled=false false (default): ignore disabled, true: method is NOT run for disabled widgets AND their children
* @todo handle template references containing content in id, eg. id="edit.$cont[something]"
*/ */
public function run($method_name, $params=array(''), $respect_disabled=false) public function run($method_name, $params=array(''), $respect_disabled=false)
{ {
$cname =& $params[0]; $cname =& $params[0];
if ($this->attrs['content']) $cname = self::form_name($cname, $this->attrs['content']); if ($this->attrs['content']) $cname = self::form_name($cname, $this->attrs['content'], $params[1]);
parent::run($method_name, $params, $respect_disabled); parent::run($method_name, $params, $respect_disabled);
} }
} }

View File

@ -53,12 +53,16 @@ class etemplate_widget_textbox extends etemplate_widget
* - int and float get casted to their type * - int and float get casted to their type
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
* @param array $expand=array values for keys 'c', 'row', 'c_', 'row_', 'cont'
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
if (!$this->is_readonly($cname)) $form_name = self::form_name($cname, $this->id, $expand);
if (!$this->is_readonly($cname, $form_name))
{ {
if (!isset($this->attrs['preg'])) if (!isset($this->attrs['preg']))
{ {
@ -75,7 +79,6 @@ class etemplate_widget_textbox extends etemplate_widget
break; break;
} }
} }
$form_name = self::form_name($cname, $this->id);
$value = $value_in = self::get_array($content, $form_name); $value = $value_in = self::get_array($content, $form_name);
$valid =& self::get_array($validated, $form_name, true); $valid =& self::get_array($validated, $form_name, true);

View File

@ -46,15 +46,16 @@ class etemplate_widget_tree extends etemplate_widget
* Validate input * Validate input
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
$form_name = self::form_name($cname, $this->id); $form_name = self::form_name($cname, $this->id, $expand);
$ok = true; $ok = true;
if (!$this->is_readonly($cname)) if (!$this->is_readonly($cname, $form_name))
{ {
$value = $value_in = self::get_array($content, $form_name); $value = $value_in = self::get_array($content, $form_name);
@ -101,7 +102,7 @@ class etemplate_widget_tree extends etemplate_widget
self::setElementAttribute($form_name, 'no_lang', $no_lang); self::setElementAttribute($form_name, 'no_lang', $no_lang);
} }
} }
// Make sure &nbsp;s, etc. are properly encoded when sent, and not double-encoded // Make sure &nbsp;s, etc. are properly encoded when sent, and not double-encoded
foreach(self::$request->sel_options[$form_name] as &$label) foreach(self::$request->sel_options[$form_name] as &$label)
{ {
@ -161,7 +162,7 @@ class etemplate_widget_tree extends etemplate_widget
* @param string $widget_type * @param string $widget_type
* @param string $legacy_options options string of widget * @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 $no_lang=false initial value of no_lang attribute (some types set it to true)
* @param boolean $readonly=false * @param boolean $readonly=false
* @param mixed $value=null value for readonly * @param mixed $value=null value for readonly
* @return array with value => label pairs * @return array with value => label pairs
*/ */

View File

@ -30,13 +30,13 @@ class etemplate_widget_vfs extends etemplate_widget_file
self::store_file($field, $file); self::store_file($field, $file);
} }
} }
/** /**
* Ajax callback to receive an incoming file * Ajax callback to receive an incoming file
* *
* The incoming file is automatically placed into the appropriate VFS location. * The incoming file is automatically placed into the appropriate VFS location.
* If the entry is not yet created, the file information is stored into the widget's value. * If the entry is not yet created, the file information is stored into the widget's value.
* When the form is submitted, the information for all files uploaded is available in the returned * When the form is submitted, the information for all files uploaded is available in the returned
* $content array and the application should deal with the file. * $content array and the application should deal with the file.
*/ */
public static function store_file($path, $file) { public static function store_file($path, $file) {
@ -91,12 +91,13 @@ error_log(lang('Error copying uploaded file to vfs!'));
* Merge any already uploaded files into the content array * Merge any already uploaded files into the content array
* *
* @param string $cname current namespace * @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content * @param array $content
* @param array &$validated=array() validated content * @param array &$validated=array() validated content
*/ */
public function validate($cname, array $content, &$validated=array()) public function validate($cname, array $expand, array $content, &$validated=array())
{ {
$form_name = self::form_name($cname, $this->id); $form_name = self::form_name($cname, $this->id, $expand);
$value = $value_in = self::get_array($content, $form_name); $value = $value_in = self::get_array($content, $form_name);
$valid =& self::get_array($validated, $form_name, true); $valid =& self::get_array($validated, $form_name, true);