mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-04 22:01:05 +01:00
cfccc90025
doublicate check was iterating through all options for each option, causing it to take eg. 25s for 7000 options
845 lines
26 KiB
PHP
845 lines
26 KiB
PHP
<?php
|
|
/**
|
|
* EGroupware - eTemplate serverside select widget
|
|
*
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
* @package etemplate
|
|
* @subpackage api
|
|
* @link http://www.egroupware.org
|
|
* @author Ralf Becker <RalfBecker@outdoor-training.de>
|
|
* @copyright 2002-14 by RalfBecker@outdoor-training.de
|
|
* @version $Id$
|
|
*/
|
|
|
|
/**
|
|
* eTemplate select widget
|
|
*
|
|
* @todo unavailable cats (eg. private cats of an other user) need to be preserved!
|
|
*/
|
|
class etemplate_widget_menupopup extends etemplate_widget
|
|
{
|
|
/**
|
|
* If the selectbox has this many rows, give it a search box automatically
|
|
*/
|
|
const SEARCH_ROW_LIMIT = PHP_INT_MAX; // Automatic disabled, only explicit
|
|
|
|
/**
|
|
* These types are either set or cached on the client side, so we don't send
|
|
* their options unless asked via AJAX
|
|
*/
|
|
public static $cached_types = array(
|
|
'select-account',
|
|
'select-app',
|
|
'select-bool',
|
|
'select-country',
|
|
'select-dow',
|
|
'select-number',
|
|
'select-priority',
|
|
'select-percent',
|
|
'select-year',
|
|
'select-month',
|
|
'select-day',
|
|
'select-hour',
|
|
'select-lang',
|
|
'select-timezone'
|
|
);
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected static $monthnames = array(
|
|
0 => '',
|
|
1 => 'January',
|
|
2 => 'February',
|
|
3 => 'March',
|
|
4 => 'April',
|
|
5 => 'May',
|
|
6 => 'June',
|
|
7 => 'July',
|
|
8 => 'August',
|
|
9 => 'September',
|
|
10 => 'October',
|
|
11 => 'November',
|
|
12 => 'December'
|
|
);
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param string|XMLReader $xml string with xml or XMLReader positioned on the element to construct
|
|
* @throws egw_exception_wrong_parameter
|
|
*/
|
|
public function __construct($xml = '')
|
|
{
|
|
if($xml) {
|
|
parent::__construct($xml);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse and set extra attributes from xml in template object
|
|
*
|
|
* Reimplemented to parse our differnt attributes
|
|
*
|
|
* @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
|
|
* @todo Use legacy_attributes instead of leaving it to typeOptions method to parse them
|
|
*/
|
|
public function set_attrs($xml, $cloned=true)
|
|
{
|
|
parent::set_attrs($xml, $cloned);
|
|
|
|
// set attrs[multiple] from attrs[options], unset options only if it just contains number or rows
|
|
if ($this->attrs['options'] > 1)
|
|
{
|
|
$this->attrs['multiple'] = (int)$this->attrs['options'];
|
|
if ((string)$this->attrs['multiple'] == $this->attrs['options'])
|
|
{
|
|
unset($this->attrs['options']);
|
|
}
|
|
}
|
|
elseif($this->attrs['rows'] > 1)
|
|
{
|
|
$this->attrs['multiple'] = true;
|
|
}
|
|
}
|
|
|
|
const UNAVAILABLE_CAT_POSTFIX = '-unavailable';
|
|
|
|
/**
|
|
* Validate input
|
|
*
|
|
* @param string $cname current namespace
|
|
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
|
|
* @param array $content
|
|
* @param array &$validated=array() validated content
|
|
*/
|
|
public function validate($cname, array $expand, array $content, &$validated=array())
|
|
{
|
|
$form_name = self::form_name($cname, $this->id, $expand);
|
|
$widget_type = $this->attrs['type'] ? $this->attrs['type'] : $this->type;
|
|
|
|
$ok = true;
|
|
if (!$this->is_readonly($cname, $form_name))
|
|
{
|
|
$value = $value_in = self::get_array($content, $form_name);
|
|
|
|
$allowed = 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));
|
|
|
|
if (!$this->attrs['multiple'] || !($this->attrs['options'] > 1)) $allowed[] = '';
|
|
|
|
foreach((array) $value as $val)
|
|
{
|
|
// handle empty-label for all widget types
|
|
if ((string)$val === '' && in_array('', $allowed)) continue;
|
|
|
|
switch ($widget_type)
|
|
{
|
|
case 'select-account':
|
|
// If in allowed options, skip account check to support app-specific options
|
|
if(count($allowed) > 0 && in_array($val,$allowed)) continue;
|
|
|
|
// validate accounts independent of options know to server
|
|
$account_type = $this->attrs['account_type'] ? $this->attrs['account_type'] : 'accounts';
|
|
$type = $GLOBALS['egw']->accounts->exists($val);
|
|
//error_log(__METHOD__."($cname,...) form_name=$form_name, widget_type=$widget_type, account_type=$account_type, type=$type");
|
|
if (!$type || $type == 1 && in_array($account_type, array('groups', 'owngroups', 'memberships')) ||
|
|
$type == 2 && $account_type == 'users' ||
|
|
in_array($account_type, array('owngroups', 'memberships')) &&
|
|
!in_array($val, $GLOBALS['egw']->accounts->memberships(
|
|
$GLOBALS['egw_info']['user']['account_id'], true))
|
|
)
|
|
{
|
|
self::set_validation_error($form_name, lang("'%1' is NOT allowed ('%2')!", $val,
|
|
!$type?'not found' : ($type == 1 ? 'user' : 'group')),'');
|
|
$value = '';
|
|
break 2;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if(!in_array($val, $allowed))
|
|
{
|
|
self::set_validation_error($form_name,lang("'%1' is NOT allowed ('%2')!", $val, implode("','",$allowed)),'');
|
|
$value = '';
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
if ($ok && $value === '' && $this->attrs['needed'])
|
|
{
|
|
self::set_validation_error($form_name,lang('Field must not be empty !!!',$value),'');
|
|
}
|
|
// some widgets sub-types need some post-processing
|
|
// ToDo: move it together with preprocessing to clientside
|
|
switch ($widget_type)
|
|
{
|
|
case 'select-dow':
|
|
$dow = 0;
|
|
foreach((array)$value as $val)
|
|
{
|
|
$dow |= $val;
|
|
}
|
|
$value = $dow;
|
|
break;
|
|
|
|
case 'select-country':
|
|
$legacy_options = $this->attrs['rows'] && strpos($this->attrs['options'], $this->attrs['rows']) !== 0 ?
|
|
$this->attrs['rows'].','.$this->attrs['options'] : $this->attrs['options'];
|
|
list(,$country_use_name) = explode(',', $legacy_options);
|
|
if ($country_use_name && $value)
|
|
{
|
|
$value = $GLOBALS['egw']->country->get_full_name($value);
|
|
}
|
|
break;
|
|
|
|
case 'select-cat':
|
|
// unavailable cats need to be merged in again
|
|
$unavailable_name = $form_name.self::UNAVAILABLE_CAT_POSTFIX;
|
|
if (isset(self::$request->preserv[$unavailable_name]))
|
|
{
|
|
if ($this->attrs['multiple'])
|
|
{
|
|
$value = array_merge($value, (array)self::$request->preserv[$unavailable_name]);
|
|
}
|
|
elseif(!$value) // for single cat, we only restore unavailable one, if no other was selected
|
|
{
|
|
$value = self::$request->preserv[$unavailable_name];
|
|
}
|
|
}
|
|
}
|
|
if (isset($value))
|
|
{
|
|
self::set_array($validated, $form_name, $value);
|
|
//error_log(__METHOD__."() $form_name: ".array2string($value_in).' --> '.array2string($value).', allowed='.array2string($allowed));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//error_log($this . "($form_name) is read-only, skipping validate");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fill type options in self::$request->sel_options to be used on the client
|
|
*
|
|
* @param string $cname
|
|
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
|
|
*/
|
|
public function beforeSendToClient($cname, array $expand=null)
|
|
{
|
|
//error_log(__METHOD__."('$cname') this->id=$this->id, this->type=$this->type, this->attrs=".array2string($this->attrs));
|
|
$matches = null;
|
|
if ($cname == '$row') // happens eg. with custom-fields: $cname='$row', this->id='#something'
|
|
{
|
|
$form_name = $this->id;
|
|
}
|
|
// happens with fields in nm-header: $cname='nm', this->id='${row}[something]' or '{$row}[something]'
|
|
elseif (preg_match('/(\${row}|{\$row})\[([^]]+)\]$/', $this->id, $matches))
|
|
{
|
|
$form_name = $matches[2];
|
|
}
|
|
// happens in auto-repeat grids: $cname='', this->id='something[{$row}]'
|
|
elseif (preg_match('/([^[]+)\[({\$row})\]$/', $this->id, $matches))
|
|
{
|
|
$form_name = $matches[1];
|
|
}
|
|
// happens with autorepeated grids: this->id='some[$row_cont[thing]][else]' --> just use 'else'
|
|
elseif (preg_match('/\$row.*\[([^]]+)\]$/', $this->id, $matches))
|
|
{
|
|
$form_name = $matches[1];
|
|
}
|
|
else
|
|
{
|
|
$form_name = self::form_name($cname, $this->id, $expand);
|
|
}
|
|
if (!is_array(self::$request->sel_options[$form_name])) self::$request->sel_options[$form_name] = array();
|
|
$type = $this->attrs['type'] ? $this->attrs['type'] : $this->type;
|
|
if ($type != 'select' && $type != 'menupopup')
|
|
{
|
|
// Check selection preference, we may be able to skip reading some data
|
|
$select_pref = $GLOBALS['egw_info']['user']['preferences']['common']['account_selection'];
|
|
if($this->attrs['type'] == 'select-account' && !$GLOBALS['egw_info']['user']['apps']['admin'] && $select_pref == 'none')
|
|
{
|
|
self::$request->preserv[$this->id] = self::$request->content[$this->id];
|
|
unset(self::$request->content[$this->id]);
|
|
$this->attrs['readonly'] = true;
|
|
}
|
|
if(!in_array($this->attrs['type'], self::$cached_types))
|
|
{
|
|
// adding type specific options here, while keep further options set by app code
|
|
// we need to make sure to run only once for auto-repeated rows, because
|
|
// array_merge used to keep options from app would otherwise add
|
|
// type-specific ones multiple time (and of cause better performance)
|
|
$no_lang = null;
|
|
static $form_names_done = array();
|
|
if (!isset($form_names_done[$form_name]) &&
|
|
($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']),
|
|
$no_lang, $this->attrs['readonly'], self::get_array(self::$request->content, $form_name), $form_name)))
|
|
{
|
|
self::fix_encoded_options($type_options);
|
|
|
|
self::$request->sel_options[$form_name] = array_merge(self::$request->sel_options[$form_name], $type_options);
|
|
|
|
// if no_lang was modified, forward modification to the client
|
|
if ($no_lang != $this->attr['no_lang'])
|
|
{
|
|
self::setElementAttribute($form_name, 'no_lang', $no_lang);
|
|
}
|
|
}
|
|
$form_names_done[$form_name] = true;
|
|
}
|
|
}
|
|
|
|
// 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]))
|
|
{
|
|
if(in_array($this->attrs['type'], self::$cached_types) && !isset($form_names_done[$options]))
|
|
{
|
|
// Fix any custom options from application
|
|
self::fix_encoded_options(self::$request->sel_options[$options],true);
|
|
$form_names_done[$options] = true;
|
|
}
|
|
// Turn on search, if there's a lot of rows (unless explicitly set)
|
|
if(!array_key_exists('search',$this->attrs) && count(self::$request->sel_options[$options]) >= self::SEARCH_ROW_LIMIT)
|
|
{
|
|
self::setElementAttribute($form_name, "search", true);
|
|
}
|
|
if(!self::$request->sel_options[$options])
|
|
{
|
|
unset(self::$request->sel_options[$options]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fix already html-encoded options, eg. "&nbps" AND optinal re-index array to keep order
|
|
*
|
|
* Get run automatic for everything in $sel_options by etemplate_new::exec / etemplate_new::fix_sel_options
|
|
*
|
|
* @param array $options
|
|
* @param boolean $use_array_of_objects Re-indexes options, making everything more complicated
|
|
*/
|
|
public static function fix_encoded_options(array &$options, $use_array_of_objects=null)
|
|
{
|
|
$backup_options = $options;
|
|
|
|
$values = array();
|
|
foreach($options as $value => &$label)
|
|
{
|
|
// Of course once we re-index the options, we can't detect duplicates
|
|
// so check here, as we re-index
|
|
// Duplicates might happen if app programmer isn't paying attention and
|
|
// either uses the same ID in the template, or adds the options twice
|
|
$check_value = (string)(is_array($label) && array_key_exists('value', $label) ? $label['value'] : $value);
|
|
if (isset($values[$check_value]))
|
|
{
|
|
unset($options[$value]);
|
|
continue;
|
|
}
|
|
$values[$check_value] = $label;
|
|
|
|
if (is_null($use_array_of_objects) && is_numeric($value) && (!is_array($label) || !isset($label['value'])))
|
|
{
|
|
$options = $backup_options;
|
|
return self::fix_encoded_options($options, true);
|
|
}
|
|
// optgroup or values for keys "label" and "title"
|
|
if(is_array($label))
|
|
{
|
|
self::fix_encoded_options($label, false);
|
|
if ($use_array_of_objects && !array_key_exists('value', $label)) $label['value'] = $value;
|
|
}
|
|
else
|
|
{
|
|
$label = html_entity_decode($label, ENT_NOQUOTES, 'utf-8');
|
|
|
|
if ($use_array_of_objects)
|
|
{
|
|
$label = array(
|
|
'value' => $value,
|
|
'label' => $label,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if ($use_array_of_objects)
|
|
{
|
|
$options = array_values($options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get options from $sel_options array for a given selectbox name
|
|
*
|
|
* @param string $name
|
|
* @param boolean $return_values =false true: return array with option values, instead of value => label pairs
|
|
* @return array
|
|
*/
|
|
public static function selOptions($name, $return_values=false)
|
|
{
|
|
$options = array();
|
|
|
|
// Check for exact match on name
|
|
if (isset(self::$request->sel_options[$name]) && is_array(self::$request->sel_options[$name]))
|
|
{
|
|
$options += self::$request->sel_options[$name];
|
|
}
|
|
|
|
// Check for base of name in root of sel_options
|
|
if(!$options)
|
|
{
|
|
$name_parts = explode('[',str_replace(array('[',']',']'),array('['),$name));
|
|
if (count($name_parts))
|
|
{
|
|
$org_name = $name_parts[count($name_parts)-1];
|
|
if (isset(self::$request->sel_options[$org_name]) && is_array(self::$request->sel_options[$org_name]))
|
|
{
|
|
$options += self::$request->sel_options[$org_name];
|
|
}
|
|
elseif (isset(self::$request->sel_options[$name_parts[0]]) && is_array(self::$request->sel_options[$name_parts[0]]))
|
|
{
|
|
$options += self::$request->sel_options[$name_parts[0]];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check for options-$name in content
|
|
if (is_array(self::$request->content['options-'.$name]))
|
|
{
|
|
$options += self::$request->content['options-'.$name];
|
|
}
|
|
if ($return_values)
|
|
{
|
|
$values = array();
|
|
foreach($options as $key => $val)
|
|
{
|
|
if (is_array($val))
|
|
{
|
|
if (isset($val['value']))
|
|
{
|
|
$values[] = $val['value'];
|
|
}
|
|
else if ((isset($val['label']) || isset($val['title'])) && count($val) == 1 ||
|
|
isset($val['title']) && isset($val['label']) && count($val) == 2)
|
|
{
|
|
// key => {label, title}
|
|
$values[] = $key;
|
|
}
|
|
else // optgroup
|
|
{
|
|
foreach($val as $k => $v)
|
|
{
|
|
$values[] = is_array($v) && isset($v['value']) ? $v['value'] : $k;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$values[] = $key;
|
|
}
|
|
}
|
|
//error_log(__METHOD__."('$name', TRUE) options=".array2string($options).' --> values='.array2string($values));
|
|
$options = $values;
|
|
}
|
|
//error_log(__METHOD__."('$name') returning ".array2string($options));
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* 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 $_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
|
|
* @param mixed $value =null value for readonly
|
|
* @param string $form_name =null
|
|
* @return array with value => label pairs
|
|
*/
|
|
public static function typeOptions($widget_type, $_legacy_options, &$no_lang=false, $readonly=false, &$value=null, $form_name=null)
|
|
{
|
|
if($widget_type && is_object($widget_type))
|
|
{
|
|
$widget = $widget_type;
|
|
$widget_type = $widget->attrs['type'] ? $widget->attrs['type'] : $widget->type;
|
|
}
|
|
// Legacy / static support
|
|
// Have to do this explicitly, since legacy options is not defined on class level
|
|
$legacy_options = explode(',',$_legacy_options);
|
|
foreach($legacy_options as &$field)
|
|
{
|
|
$field = self::expand_name($field, 0, 0,'','',self::$cont);
|
|
}
|
|
|
|
list($rows,$type,$type2,$type3,$type4,$type5) = $legacy_options;
|
|
$no_lang = false;
|
|
$options = array();
|
|
switch ($widget_type)
|
|
{
|
|
case 'select-percent': // options: #row,decrement(default=10)
|
|
$decr = $type > 0 ? $type : 10;
|
|
for ($i=0; $i <= 100; $i += $decr)
|
|
{
|
|
$options[intval($i)] = intval($i).'%';
|
|
}
|
|
$options[100] = '100%';
|
|
if (!$rows || !empty($value))
|
|
{
|
|
$value = intval(($value+($decr/2)) / $decr) * $decr;
|
|
}
|
|
$no_lang = True;
|
|
break;
|
|
|
|
case 'select-priority':
|
|
$options = array('','low','normal','high');
|
|
break;
|
|
|
|
case 'select-bool': // equal to checkbox, can be used with nextmatch-customfilter to filter a boolean column
|
|
$options = array(0 => 'no',1 => 'yes');
|
|
break;
|
|
|
|
case 'select-country': // #Row|Extralabel,1=use country name, 0=use 2 letter-code,custom country field name
|
|
if($type == 0 && $type2)
|
|
{
|
|
$custom_label = is_numeric($type2) ? 'Custom' : $type2;
|
|
$options = array('-custom-' => lang($custom_label)) + $GLOBALS['egw']->country->countries();
|
|
}
|
|
else
|
|
{
|
|
$options = $GLOBALS['egw']->country->countries();
|
|
}
|
|
if ($type && $value)
|
|
{
|
|
$value = $GLOBALS['egw']->country->country_code($value);
|
|
if (!isset($options[$value]))
|
|
{
|
|
if($type2)
|
|
{
|
|
$options[$value] = $value;
|
|
}
|
|
}
|
|
}
|
|
$no_lang = True;
|
|
break;
|
|
|
|
case 'select-state':
|
|
$options = $GLOBALS['egw']->country->us_states();
|
|
$no_lang = True;
|
|
break;
|
|
|
|
case 'select-cat': // !$type == globals cats too, $type2: extraStyleMultiselect, $type3: application, if not current-app, $type4: parent-id, $type5=owner (-1=global),$type6=show missing
|
|
if ((!$type3 || $type3 === $GLOBALS['egw']->categories->app_name) &&
|
|
(!$type5 || $type5 == $GLOBALS['egw']->categories->account_id))
|
|
{
|
|
$categories = $GLOBALS['egw']->categories;
|
|
}
|
|
else // we need to instanciate a new cat object for the correct application
|
|
{
|
|
$categories = new categories($type5,$type3);
|
|
}
|
|
// Allow text for global
|
|
$type = ($type && strlen($type) > 1 ? $type : !$type);
|
|
// we cast $type4 (parent) to int, to get default of 0 if omitted
|
|
foreach((array)$categories->return_sorted_array(0,False,'','','',$type,(int)$type4,true) as $cat)
|
|
{
|
|
$s = str_repeat(' ',$cat['level']) . stripslashes($cat['name']);
|
|
|
|
if (categories::is_global($cat))
|
|
{
|
|
$s .= categories::$global_marker;
|
|
}
|
|
$options[$cat['id']] = array(
|
|
'label' => $s,
|
|
'title' => $cat['description'],
|
|
);
|
|
// Send data too
|
|
if(is_array($cat['data']))
|
|
{
|
|
$options[$cat['id']] += $cat['data'];
|
|
}
|
|
}
|
|
// preserv unavailible cats (eg. private user-cats)
|
|
if ($value && ($unavailible = array_diff(is_array($value) ? $value : explode(',',$value),array_keys((array)$options))))
|
|
{
|
|
// unavailable cats need to be merged in again
|
|
$unavailable_name = $form_name.self::UNAVAILABLE_CAT_POSTFIX;
|
|
self::$request->preserv[$unavailable_name] = $unavailible;
|
|
}
|
|
$no_lang = True;
|
|
break;
|
|
|
|
case 'select-year': // options: #rows,#before(default=3),#after(default=2)
|
|
$options[''] = '';
|
|
if ($type <= 0) $type = 3;
|
|
if ($type2 <= 0) $type2 = 2;
|
|
if ($type > 100 && $type2 > 100 && $type > $type) { $y = $type; $type=$type2; $type2=$y; }
|
|
$y = date('Y')-$type;
|
|
if ($value && $value-$type < $y || $type > 100) $y = $type > 100 ? $type : $value-$type;
|
|
$to = date('Y')+$type2;
|
|
if ($value && $value+$type2 > $to || $type2 > 100) $to = $type2 > 100 ? $type2 : $value+$type2;
|
|
for ($n = 0; $y <= $to && $n < 200; ++$n)
|
|
{
|
|
$options[$y] = $y++;
|
|
}
|
|
$no_lang = True;
|
|
break;
|
|
|
|
case 'select-month':
|
|
$options = self::$monthnames;
|
|
$value = intval($value);
|
|
break;
|
|
|
|
case 'select-dow': // options: rows[,0=summaries befor days, 1=summaries after days, 2=no summaries[,extraStyleMultiselect]]
|
|
if (!defined('MCAL_M_SUNDAY'))
|
|
{
|
|
define('MCAL_M_SUNDAY',1);
|
|
define('MCAL_M_MONDAY',2);
|
|
define('MCAL_M_TUESDAY',4);
|
|
define('MCAL_M_WEDNESDAY',8);
|
|
define('MCAL_M_THURSDAY',16);
|
|
define('MCAL_M_FRIDAY',32);
|
|
define('MCAL_M_SATURDAY',64);
|
|
|
|
define('MCAL_M_WEEKDAYS',62);
|
|
define('MCAL_M_WEEKEND',65);
|
|
define('MCAL_M_ALLDAYS',127);
|
|
}
|
|
$weekstart = $GLOBALS['egw_info']['user']['preferences']['calendar']['weekdaystarts'];
|
|
$options = array();
|
|
if ($rows >= 2 && !$type)
|
|
{
|
|
$options = array(
|
|
MCAL_M_ALLDAYS => 'all days',
|
|
MCAL_M_WEEKDAYS => 'working days',
|
|
MCAL_M_WEEKEND => 'weekend',
|
|
);
|
|
}
|
|
if ($weekstart == 'Saturday') $options[MCAL_M_SATURDAY] = 'saturday';
|
|
if ($weekstart != 'Monday') $options[MCAL_M_SUNDAY] = 'sunday';
|
|
$options += array(
|
|
MCAL_M_MONDAY => 'monday',
|
|
MCAL_M_TUESDAY => 'tuesday',
|
|
MCAL_M_WEDNESDAY=> 'wednesday',
|
|
MCAL_M_THURSDAY => 'thursday',
|
|
MCAL_M_FRIDAY => 'friday',
|
|
);
|
|
if ($weekstart != 'Saturday') $options[MCAL_M_SATURDAY] = 'saturday';
|
|
if ($weekstart == 'Monday') $options[MCAL_M_SUNDAY] = 'sunday';
|
|
if ($rows >= 2 && $type == 1)
|
|
{
|
|
$options += array(
|
|
MCAL_M_ALLDAYS => 'all days',
|
|
MCAL_M_WEEKDAYS => 'working days',
|
|
MCAL_M_WEEKEND => 'weekend',
|
|
);
|
|
}
|
|
$value_in = $value;
|
|
$value = array();
|
|
foreach(array_keys($options) as $val)
|
|
{
|
|
if (($value_in & $val) == $val)
|
|
{
|
|
$value[] = $val;
|
|
|
|
if ($val == MCAL_M_ALLDAYS ||
|
|
$val == MCAL_M_WEEKDAYS && $value_in == MCAL_M_WEEKDAYS ||
|
|
$val == MCAL_M_WEEKEND && $value_in == MCAL_M_WEEKEND)
|
|
{
|
|
break; // dont set the others
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'select-day':
|
|
$type = 1;
|
|
$type2 = 31;
|
|
$type3 = 1;
|
|
// fall-through
|
|
|
|
case 'select-number': // options: rows,min,max,decrement,suffix
|
|
$type = $type === '' ? 1 : intval($type); // min
|
|
$type2 = $type2 === '' ? 10 : intval($type2); // max
|
|
$format = '%d';
|
|
if (!empty($type3) && $type3[0] == '0') // leading zero
|
|
{
|
|
$format = '%0'.strlen($type3).'d';
|
|
}
|
|
$type3 = !$type3 ? 1 : intval($type3); // decrement
|
|
if (($type <= $type2) != ($type3 > 0))
|
|
{
|
|
$type3 = -$type3; // void infinite loop
|
|
}
|
|
if (!empty($type4)) $format .= lang($type4);
|
|
for ($i=0,$n=$type; $n <= $type2 && $i <= 100; $n += $type3,++$i)
|
|
{
|
|
$options[$n] = sprintf($format,$n);
|
|
}
|
|
$no_lang = True;
|
|
break;
|
|
|
|
case 'select-hour':
|
|
for ($h = 0; $h <= 23; ++$h)
|
|
{
|
|
$options[$h] = $GLOBALS['egw_info']['user']['preferences']['common']['timeformat'] == 12 ?
|
|
(($h % 12 ? $h % 12 : 12).' '.($h < 12 ? lang('am') : lang('pm'))) :
|
|
sprintf('%02d',$h);
|
|
}
|
|
$no_lang = True;
|
|
break;
|
|
|
|
case 'select-app': // type2: 'user'=apps of current user, 'enabled', 'installed' (default), 'all' = not installed ones too
|
|
$apps = self::app_options($type2);
|
|
$options = is_array($options) ? $options+$apps : $apps;
|
|
break;
|
|
|
|
case 'select-lang':
|
|
$options = translation::list_langs();
|
|
$no_lang = True;
|
|
break;
|
|
|
|
case 'select-timezone': // options: #rows,$type
|
|
if (is_numeric($value))
|
|
{
|
|
$value = calendar_timezones::id2tz($value);
|
|
}
|
|
if ($readonly) // for readonly we dont need to fetch all TZ's
|
|
{
|
|
$options[$value] = calendar_timezones::tz2id($value,'name');
|
|
}
|
|
else
|
|
{
|
|
$options = $type ? egw_time::getTimezones() : egw_time::getUserTimezones($value);
|
|
}
|
|
break;
|
|
}
|
|
if ($rows > 1 || $readonly)
|
|
{
|
|
unset($options['']);
|
|
}
|
|
|
|
//error_log(__METHOD__."('$widget_type', '$_legacy_options', no_lang=".array2string($no_lang).', readonly='.array2string($readonly).", value=$value) returning ".array2string($options));
|
|
return $options;
|
|
}
|
|
|
|
/**
|
|
* Get available apps as options
|
|
*
|
|
* @param string $type2 ='installed' 'user'=apps of current user, 'enabled', 'installed' (default), 'all' = not installed ones too
|
|
* @return array app => label pairs sorted by label
|
|
*/
|
|
public static function app_options($type2)
|
|
{
|
|
$apps = array();
|
|
foreach ($GLOBALS['egw_info']['apps'] as $app => $data)
|
|
{
|
|
if ($type2 == 'enabled' && (!$data['enabled'] || !$data['status'] || $data['status'] == 3))
|
|
{
|
|
continue; // app not enabled (user can not have run rights for these apps)
|
|
}
|
|
if ($type2 != 'user' || $GLOBALS['egw_info']['user']['apps'][$app])
|
|
{
|
|
$apps[$app] = lang($app);
|
|
}
|
|
}
|
|
if ($type2 == 'all')
|
|
{
|
|
$dir = opendir(EGW_SERVER_ROOT);
|
|
while ($file = readdir($dir))
|
|
{
|
|
if (@is_dir(EGW_SERVER_ROOT."/$file/setup") && $file[0] != '.' &&
|
|
!isset($apps[$app = basename($file)]))
|
|
{
|
|
$apps[$app] = $app . ' (*)';
|
|
}
|
|
}
|
|
closedir($dir);
|
|
}
|
|
natcasesort($apps);
|
|
return $apps;
|
|
}
|
|
|
|
/**
|
|
* internal function to format account-data
|
|
*
|
|
* @param int $id
|
|
* @param array $acc =null optional values for keys account_(type|lid|lastname|firstname) to not read them again
|
|
* @param int $longnames
|
|
* @param boolean $show_type true: return array with values for keys label and icon, false: only label
|
|
* @return string|array
|
|
*/
|
|
private static function accountInfo($id,$acc=null,$longnames=0,$show_type=false)
|
|
{
|
|
if (!$id)
|
|
{
|
|
return ' ';
|
|
}
|
|
|
|
if (!is_array($acc))
|
|
{
|
|
$data = $GLOBALS['egw']->accounts->get_account_data($id);
|
|
if (!isset($data[$id])) return '#'.$id;
|
|
foreach(array('type','lid','firstname','lastname') as $name)
|
|
{
|
|
$acc['account_'.$name] = $data[$id][$name];
|
|
}
|
|
}
|
|
|
|
if ($acc['account_type'] == 'g')
|
|
{
|
|
$longnames = 1;
|
|
}
|
|
switch ($longnames)
|
|
{
|
|
case 2:
|
|
$info .= '<'.$acc['account_lid'].'> ';
|
|
// fall-through
|
|
case 1:
|
|
$info .= $acc['account_type'] == 'g' ? lang('group').' '.$acc['account_lid'] :
|
|
$acc['account_firstname'].' '.$acc['account_lastname'];
|
|
break;
|
|
case '0':
|
|
$info .= $acc['account_lid'];
|
|
break;
|
|
default: // use the phpgw default
|
|
$info = $GLOBALS['egw']->common->display_fullname($acc['account_lid'],
|
|
$acc['account_firstname'],$acc['account_lastname']);
|
|
break;
|
|
}
|
|
if($show_type) {
|
|
$info = array(
|
|
'label' => $info,
|
|
'icon' => $acc['account_type'] == 'g' ? 'addressbook/group' : 'user'
|
|
);
|
|
}
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* Some select options are fairly static, but can only be generated on the server
|
|
* so we generate them here, then cache them client-side
|
|
*
|
|
* @param string $type
|
|
* @param Array|String $attributes
|
|
*
|
|
*/
|
|
public static function ajax_get_options($type, $attributes)
|
|
{
|
|
$options = self::typeOptions($type, $attributes);
|
|
self::fix_encoded_options($options,true);
|
|
$response = egw_json_response::get();
|
|
$response->data($options);
|
|
}
|
|
}
|
|
|
|
etemplate_widget::registerWidget('etemplate_widget_menupopup', array('selectbox','listbox','select','menupopup'));
|