symphony/lib/toolkit/fields/field.select.php
<?php
/**
* @package toolkit
*/
/**
* A simple Select field that essentially maps to HTML's `<select/>`. The
* options for this field can be static, or feed from another field.
*/
class FieldSelect extends FieldTagList implements ExportableField, ImportableField
{
public function __construct()
{
parent::__construct();
$this->_name = __('Select Box');
$this->_required = true;
$this->_showassociation = true;
$this->entryQueryFieldAdapter = new EntryQueryListAdapter($this);
// Set default
$this->set('show_column', 'yes');
$this->set('location', 'sidebar');
$this->set('required', 'no');
}
/*-------------------------------------------------------------------------
Definition:
-------------------------------------------------------------------------*/
public function canToggle()
{
return ($this->get('allow_multiple_selection') === 'yes' ? false : true);
}
public function getToggleStates()
{
$values = preg_split('/,\s*/i', $this->get('static_options'), -1, PREG_SPLIT_NO_EMPTY);
if ($this->get('dynamic_options') != '') {
$this->findAndAddDynamicOptions($values);
}
$values = array_map('trim', $values);
// Fixes issues on PHP5.3. RE: #1773 ^BA
if (empty($values)) {
return $values;
}
$states = array_combine($values, $values);
if ($this->get('sort_options') === 'yes') {
natsort($states);
}
return $states;
}
public function toggleFieldData(array $data, $newState, $entry_id = null)
{
$data['value'] = $newState;
$data['handle'] = Lang::createHandle($newState);
return $data;
}
public function canFilter()
{
return true;
}
public function canPrePopulate()
{
return true;
}
public function isSortable()
{
return true;
}
public function allowDatasourceOutputGrouping()
{
// Grouping follows the same rule as toggling.
return $this->canToggle();
}
public function allowDatasourceParamOutput()
{
return true;
}
public function requiresSQLGrouping()
{
// SQL grouping follows the opposite rule as toggling.
return !$this->canToggle();
}
public function fetchSuggestionTypes()
{
return array('association', 'static');
}
/*-------------------------------------------------------------------------
Utilities:
-------------------------------------------------------------------------*/
public function findAndAddDynamicOptions(&$values)
{
if (!is_array($values)) {
$values = [];
}
$results = null;
// Fixes #1802
if (!Symphony::Database()->tableExists('tbl_entries_data_' . General::intval($this->get('dynamic_options')))) {
return;
}
// Ensure that the table has a 'value' column
if (count(Symphony::Database()
->showColumns()
->from('tbl_entries_data_' . $this->get('dynamic_options'))
->like('value')
->execute()
->rows()) === 1
) {
$results = Symphony::Database()
->select(['value'])
->distinct()
->from('tbl_entries_data_' . $this->get('dynamic_options'))
->orderBy(['value' => 'ASC'])
->execute()
->column('value');
}
// In the case of a Upload field, use 'file' instead of 'value'
if (!$results && count(Symphony::Database()
->showColumns()
->from('tbl_entries_data_' . $this->get('dynamic_options'))
->like('file')
->execute()
->rows()) === 1
) {
$results = Symphony::Database()
->select(['value'])
->distinct()
->from('tbl_entries_data_' . $this->get('dynamic_options'))
->orderBy(['file' => 'ASC'])
->execute()
->column('file');
}
if ($results) {
if ($this->get('sort_options') == 'no') {
natsort($results);
}
$values = array_merge($values, $results);
}
}
/*-------------------------------------------------------------------------
Settings:
-------------------------------------------------------------------------*/
public function findDefaults(array &$settings)
{
if (!isset($settings['allow_multiple_selection'])) {
$settings['allow_multiple_selection'] = 'no';
}
if (!isset($settings['show_association'])) {
$settings['show_association'] = 'no';
}
if (!isset($settings['sort_options'])) {
$settings['sort_options'] = 'no';
}
}
public function displaySettingsPanel(XMLElement &$wrapper, $errors = null)
{
Field::displaySettingsPanel($wrapper, $errors);
$div = new XMLElement('div', null, array('class' => 'two columns'));
// Predefined Values
$label = Widget::Label(__('Static Values'));
$label->setAttribute('class', 'column');
$label->appendChild(new XMLElement('i', __('Optional')));
$input = Widget::Input('fields['.$this->get('sortorder').'][static_options]', General::sanitize($this->get('static_options')));
$label->appendChild($input);
$div->appendChild($label);
// Dynamic Values
// Only append selected ids, load full section information asynchronously
$label = Widget::Label(__('Dynamic Values'));
$label->setAttribute('class', 'column');
$label->appendChild(new XMLElement('i', __('Optional')));
$options = array(
array('', false, __('None'))
);
if ($this->get('dynamic_options')) {
$options[] = array($this->get('dynamic_options'));
}
$label->appendChild(
Widget::Select('fields['.$this->get('sortorder').'][dynamic_options]', $options, array(
'class' => 'js-fetch-sections'
))
);
if (isset($errors['dynamic_options'])) {
$div->appendChild(Widget::Error($label, $errors['dynamic_options']));
} else {
$div->appendChild($label);
}
$wrapper->appendChild($div);
// Other settings
$div = new XMLElement('div', null, array('class' => 'two columns'));
// Allow selection of multiple items
$this->createCheckboxSetting($div, 'allow_multiple_selection', __('Allow selection of multiple options'));
// Sort options?
$this->createCheckboxSetting($div, 'sort_options', __('Sort all options alphabetically'));
$wrapper->appendChild($div);
// Associations
$fieldset = new XMLElement('fieldset');
$this->appendAssociationInterfaceSelect($fieldset);
$this->appendShowAssociationCheckbox($fieldset);
$wrapper->appendChild($fieldset);
// Requirements and table display
$this->appendStatusFooter($wrapper);
}
public function checkFields(array &$errors, $checkForDuplicates = true)
{
if (!is_array($errors)) {
$errors = array();
}
if ($this->get('static_options') == '' && ($this->get('dynamic_options') == '' || $this->get('dynamic_options') == 'none')) {
$errors['dynamic_options'] = __('At least one source must be specified, dynamic or static.');
}
Field::checkFields($errors, $checkForDuplicates);
}
public function commit()
{
if (!Field::commit()) {
return false;
}
$id = $this->get('id');
if ($id === false) {
return false;
}
$fields = array();
if ($this->get('static_options') != '') {
$fields['static_options'] = $this->get('static_options');
}
if ($this->get('dynamic_options') != '') {
$fields['dynamic_options'] = $this->get('dynamic_options');
}
$fields['allow_multiple_selection'] = ($this->get('allow_multiple_selection') ? $this->get('allow_multiple_selection') : 'no');
$fields['sort_options'] = $this->get('sort_options') === 'yes' ? 'yes' : 'no';
if (!FieldManager::saveSettings($id, $fields)) {
return false;
}
SectionManager::removeSectionAssociation($id);
// Dynamic Options isn't an array like in Select Box Link
$field_id = $this->get('dynamic_options');
if (!is_null($field_id) && is_numeric($field_id)) {
SectionManager::createSectionAssociation(null, $id, (int)$field_id, $this->get('show_association') === 'yes' ? true : false, $this->get('association_ui'), $this->get('association_editor'));
}
return true;
}
/*-------------------------------------------------------------------------
Publish:
-------------------------------------------------------------------------*/
public function displayPublishPanel(XMLElement &$wrapper, $data = null, $flagWithError = null, $fieldnamePrefix = null, $fieldnamePostfix = null, $entry_id = null)
{
$states = $this->getToggleStates();
$value = isset($data['value']) ? $data['value'] : null;
if (!is_array($value)) {
$value = array($value);
}
$options = array();
if ($this->get('required') !== 'yes') {
$options[] = array(null, false, null);
}
foreach ($states as $handle => $v) {
$options[] = array(General::sanitize($v), in_array($v, $value), General::sanitize($v));
}
$fieldname = 'fields'.$fieldnamePrefix.'['.$this->get('element_name').']'.$fieldnamePostfix;
if ($this->get('allow_multiple_selection') === 'yes') {
$fieldname .= '[]';
}
$label = Widget::Label($this->get('label'));
if ($this->get('required') !== 'yes') {
$label->appendChild(new XMLElement('i', __('Optional')));
}
$label->appendChild(Widget::Select($fieldname, $options, ($this->get('allow_multiple_selection') === 'yes' ? array('multiple' => 'multiple', 'size' => count($options)) : null)));
if ($flagWithError != null) {
$wrapper->appendChild(Widget::Error($label, $flagWithError));
} else {
$wrapper->appendChild($label);
}
}
public function checkPostFieldData($data, &$message, $entry_id = null)
{
return Field::checkPostFieldData($data, $message, $entry_id);
}
public function processRawFieldData($data, &$status, &$message = null, $simulate = false, $entry_id = null)
{
$status = self::__OK__;
if (!is_array($data)) {
return array(
'value' => $data,
'handle' => Lang::createHandle($data)
);
}
if (empty($data)) {
return null;
}
$result = array(
'value' => array(),
'handle' => array()
);
foreach ($data as $value) {
$result['value'][] = $value;
$result['handle'][] = Lang::createHandle($value);
}
return $result;
}
/*-------------------------------------------------------------------------
Output:
-------------------------------------------------------------------------*/
public function prepareTextValue($data, $entry_id = null)
{
$value = $this->prepareExportValue($data, ExportableField::LIST_OF + ExportableField::VALUE, $entry_id);
return implode(', ', $value);
}
/*-------------------------------------------------------------------------
Import:
-------------------------------------------------------------------------*/
public function prepareImportValue($data, $mode, $entry_id = null)
{
$message = $status = null;
$modes = (object)$this->getImportModes();
if (!is_array($data)) {
$data = array($data);
}
if ($mode === $modes->getValue) {
if ($this->get('allow_multiple_selection') === 'no') {
$data = array(implode('', $data));
}
return $data;
} elseif ($mode === $modes->getPostdata) {
return $this->processRawFieldData($data, $status, $message, true, $entry_id);
}
return null;
}
/*-------------------------------------------------------------------------
Export:
-------------------------------------------------------------------------*/
/**
* Return a list of supported export modes for use with `prepareExportValue`.
*
* @return array
*/
public function getExportModes()
{
return array(
'listHandle' => ExportableField::LIST_OF
+ ExportableField::HANDLE,
'listValue' => ExportableField::LIST_OF
+ ExportableField::VALUE,
'listHandleToValue' => ExportableField::LIST_OF
+ ExportableField::HANDLE
+ ExportableField::VALUE,
'getPostdata' => ExportableField::POSTDATA
);
}
/**
* Give the field some data and ask it to return a value using one of many
* possible modes.
*
* @param mixed $data
* @param integer $mode
* @param integer $entry_id
* @return array
*/
public function prepareExportValue($data, $mode, $entry_id = null)
{
$modes = (object)$this->getExportModes();
if (isset($data['handle']) && is_array($data['handle']) === false) {
$data['handle'] = array(
$data['handle']
);
}
if (isset($data['value']) && is_array($data['value']) === false) {
$data['value'] = array(
$data['value']
);
}
// Handle => Value pairs:
if ($mode === $modes->listHandleToValue) {
return isset($data['handle'], $data['value'])
? array_combine($data['handle'], $data['value'])
: array();
// Array of handles:
} elseif ($mode === $modes->listHandle) {
return isset($data['handle'])
? $data['handle']
: array();
// Array of values:
} elseif ($mode === $modes->listValue || $mode === $modes->getPostdata) {
return isset($data['value'])
? $data['value']
: array();
}
}
/*-------------------------------------------------------------------------
Filtering:
-------------------------------------------------------------------------*/
public function displayFilteringOptions(XMLElement &$wrapper)
{
$existing_options = $this->getToggleStates();
if (is_array($existing_options) && !empty($existing_options)) {
$optionlist = new XMLElement('ul');
$optionlist->setAttribute('class', 'tags');
$optionlist->setAttribute('data-interactive', 'data-interactive');
foreach ($existing_options as $option) {
$optionlist->appendChild(
new XMLElement('li', General::sanitize($option))
);
};
$wrapper->appendChild($optionlist);
}
}
/*-------------------------------------------------------------------------
Grouping:
-------------------------------------------------------------------------*/
public function groupRecords($records)
{
if (!is_array($records) || empty($records)) {
return;
}
$groups = array($this->get('element_name') => array());
foreach ($records as $r) {
$data = $r->getData($this->get('id'));
$value = General::sanitize($data['value']);
if (!isset($groups[$this->get('element_name')][$data['handle']])) {
$groups[$this->get('element_name')][$data['handle']] = array(
'attr' => array('handle' => $data['handle'], 'value' => $value),
'records' => array(),
'groups' => array()
);
}
$groups[$this->get('element_name')][$data['handle']]['records'][] = $r;
}
return $groups;
}
/*-------------------------------------------------------------------------
Events:
-------------------------------------------------------------------------*/
public function getExampleFormMarkup()
{
$states = $this->getToggleStates();
$options = array();
foreach ($states as $handle => $v) {
$options[] = array($v, null, $v);
}
$fieldname = 'fields['.$this->get('element_name').']';
if ($this->get('allow_multiple_selection') === 'yes') {
$fieldname .= '[]';
}
$label = Widget::Label($this->get('label'));
$label->appendChild(Widget::Select($fieldname, $options, ($this->get('allow_multiple_selection') === 'yes' ? array('multiple' => 'multiple') : null)));
return $label;
}
}