Newer
Older
function getChoices($verbose=false) {
if ($this->_choices === null || $verbose) {
// Allow choices to be set in this->ht (for configurationOptions)
$this->_choices = $this->get('choices');
if (!$this->_choices) {
$this->_choices = array();
$config = $this->getConfiguration();
$choices = explode("\n", $config['choices']);
foreach ($choices as $choice) {
// Allow choices to be key: value
list($key, $val) = explode(':', $choice);
if ($val == null)
$val = $key;
$this->_choices[trim($key)] = trim($val);
}
// Add old selections if nolonger available
// This is necessary so choices made previously can be
// retained
$values = ($a=$this->getAnswer()) ? $a->getValue() : array();
if ($values && is_array($values)) {
foreach ($values as $k => $v) {
if (!isset($this->_choices[$k])) {
if ($verbose) $v .= ' (retired)';
$this->_choices[$k] = $v;
}
}
}
}
}
return $this->_choices;
}
}
class DatetimeField extends FormField {
static $widget = 'DatetimePickerWidget';
function to_database($value) {
// Store time in gmt time, unix epoch format
return (string) $value;
}
function to_php($value) {
if (!$value)
return $value;
else
return (int) $value;
}
function toString($value) {
global $cfg;
$config = $this->getConfiguration();
// If GMT is set, convert to local time zone. Otherwise, leave
// unchanged (default TZ is UTC)
if ($config['time'])
return Format::datetime($value, false, !$config['gmt'] ? 'UTC' : false);
return Format::date($value, false, false, !$config['gmt'] ? 'UTC' : false);
function export($value) {
$config = $this->getConfiguration();
if (!$value)
return '';
else
return Format::date($value, false, 'y-MM-dd HH:mm:ss', !$config['gmt'] ? 'UTC' : false);
function getConfigurationOptions() {
return array(
'time' => new BooleanField(array(
'id'=>1, 'label'=>__('Time'), 'required'=>false, 'default'=>false,
'desc'=>__('Show time selection with date picker')))),
'id'=>2, 'label'=>__('Timezone Aware'), 'required'=>false,
'desc'=>__("Show date/time relative to user's timezone")))),
'id'=>3, 'label'=>__('Earliest'), 'required'=>false,
'hint'=>__('Earliest date selectable'))),
'id'=>4, 'label'=>__('Latest'), 'required'=>false,
'default'=>null, 'hint'=>__('Latest date selectable'))),
'id'=>5, 'label'=>__('Allow Future Dates'), 'required'=>false,
'desc'=>__('Allow entries into the future' /* Used in the date field */)),
)),
);
}
function validateEntry($value) {
$config = $this->getConfiguration();
parent::validateEntry($value);
if (!$value) return;
if ($config['min'] and $value < $config['min'])
$this->_errors[] = __('Selected date is earlier than permitted');
elseif ($config['max'] and $value > $config['max'])
$this->_errors[] = __('Selected date is later than permitted');
// strtotime returns -1 on error for PHP < 5.1.0 and false thereafter
elseif ($value === -1 or $value === false)
$this->_errors[] = __('Enter a valid date');
/**
* This is kind-of a special field that doesn't have any data. It's used as
* a field to provide a horizontal section break in the display of a form
*/
class SectionBreakField extends FormField {
static $widget = 'SectionBreakWidget';
function hasData() {
return false;
}
function isBlockLevel() {
return true;
}
}
class ThreadEntryField extends FormField {
static $widget = 'ThreadEntryWidget';
function isChangeable() {
return false;
}
function isBlockLevel() {
return true;
}
function isPresentationOnly() {
return true;
}
function hasSpecialSearch() {
return false;
}
function getConfigurationOptions() {
global $cfg;
$attachments = new FileUploadField();
$fileupload_config = $attachments->getConfigurationOptions();
if ($cfg->getAllowedFileTypes())
$fileupload_config['extensions']->set('default', $cfg->getAllowedFileTypes());
return array(
'attachments' => new BooleanField(array(
'label'=>__('Enable Attachments'),
'default'=>$cfg->allowAttachments(),
'configuration'=>array(
'desc'=>__('Enables attachments on tickets, regardless of channel'),
'validators' => function($self, $value) {
if (!ini_get('file_uploads'))
$self->addError(__('The "file_uploads" directive is disabled in php.ini'));
}
+ $fileupload_config;
function isAttachmentsEnabled() {
$config = $this->getConfiguration();
return $config['attachments'];
}
}
class PriorityField extends ChoiceField {
function getWidget() {
$widget = parent::getWidget();
if ($widget->value instanceof Priority)
$widget->value = $widget->value->getId();
return $widget;
}
function hasIdValue() {
return true;
}
function isChangeable() {
return $this->getForm()->get('type') != 'T' ||
$this->get('name') != 'priority';
}
global $cfg;
$this->ht['default'] = $cfg->getDefaultPriorityId();
$sql = 'SELECT priority_id, priority_desc FROM '.PRIORITY_TABLE
.' ORDER BY priority_urgency DESC';
if (!($res = db_query($sql)))
return $choices;
while ($row = db_fetch_row($res))
$choices[$row[0]] = $row[1];
return $choices;
}
function parse($id) {
return $this->to_php(null, $id);
}
function to_php($value, $id=false) {
if (is_array($id)) {
reset($id);
$id = key($id);
}
return Priority::lookup($id);
}
function to_database($prio) {
return ($prio instanceof Priority)
? array($prio->getDesc(), $prio->getId())
: $prio;
}
function toString($value) {
return ($value instanceof Priority) ? $value->getDesc() : $value;
}
function searchable($value) {
// Priority isn't searchable this way
return null;
}
function getConfigurationOptions() {
return array(
'prompt' => new TextboxField(array(
'id'=>2, 'label'=>__('Prompt'), 'required'=>false, 'default'=>'',
'hint'=>__('Leading text shown before a value is selected'),
'configuration'=>array('size'=>40, 'length'=>40),
)),
);
FormField::addFieldTypes(/*@trans*/ 'Dynamic Fields', function() {
'priority' => array(__('Priority Level'), PriorityField),
class TicketStateField extends ChoiceField {
'name' => /* @trans, @context "ticket state name" */ 'Open',
'verb' => /* @trans, @context "ticket state action" */ 'Open'
'name' => /* @trans, @context "ticket state name" */ 'Closed',
'verb' => /* @trans, @context "ticket state action" */ 'Close'
// Private states
static $_privatestates = array(
'name' => /* @trans, @context "ticket state name" */ 'Archived',
'verb' => /* @trans, @context "ticket state action" */ 'Archive'
'name' => /* @trans, @context "ticket state name" */ 'Deleted',
'verb' => /* @trans, @context "ticket state action" */ 'Delete'
);
function hasIdValue() {
return true;
}
function isChangeable() {
return false;
}
function getChoices() {
static $_choices;
if (!isset($_choices)) {
// Translate and cache the choices
foreach (static::$_states as $k => $v)
$_choices[$k] = _P('ticket state name', $v['name']);
$this->ht['default'] = '';
}
return $_choices;
}
function getChoice($state) {
if ($state && is_array($state))
$state = key($state);
if (isset(static::$_states[$state]))
return _P('ticket state name', static::$_states[$state]['name']);
if (isset(static::$_privatestates[$state]))
return _P('ticket state name', static::$_privatestates[$state]['name']);
}
function getConfigurationOptions() {
return array(
'prompt' => new TextboxField(array(
'id'=>2, 'label'=> __('Prompt'), 'required'=>false, 'default'=>'',
'hint'=> __('Leading text shown before a value is selected'),
'configuration'=>array('size'=>40, 'length'=>40),
)),
);
}
static function getVerb($state) {
if (isset(static::$_states[$state]))
return _P('ticket state action', static::$_states[$state]['verb']);
if (isset(static::$_privatestates[$state]))
return _P('ticket state action', static::$_privatestates[$state]['verb']);
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
}
FormField::addFieldTypes('Dynamic Fields', function() {
return array(
'state' => array('Ticket State', TicketStateField, false),
);
});
class TicketFlagField extends ChoiceField {
// Supported flags (TODO: move to configurable custom list)
static $_flags = array(
'onhold' => array(
'flag' => 1,
'name' => 'Onhold',
'states' => array('open'),
),
'overdue' => array(
'flag' => 2,
'name' => 'Overdue',
'states' => array('open'),
),
'answered' => array(
'flag' => 4,
'name' => 'Answered',
'states' => array('open'),
)
);
var $_choices;
function hasIdValue() {
return true;
}
function isChangeable() {
return true;
}
function getChoices() {
$this->ht['default'] = '';
if (!$this->_choices) {
foreach (static::$_flags as $k => $v)
$this->_choices[$k] = $v['name'];
}
return $this->_choices;
}
function getConfigurationOptions() {
return array(
'prompt' => new TextboxField(array(
'id'=>2, 'label'=>'Prompt', 'required'=>false, 'default'=>'',
'hint'=>'Leading text shown before a value is selected',
'configuration'=>array('size'=>40, 'length'=>40),
)),
);
}
}
FormField::addFieldTypes('Dynamic Fields', function() {
return array(
'flags' => array('Ticket Flags', TicketFlagField, false),
);
});
class FileUploadField extends FormField {
static $widget = 'FileUploadWidget';
protected $attachments;
static function getFileTypes() {
static $filetypes;
if (!isset($filetypes))
$filetypes = YamlDataParser::load(INCLUDE_DIR . '/config/filetype.yaml');
return $filetypes;
}
function getConfigurationOptions() {
// Compute size selections
$sizes = array('262144' => '— '.__('Small').' —');
$next = 512 << 10;
$max = strtoupper(ini_get('upload_max_filesize'));
$limit = (int) $max;
if (!$limit) $limit = 2 << 20; # 2M default value
elseif (strpos($max, 'K')) $limit <<= 10;
elseif (strpos($max, 'M')) $limit <<= 20;
elseif (strpos($max, 'G')) $limit <<= 30;
while ($next <= $limit) {
// Select the closest, larger value (in case the
// current value is between two)
$sizes[$next] = Format::file_size($next);
$next *= 2;
}
// Add extra option if top-limit in php.ini doesn't fall
// at a power of two
if ($next < $limit * 2)
$sizes[$limit] = Format::file_size($limit);
foreach (self::getFileTypes() as $type=>$info) {
$types[$type] = $info['description'];
}
return array(
'size' => new ChoiceField(array(
'label'=>__('Maximum File Size'),
'hint'=>__('Choose maximum size of a single file uploaded to this field'),
'default'=>$cfg->getMaxFileSize(),
'mimetypes' => new ChoiceField(array(
'label'=>__('Restrict by File Type'),
'hint'=>__('Optionally, choose acceptable file types.'),
'required'=>false,
'choices'=>$types,
'configuration'=>array('multiselect'=>true,'prompt'=>__('No restrictions'))
'extensions' => new TextareaField(array(
'label'=>__('Additional File Type Filters'),
'hint'=>__('Optionally, enter comma-separated list of additional file types, by extension. (e.g .doc, .pdf).'),
'configuration'=>array('html'=>false, 'rows'=>2),
)),
'max' => new TextboxField(array(
'label'=>__('Maximum Files'),
'hint'=>__('Users cannot upload more than this many files.'),
'default'=>false,
'required'=>false,
'validator'=>'number',
'configuration'=>array('size'=>8, 'length'=>4, 'placeholder'=>__('No limit')),
function hasSpecialSearch() {
return false;
}
/**
* Called from the ajax handler for async uploads via web clients.
*/
function ajaxUpload($bypass=false) {
$config = $this->getConfiguration();
$files = AttachmentFile::format($_FILES['upload'],
// For numeric fields assume configuration exists
!is_numeric($this->get('id')));
if (count($files) != 1)
Http::response(400, 'Send one file at a time');
$file = array_shift($files);
$file['name'] = urldecode($file['name']);
if (!$bypass && !$this->isValidFileType($file['name'], $file['type']))
Http::response(415, 'File type is not allowed');
$config = $this->getConfiguration();
if (!$bypass && $file['size'] > $config['size'])
Http::response(413, 'File is too large');
if (!($id = AttachmentFile::upload($file)))
Http::response(500, 'Unable to store file: '. $file['error']);
/**
* Called from FileUploadWidget::getValue() when manual upload is used
* for browsers which do not support the HTML5 way of uploading async.
*/
function uploadFile($file) {
if (!$this->isValidFileType($file['name'], $file['type']))
throw new FileUploadError(__('File type is not allowed'));
$config = $this->getConfiguration();
if ($file['size'] > $config['size'])
throw new FileUploadError(__('File size is too large'));
return AttachmentFile::upload($file);
}
/**
* Called from API and email routines and such to handle attachments
* sent other than via web upload
*/
function uploadAttachment(&$file) {
if (!$this->isValidFileType($file['name'], $file['type']))
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
throw new FileUploadError(__('File type is not allowed'));
if (is_callable($file['data']))
$file['data'] = $file['data']();
if (!isset($file['size'])) {
// bootstrap.php include a compat version of mb_strlen
if (extension_loaded('mbstring'))
$file['size'] = mb_strlen($file['data'], '8bit');
else
$file['size'] = strlen($file['data']);
}
$config = $this->getConfiguration();
if ($file['size'] > $config['size'])
throw new FileUploadError(__('File size is too large'));
if (!$id = AttachmentFile::save($file))
throw new FileUploadError(__('Unable to save file'));
return $id;
}
function isValidFileType($name, $type=false) {
$config = $this->getConfiguration();
// Check MIME type - file ext. shouldn't be solely trusted.
if ($type && $config['__mimetypes']
&& in_array($type, $config['__mimetypes']))
// Return true if all file types are allowed (.*)
if (!$config['__extensions'] || in_array('.*', $config['__extensions']))
$allowed = $config['__extensions'];
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
return ($ext && is_array($allowed) && in_array(".$ext", $allowed));
}
function getFiles() {
if (!isset($this->attachments) && ($a = $this->getAnswer())
&& ($e = $a->getEntry()) && ($e->get('id'))
) {
$this->attachments = new GenericAttachments(
// Combine the field and entry ids to make the key
sprintf('%u', crc32('E'.$this->get('id').$e->get('id'))),
'E');
}
return $this->attachments ? $this->attachments->getAll() : array();
}
function getConfiguration() {
$config = parent::getConfiguration();
$_types = self::getFileTypes();
$mimetypes = array();
$extensions = array();
if (isset($config['mimetypes']) && is_array($config['mimetypes'])) {
foreach ($config['mimetypes'] as $type=>$desc) {
foreach ($_types[$type]['types'] as $mime=>$exts) {
$mimetypes[$mime] = true;
if (is_array($exts))
foreach ($exts as $ext)
$extensions['.'.$ext] = true;
}
}
}
if (strpos($config['extensions'], '.*') !== false)
$config['extensions'] = '';
if (is_string($config['extensions'])) {
foreach (preg_split('/\s+/', str_replace(',',' ', $config['extensions'])) as $ext) {
if (!$ext) {
continue;
}
elseif (strpos($ext, '/')) {
}
else {
if ($ext[0] != '.')
$ext = '.' . $ext;
// Add this to the MIME types list so it can be exported to
// the @accept attribute
if (!isset($extensions[$ext]))
$mimetypes[$ext] = true;
$extensions[$ext] = true;
}
$config['__extensions'] = array_keys($extensions);
}
elseif (is_array($config['extensions'])) {
$config['__extensions'] = $config['extensions'];
}
// 'mimetypes' is the array represented from the user interface,
// '__mimetypes' is a complete list of supported MIME types.
$config['__mimetypes'] = array_keys($mimetypes);
return $config;
}
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
// When the field is saved to database, encode the ID listing as a json
// array. Then, inspect the difference between the files actually
// attached to this field
function to_database($value) {
$this->getFiles();
if (isset($this->attachments)) {
$ids = array();
// Handle deletes
foreach ($this->attachments->getAll() as $f) {
if (!in_array($f['id'], $value))
$this->attachments->delete($f['id']);
else
$ids[] = $f['id'];
}
// Handle new files
foreach ($value as $id) {
if (!in_array($id, $ids))
$this->attachments->upload($id);
}
}
return JsonDataEncoder::encode($value);
}
function parse($value) {
// Values in the database should be integer file-ids
return array_map(function($e) { return (int) $e; },
$value ?: array());
}
function to_php($value) {
return JsonDataParser::decode($value);
}
function display($value) {
$links = array();
foreach ($this->getFiles() as $f) {
$hash = strtolower($f['key']
. md5($f['id'].session_id().strtolower($f['key'])));
$links[] = sprintf('<a class="no-pjax" href="file.php?h=%s">%s</a>',
$hash, Format::htmlchars($f['name']));
}
return implode('<br/>', $links);
}
function toString($value) {
$files = array();
foreach ($this->getFiles() as $f) {
$files[] = $f['name'];
}
return implode(', ', $files);
}
function __construct($field) {
$this->field = $field;
$this->name = $field->getFormName();
$this->id = '_' . $this->name;
$this->value = $this->getValue();
if (!isset($this->value) && is_object($this->field->getAnswer()))
$this->value = $this->field->getAnswer()->getValue();
if (!isset($this->value) && isset($this->field->value))
$data = $this->field->getSource();
// Search for HTML form name first
if (isset($data[$this->name]))
return $data[$this->name];
elseif (isset($data[$this->field->get('name')]))
return $data[$this->field->get('name')];
return null;
/**
* getJsValueGetter
*
* Used with the dependent fields feature, this function should return a
* single javascript expression which can be used in a larger expression
* (<> == true, where <> is the result of this function). The %s token
* will be replaced with a jQuery variable representing this widget.
*/
function getJsValueGetter() {
return '%s.val()';
}
}
class TextboxWidget extends Widget {
function render($mode=false) {
$config = $this->field->getConfiguration();
if (isset($config['size']))
$size = "size=\"{$config['size']}\"";
if (isset($config['length']) && $config['length'])
$maxlength = "maxlength=\"{$config['length']}\"";
if (isset($config['classes']))
$classes = 'class="'.$config['classes'].'"';
if (isset($config['autocomplete']))
$autocomplete = 'autocomplete="'.($config['autocomplete']?'on':'off').'"';
if (isset($config['disabled']))
$disabled = 'disabled="disabled"';
if (isset($config['translatable']) && $config['translatable'])
$translatable = 'data-translate-tag="'.$config['translatable'].'"';
$placeholder = sprintf('placeholder="%s"', $this->field->getLocal('placeholder',
$config['placeholder']));
<input type="<?php echo static::$input_type; ?>"
id="<?php echo $this->id; ?>"
<?php echo implode(' ', array_filter(array(
$size, $maxlength, $classes, $autocomplete, $disabled,
$translatable, $placeholder))); ?>
name="<?php echo $this->name; ?>"
value="<?php echo Format::htmlchars($this->value); ?>"/>
</span>
<?php
}
}
class PasswordWidget extends TextboxWidget {
static $input_type = 'password';
function parseValue() {
// Show empty box unless failed POST
if ($_SERVER['REQUEST_METHOD'] == 'POST'
&& $this->field->getForm()->isValid())
parent::parseValue();
else
$this->value = '';
}
}
function render($mode=false) {
$class = $cols = $rows = $maxlength = "";
if (isset($config['rows']))
$rows = "rows=\"{$config['rows']}\"";
if (isset($config['cols']))
$cols = "cols=\"{$config['cols']}\"";
if (isset($config['length']) && $config['length'])
$maxlength = "maxlength=\"{$config['length']}\"";
if (isset($config['html']) && $config['html']) {
$class = array('richtext', 'no-bar');
$class[] = @$config['size'] ?: 'small';
$class = sprintf('class="%s"', implode(' ', $class));
$this->value = Format::viewableImages($this->value);
}
<span style="display:inline-block;width:100%">
<textarea <?php echo $rows." ".$cols." ".$maxlength." ".$class
.' placeholder="'.$config['placeholder'].'"'; ?>
id="<?php echo $this->id; ?>"
name="<?php echo $this->name; ?>"><?php
echo Format::htmlchars($this->value);
?></textarea>
</span>
<?php
}
}
class PhoneNumberWidget extends Widget {
function render($mode=false) {
$config = $this->field->getConfiguration();
list($phone, $ext) = explode("X", $this->value);
?>
<input id="<?php echo $this->id; ?>" type="text" name="<?php echo $this->name; ?>" value="<?php
echo Format::htmlchars($phone); ?>"/><?php
// Allow display of extension field even if disabled if the phone
// number being edited has an extension
if ($ext || $config['ext']) { ?> <?php echo __('Ext'); ?>:
echo $this->name; ?>-ext" value="<?php echo Format::htmlchars($ext);
?>" size="5"/>
$data = $this->field->getSource();
$base = parent::getValue();
if ($base === null)
return $base;
$ext = $data["{$this->name}-ext"];
// NOTE: 'X' is significant. Don't change it
}
}
class ChoicesWidget extends Widget {
static $media = array(
'css' => array(
'/css/jquery.multiselect.css',
),
);
if ($mode == 'view') {
if (!($val = (string) $this->field))
$val = sprintf('<span class="faded">%s</span>', __('None'));
echo $val;
return;
}
if ($mode == 'search') {
$config['multiselect'] = true;
}
// Determine the value for the default (the one listed if nothing is
// selected)
$prompt = ($config['prompt'])
? $this->field->getLocal('prompt', $config['prompt'])
: __('Select'
/* Used as a default prompt for a custom drop-down list */);
// We don't consider the 'default' when rendering in 'search' mode
if (!strcasecmp($mode, 'search')) {
$def_val = $prompt;
} else {
$def_key = $this->field->get('default');
if (!$def_key && $config['default'])
$def_key = $config['default'];
if (is_array($def_key))
$def_key = key($def_key);
$have_def = isset($choices[$def_key]);
$def_val = $have_def ? $choices[$def_key] : $prompt;
$values = $this->value;
if (!is_array($values) && $values) {
$values = array($values => $this->field->getChoice($values));
}
$values = $have_def ? array($def_key => $choices[$def_key]) : array();
?>
<select name="<?php echo $this->name; ?>[]"
id="<?php echo $this->id; ?>"
data-prompt="<?php echo $prompt; ?>"
<?php if ($config['multiselect'])
echo ' multiple="multiple" class="multiselect"'; ?>>
<?php if (!$have_def && !$config['multiselect']) { ?>
<option value="<?php echo $def_key; ?>">— <?php
echo $def_val; ?> —</option>
<?php }
if (!$have_def && $key == $def_key)
continue; ?>
<option value="<?php echo $key; ?>" <?php
if (isset($values[$key])) echo 'selected="selected"';
?>><?php echo $name; ?></option>
<?php } ?>
</select>
<?php
if ($config['multiselect']) {
?>
<script type="text/javascript">
$(function() {
.multiselect({'noneSelectedText':'<?php echo $prompt; ?>'});
});
</script>
<?php
}
function getValue() {
$value = parent::getValue();
if (!$value) return null;
// Assume multiselect
$values = array();
$choices = $this->field->getChoices();
if (is_array($value)) {
foreach($value as $k => $v) {
if (isset($choices[$v]))
$values[$v] = $choices[$v];
}
}
return $values;
}
function getJsValueGetter() {
return '%s.find(":selected").val()';
}
}
class CheckboxWidget extends Widget {
function __construct($field) {
parent::__construct($field);
$this->name = '_field-checkboxes';
}
function render($mode=false) {
if (!isset($this->value))
$this->value = $this->field->get('default');
<input id="<?php echo $this->id; ?>" style="vertical-align:top;"
type="checkbox" name="<?php echo $this->name; ?>[]" <?php
if ($this->value) echo 'checked="checked"'; ?> value="<?php
echo $this->field->get('id'); ?>"/>
<?php
if ($config['desc']) { ?>
<em style="display:inline-block"><?php
echo Format::viewableImages($config['desc']); ?></em>
<?php }
}
function getValue() {
$data = $this->field->getSource();
if (count($data))
return @in_array($this->field->get('id'), $data[$this->name]);
function getJsValueGetter() {
return '%s.is(":checked")';
}
class DatetimePickerWidget extends Widget {
function render($mode=false) {
$config = $this->field->getConfiguration();
if ($this->value) {
$this->value = is_int($this->value) ? $this->value :
strtotime($this->value);
if ($config['gmt']) {
// Convert to GMT time
$tz = new DateTimeZone($cfg->getTimezone());
$D = DateTime::createFromFormat('U', $this->value);
$this->value += $tz->getOffset($D);
}
list($hr, $min) = explode(':', date('H:i', $this->value));
$this->value = Format::date($this->value, false, false, 'UTC');
}
?>
<input type="text" name="<?php echo $this->name; ?>"
id="<?php echo $this->id; ?>"
value="<?php echo Format::htmlchars($this->value); ?>" size="12"
autocomplete="off" class="dp" />
<script type="text/javascript">
$(function() {
$('input[name="<?php echo $this->name; ?>"]').datepicker({
<?php
if ($config['min'])
echo "minDate: new Date({$config['min']}000),";
if ($config['max'])
echo "maxDate: new Date({$config['max']}000),";
elseif (!$config['future'])
echo "maxDate: new Date().getTime(),";
?>
numberOfMonths: 2,
showButtonPanel: true,
buttonImage: './images/cal.png',
dateFormat: $.translate_format('<?php echo $cfg->getDateFormat(true); ?>')
});
});
</script>
<?php
if ($config['time'])
// TODO: Add time picker -- requires time picker or selection with
// Misc::timeDropdown
echo ' ' . Misc::timeDropdown($hr, $min, $this->name . ':time');
}
/**
* Function: getValue
* Combines the datepicker date value and the time dropdown selected
* time value into a single date and time string value.
*/
function getValue() {