forked from extern/egroupware
277 lines
9.0 KiB
PHP
277 lines
9.0 KiB
PHP
<?php
|
|
/**
|
|
* EGroupware - eTemplate custom fields widget
|
|
*
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
* @package etemplate
|
|
* @subpackage api
|
|
* @link http://www.egroupware.org
|
|
* @author Nathan Gray
|
|
* @copyright 2011 Nathan Gray
|
|
* @version $Id$
|
|
*/
|
|
|
|
/**
|
|
* Widgets for custom fields and listing custom fields
|
|
*
|
|
*/
|
|
class etemplate_widget_customfields extends etemplate_widget_transformer
|
|
{
|
|
|
|
/**
|
|
* Allowd types of customfields
|
|
*
|
|
* The additionally allowed app-names from the link-class, will be add by the edit-method only,
|
|
* as the link-class has to be called, which can NOT be instanciated by the constructor, as
|
|
* we get a loop in the instanciation.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected static $cf_types = array(
|
|
'text' => 'Text',
|
|
'float' => 'Float',
|
|
'label' => 'Label',
|
|
'select' => 'Selectbox',
|
|
'ajax_select' => 'Search',
|
|
'radio' => 'Radiobutton',
|
|
'checkbox' => 'Checkbox',
|
|
'date' => 'Date',
|
|
'date-time'=> 'Date+Time',
|
|
'select-account' => 'Select account',
|
|
'button' => 'Button', // button to execute javascript
|
|
'url' => 'Url',
|
|
'url-email'=> 'EMail',
|
|
'url-phone'=> 'Phone number',
|
|
'htmlarea' => 'Formatted Text (HTML)',
|
|
'link-entry' => 'Select entry', // should be last type, as the individual apps get added behind
|
|
);
|
|
|
|
/**
|
|
* @var $prefix string Prefix for every custiomfield name returned in $content (# for general (admin) customfields)
|
|
*/
|
|
protected static $prefix = '#';
|
|
|
|
// Make settings available globally
|
|
const GLOBAL_VALS = '~custom_fields~';
|
|
|
|
// Used if there's no ID provided
|
|
const GLOBAL_ID = 'custom_fields';
|
|
|
|
protected $legacy_options = 'sub-type,use-private,field-names';
|
|
|
|
protected static $transformation = array(
|
|
'type' => array(
|
|
'customfields-types' => array(
|
|
'type' => 'select',
|
|
'sel_options' => array()
|
|
),
|
|
'customfields-list' => array(
|
|
'readonly' => true
|
|
)
|
|
)
|
|
);
|
|
|
|
public function __construct($xml)
|
|
{
|
|
parent::__construct($xml);
|
|
}
|
|
|
|
/**
|
|
* Fill type options in self::$request->sel_options to be used on the client
|
|
*
|
|
* @param string $cname
|
|
*/
|
|
public function beforeSendToClient($cname)
|
|
{
|
|
// No name, no way to get parameters client-side.
|
|
if(!$this->id) $this->id = self::GLOBAL_ID;
|
|
|
|
$form_name = self::form_name($cname, $this->id);
|
|
|
|
// Store properties at top level, so all customfield widgets can share
|
|
$app =& $this->getElementAttribute(self::GLOBAL_VALS, 'app');
|
|
if($this->getElementAttribute($form_name, 'app'))
|
|
{
|
|
$app =& $this->getElementAttribute($form_name, 'app');
|
|
} else {
|
|
// Checking creates it even if it wasn't there
|
|
unset(self::$request->modifications[$form_name]['app']);
|
|
}
|
|
|
|
if($this->getElementAttribute($form_name, 'customfields'))
|
|
{
|
|
$customfields =& $this->getElementAttribute($form_name, 'customfields');
|
|
}
|
|
elseif($app)
|
|
{
|
|
// Checking creates it even if it wasn't there
|
|
unset(self::$request->modifications[$form_name]['customfields']);
|
|
$customfields =& $this->getElementAttribute(self::GLOBAL_VALS, 'customfields');
|
|
}
|
|
|
|
if(!$app)
|
|
{
|
|
$app =& $this->setElementAttribute(self::GLOBAL_VALS, 'app', $GLOBALS['egw_info']['flags']['currentapp']);
|
|
$customfields =& $this->setElementAttribute(self::GLOBAL_VALS, 'customfields', config::get_customfields($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'] && (
|
|
$GLOBALS['egw_info']['flags']['currentapp'] == 'etemplate' || !$this->attrs['customfields'] ||
|
|
etemplate::$hooked
|
|
))
|
|
{
|
|
// app changed
|
|
$customfields =& config::get_customfields($app);
|
|
}
|
|
|
|
// Filter fields
|
|
if($this->attrs['field-names'])
|
|
{
|
|
if($this->attrs['field-names'][0] == '!') {
|
|
$negate_field_filter = true;
|
|
$this->attrs['field-names'] = substr($this->attrs['field_names'],1);
|
|
}
|
|
$field_filter = explode(',', $this->attrs['field_names']);
|
|
}
|
|
$fields = $customfields;
|
|
|
|
$use_private = self::expand_name($this->attrs['use-private'],0,0);
|
|
foreach((array)$fields as $key => $field)
|
|
{
|
|
// remove private or non-private cf's, if only one kind should be displayed
|
|
if ((string)$this->attrs['use-private'] !== '' && (boolean)$field['private'] != (boolean)$use_private)
|
|
{
|
|
unset($fields[$key]);
|
|
}
|
|
|
|
// Remove filtered fields
|
|
if($field_filter && (!$negate_field_filter && !in_array($key, $field_filter) ||
|
|
$negate_field_filter && in_array($key, $field_filter)))
|
|
{
|
|
unset($fields[$key]);
|
|
}
|
|
}
|
|
// check if name refers to a single custom field --> show only that
|
|
if (($pos=strpos($form_name,self::$prefix)) !== false && // allow the prefixed name to be an array index too
|
|
preg_match("/$this->prefix([^\]]+)/",$form_name,$matches) && isset($fields[$name=$matches[1]]))
|
|
{
|
|
$fields = array($name => $fields[$name]);
|
|
$value = array($this->prefix.$name => $value);
|
|
$singlefield = true;
|
|
$form_name = substr($form_name,0,-strlen("[$this->prefix$name]"));
|
|
}
|
|
|
|
if(!is_array($fields)) $fields = array();
|
|
switch($type = $this->type)
|
|
{
|
|
case 'customfields-types':
|
|
foreach(self::$cf_types as $lname => $label)
|
|
{
|
|
$sel_options[$lname] = lang($label);
|
|
$fields_with_vals[]=$lname;
|
|
}
|
|
$link_types = egw_link::app_list();
|
|
ksort($link_types);
|
|
foreach($link_types as $lname => $label) $sel_options[$lname] = '- '.$label;
|
|
|
|
self::$transformation['type'][$type]['sel_options'] = $sel_options;
|
|
self::$transformation['type'][$type]['no_lang'] = true;
|
|
return parent::beforeSendToClient($cname);
|
|
case 'customfields-list':
|
|
foreach(array_reverse($fields) as $lname => $field)
|
|
{
|
|
if (!empty($this->attrs['sub-type']) && !empty($field['type2']) && strpos(','.$field['type2'].',',','.$type2.',') === false) continue; // not for our content type//
|
|
if (isset($value[$this->prefix.$lname]) && $value[$this->prefix.$lname] !== '') //break;
|
|
{
|
|
$fields_with_vals[]=$lname;
|
|
}
|
|
//$stop_at_field = $name;
|
|
}
|
|
break;
|
|
default:
|
|
foreach(array_reverse($fields) as $lname => $field)
|
|
{
|
|
$fields_with_vals[]=$lname;
|
|
}
|
|
}
|
|
if($fields != $customfields)
|
|
{
|
|
// This widget has different settings from global
|
|
$this->setElementAttribute($form_name, 'customfields', $fields);
|
|
$this->setElementAttribute($form_name, 'fields', array_fill_keys(array_keys($fields), true));
|
|
}
|
|
parent::beforeSendToClient($cname);
|
|
|
|
// Re-format date custom fields from Y-m-d
|
|
$field_settings =& self::get_array(self::$request->modifications, "{$this->id}[customfields]",true);
|
|
$field_settings = array();
|
|
foreach($fields as $fname => $field)
|
|
{
|
|
if($field['type'] == 'date' && self::$request->content[self::$prefix.$fname])
|
|
{
|
|
self::$request->content[self::$prefix.$fname] = strtotime(self::$request->content[self::$prefix.$fname]);
|
|
}
|
|
|
|
// Run beforeSendToClient for each field
|
|
$widget = self::factory($field['type'], '<?xml version="1.0"?><'.$field['type'].' type="'.$field['type'].'"/>', self::$prefix.$fname);
|
|
if(method_exists($widget, 'beforeSendToClient'))
|
|
{
|
|
$widget->id = "customfields[{$fname}]";
|
|
$widget->beforeSendToClient($this->id, $fname);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate input
|
|
*
|
|
* Following attributes get checked:
|
|
* - needed: value must NOT be empty
|
|
* - min, max: int and float widget only
|
|
* - maxlength: maximum length of string (longer strings get truncated to allowed size)
|
|
* - preg: perl regular expression incl. delimiters (set by default for int, float and colorpicker)
|
|
* - int and float get casted to their type
|
|
*
|
|
* @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())
|
|
{
|
|
if ($this->id)
|
|
{
|
|
$form_name = self::form_name($cname, $this->id, $expand);
|
|
}
|
|
else
|
|
{
|
|
$form_name = self::GLOBAL_ID;
|
|
}
|
|
|
|
if (!$this->is_readonly($cname, $form_name))
|
|
{
|
|
$value_in = self::get_array($content, $form_name);
|
|
if(is_array($value_in))
|
|
{
|
|
foreach($value_in as $field => $value)
|
|
{
|
|
if ((string)$value === '' && $this->attrs['needed'])
|
|
{
|
|
self::set_validation_error($form_name,lang('Field must not be empty !!!'),'');
|
|
}
|
|
$valid =& self::get_array($validated, $this->id ? $form_name : $field, true);
|
|
|
|
$valid = is_array($value) ? implode(',',$value) : $value;
|
|
error_log(__METHOD__."() $form_name $field: ".array2string($value).' --> '.array2string($value));
|
|
}
|
|
} elseif ($this->type == 'customfields-types') {
|
|
// Transformation doesn't handle validation
|
|
$valid =& self::get_array($validated, $this->id ? $form_name : $field, true);
|
|
$valid = $value_in;
|
|
error_log(__METHOD__."() $form_name $field: ".array2string($value).' --> '.array2string($value));
|
|
}
|
|
}
|
|
}
|
|
}
|