X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=CRM%2FCore%2FBAO%2FCustomField.php;h=ae9c4023a18b806875c4058d6ceb543267825567;hb=e34e697967b00afe2802510acd6bb91c0fc9f1a1;hp=6555a2dcf59d0d243c79f636be269185f6062111;hpb=10b0b876976ece060f78819ed879a94c79b43ab4;p=civicrm-core.git diff --git a/CRM/Core/BAO/CustomField.php b/CRM/Core/BAO/CustomField.php index 6555a2dcf5..ae9c4023a1 100644 --- a/CRM/Core/BAO/CustomField.php +++ b/CRM/Core/BAO/CustomField.php @@ -362,6 +362,47 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { return CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomField', $id, 'label'); } + /** + * @param string $context + * @return array|bool + */ + public function getOptions($context = NULL) { + CRM_Core_DAO::buildOptionsContext($context); + + if (!$this->id) { + return FALSE; + } + if (!$this->data_type || !$this->custom_group_id) { + $this->find(TRUE); + } + + if (!empty($this->option_group_id)) { + $options = CRM_Core_OptionGroup::valuesByID( + $this->option_group_id, + FALSE, + FALSE, + FALSE, + 'label', + !($context == 'validate' || $context == 'get') + ); + } + elseif ($this->data_type === 'StateProvince') { + $options = CRM_Core_Pseudoconstant::stateProvince(); + } + elseif ($this->data_type === 'Country') { + $options = $context == 'validate' ? CRM_Core_Pseudoconstant::countryIsoCode() : CRM_Core_Pseudoconstant::country(); + } + elseif ($this->data_type === 'Boolean') { + $options = $context == 'validate' ? array(0, 1) : CRM_Core_SelectValues::boolean(); + } + else { + return FALSE; + } + CRM_Utils_Hook::customFieldOptions($this->id, $options, FALSE); + CRM_Utils_Hook::fieldOptions($this->getEntity(), "custom_{$this->id}", $options, array('context' => $context)); + return $options; + } + /** * Store and return an array of all active custom fields. * @@ -700,11 +741,11 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { * @param int $fieldID * The custom field ID. * - * @return CRM_Core_DAO_CustomField + * @return CRM_Core_BAO_CustomField * The field object. */ public static function getFieldObject($fieldID) { - $field = new CRM_Core_DAO_CustomField(); + $field = new CRM_Core_BAO_CustomField(); // check if we can get the field values from the system cache $cacheKey = "CRM_Core_DAO_CustomField_{$fieldID}"; @@ -729,33 +770,28 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { } /** - * This function for building custom fields. + * Add a custom field to an existing form. * * @param CRM_Core_Form $qf * Form object (reference). * @param string $elementName * Name of the custom field. * @param int $fieldId - * @param bool $inactiveNeeded - * -deprecated. * @param bool $useRequired * True if required else false. * @param bool $search * True if used for search else false. * @param string $label * Label for custom field. + * @return \HTML_QuickForm_Element|null + * @throws \CiviCRM_API3_Exception */ public static function addQuickFormElement( - &$qf, - $elementName, - $fieldId, - $inactiveNeeded = FALSE, - $useRequired = TRUE, - $search = FALSE, - $label = NULL + $qf, $elementName, $fieldId, $useRequired = TRUE, $search = FALSE, $label = NULL ) { $field = self::getFieldObject($fieldId); $widget = $field->html_type; + $element = NULL; // Custom field HTML should indicate group+field name $groupName = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $field->custom_group_id); @@ -784,10 +820,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { ))); if ($isSelect) { - $options = CRM_Utils_Array::value('values', civicrm_api3('contact', 'getoptions', array( - 'field' => "custom_$fieldId", - 'context' => $search ? 'search' : 'create', - ), array())); + $options = $field->getOptions($search ? 'search' : 'create'); // Consolidate widget types to simplify the below switch statement if ($search || ($widget !== 'AdvMulti-Select' && strpos($widget, 'Select') !== FALSE)) { @@ -806,8 +839,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { // Add data for popup link. Normally this is handled by CRM_Core_Form->addSelect if ($field->option_group_id && !$search && $widget == 'Select' && CRM_Core_Permission::check('administer CiviCRM')) { $selectAttributes += array( - 'data-api-entity' => 'contact', - // FIXME: This works because the getoptions api isn't picky about custom fields, but it's WRONG + 'data-api-entity' => $field->getEntity(), 'data-api-field' => 'custom_' . $field->id, 'data-option-edit-path' => 'civicrm/admin/options/' . CRM_Core_DAO::getFieldValue('CRM_Core_DAO_OptionGroup', $field->option_group_id), ); @@ -828,7 +860,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { $qf->add('text', $elementName . '_to', ts('To'), $field->attributes); } else { - $element = &$qf->add('text', $elementName, $label, + $element = $qf->add('text', $elementName, $label, $field->attributes, $useRequired && !$search ); @@ -852,7 +884,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { if ($field->text_length) { $attributes .= ' maxlength=' . $field->text_length; } - $element = &$qf->add('textarea', + $element = $qf->add('textarea', $elementName, $label, $attributes, @@ -861,37 +893,19 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { break; case 'Select Date': + $attr = array('data-crm-custom' => $dataCrmCustomVal); + $params = array( + 'date' => $field->date_format, + 'minDate' => isset($field->start_date_years) ? (date('Y') - $field->start_date_years) . '-01-01' : NULL, + 'maxDate' => isset($field->end_date_years) ? (date('Y') + $field->end_date_years) . '-01-01' : NULL, + 'time' => $field->time_format ? $field->time_format * 12 : FALSE, + ); if ($field->is_search_range && $search) { - $qf->addDate($elementName . '_from', $label . ' - ' . ts('From'), FALSE, - array( - 'format' => $field->date_format, - 'timeFormat' => $field->time_format, - 'startOffset' => $field->start_date_years, - 'endOffset' => $field->end_date_years, - 'data-crm-custom' => $dataCrmCustomVal, - ) - ); - - $qf->addDate($elementName . '_to', ts('To'), FALSE, - array( - 'format' => $field->date_format, - 'timeFormat' => $field->time_format, - 'startOffset' => $field->start_date_years, - 'endOffset' => $field->end_date_years, - 'data-crm-custom' => $dataCrmCustomVal, - ) - ); + $qf->add('datepicker', $elementName . '_from', $label, $attr + array('placeholder' => ts('From')), FALSE, $params); + $qf->add('datepicker', $elementName . '_to', NULL, $attr + array('placeholder' => ts('To')), FALSE, $params); } else { - $required = $useRequired && !$search; - - $qf->addDate($elementName, $label, $required, array( - 'format' => $field->date_format, - 'timeFormat' => $field->time_format, - 'startOffset' => $field->start_date_years, - 'endOffset' => $field->end_date_years, - 'data-crm-custom' => $dataCrmCustomVal, - )); + $element = $qf->add('datepicker', $elementName, $label, $attr, $useRequired && !$search, $params); } break; @@ -900,12 +914,12 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { foreach ($options as $v => $l) { $choice[] = $qf->createElement('radio', NULL, '', $l, (string) $v, $field->attributes); } - $group = $qf->addGroup($choice, $elementName, $label); + $element = $qf->addGroup($choice, $elementName, $label); if ($useRequired && !$search) { $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required'); } else { - $group->setAttribute('allowClear', TRUE); + $element->setAttribute('allowClear', TRUE); } break; @@ -914,7 +928,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { if (empty($selectAttributes['multiple'])) { $options = array('' => $placeholder) + $options; } - $qf->add('select', $elementName, $label, $options, $useRequired && !$search, $selectAttributes); + $element = $qf->add('select', $elementName, $label, $options, $useRequired && !$search, $selectAttributes); // Add and/or option for fields that store multiple values if ($search && self::isSerialized($field)) { @@ -929,7 +943,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { break; case 'AdvMulti-Select': - $include =& $qf->addElement( + $element = $qf->addElement( 'advmultiselect', $elementName, $label, $options, @@ -941,8 +955,8 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { ) ); - $include->setButtonAttributes('add', array('value' => ts('Add >>'))); - $include->setButtonAttributes('remove', array('value' => ts('<< Remove'))); + $element->setButtonAttributes('add', array('value' => ts('Add >>'))); + $element->setButtonAttributes('remove', array('value' => ts('<< Remove'))); if ($useRequired && !$search) { $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required'); @@ -954,7 +968,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { foreach ($options as $v => $l) { $check[] = &$qf->addElement('advcheckbox', $v, NULL, $l, array('data-crm-custom' => $dataCrmCustomVal)); } - $qf->addGroup($check, $elementName, $label); + $element = $qf->addGroup($check, $elementName, $label); if ($useRequired && !$search) { $qf->addRule($elementName, ts('%1 is a required field.', array(1 => $label)), 'required'); } @@ -963,9 +977,9 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { case 'File': // we should not build upload file in search mode if ($search) { - return; + return NULL; } - $qf->add( + $element = $qf->add( strtolower($field->html_type), $elementName, $label, @@ -984,7 +998,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { if ($field->text_length) { $attributes['maxlength'] = $field->text_length; } - $qf->add('wysiwyg', $elementName, $label, $attributes, $search); + $element = $qf->add('wysiwyg', $elementName, $label, $attributes, $search); break; case 'Autocomplete-Select': @@ -1001,10 +1015,8 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { } if ($field->data_type == 'ContactReference') { $attributes['class'] = (isset($attributes['class']) ? $attributes['class'] . ' ' : '') . 'crm-form-contact-reference huge'; - $attributes['data-api-entity'] = 'contact'; - $qf->add('text', $elementName, $label, $attributes, - $useRequired && !$search - ); + $attributes['data-api-entity'] = 'Contact'; + $element = $qf->add('text', $elementName, $label, $attributes, $useRequired && !$search); $urlParams = "context=customfield&id={$field->id}"; @@ -1024,7 +1036,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { 'params' => array('option_group_id' => $field->option_group_id), ), ); - $qf->addEntityRef($elementName, $label, $attributes, $useRequired && !$search); + $element = $qf->addEntityRef($elementName, $label, $attributes, $useRequired && !$search); } $qf->assign('customUrls', $customUrls); @@ -1071,6 +1083,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { if ($field->is_view && !$search) { $qf->freeze($elementName); } + return $element; } /** @@ -1098,222 +1111,132 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { } /** - * Find the display value for this field. - * - * @param mixed $value - * The custom field value. - * @param int $id - * The custom field id. - * @param array $options - * The assoc array of option name/value pairs. - * - * @param int $contactID - * @param int $fieldID + * @param string|int|array|null $value + * @param CRM_Core_BAO_CustomField|int|array|string $field + * @param $contactId * * @return string - * the display value + * @throws \Exception */ - public static function getDisplayValue($value, $id, &$options, $contactID = NULL, $fieldID = NULL) { - $option = &$options[$id]; - $attributes = &$option['attributes']; - $html_type = $attributes['html_type']; - $data_type = $attributes['data_type']; - - return self::getDisplayValueCommon($value, - $option, - $html_type, - $data_type, - $contactID, - $fieldID - ); + public static function displayValue($value, $field, $contactId = NULL) { + $field = is_array($field) ? $field['id'] : $field; + $fieldId = is_object($field) ? $field->id : (int) str_replace('custom_', '', $field); + + if (!$fieldId) { + throw new Exception('CRM_Core_BAO_CustomField::displayValue requires a field id'); + } + + if (!is_a($field, 'CRM_Core_BAO_CustomField')) { + $field = self::getFieldObject($fieldId); + } + + $fieldInfo = array('options' => $field->getOptions()) + (array) $field; + + return self::formatDisplayValue($value, $fieldInfo, $contactId); } /** - * Get display value. + * Lower-level logic for rendering a custom field value * - * @param string $value - * @param array $option - * @param string $html_type - * @param string $data_type - * @param int $contactID - * @param int $fieldID + * @param string|array $value + * @param array $field + * @param int|null $entityId * - * @return array|mixed|null|string + * @return string */ - public static function getDisplayValueCommon( - $value, - &$option, - $html_type, - $data_type, - $contactID = NULL, - $fieldID = NULL - ) { - $display = $value; - - if ($fieldID && - (($html_type == 'Radio' && $data_type != 'Boolean') || - ($html_type == 'Autocomplete-Select' && $data_type != 'ContactReference') || - $html_type == 'Select' || - $html_type == 'CheckBox' || - $html_type == 'AdvMulti-Select' || - $html_type == 'Multi-Select' - ) - ) { - CRM_Utils_Hook::customFieldOptions($fieldID, $option); + private static function formatDisplayValue($value, $field, $entityId = NULL) { + + if (self::isSerialized($field) && !is_array($value)) { + $value = CRM_Utils_Array::explodePadded($value); + } + // CRM-12989 fix + if ($field['html_type'] == 'CheckBox') { + CRM_Utils_Array::formatArrayKeys($value); } - switch ($html_type) { - case 'Radio': - if ($data_type == 'Boolean') { - $options = array('No', 'Yes'); - } - else { - $options = $option; - } - if (is_array($value)) { - $display = NULL; - foreach ($value as $data) { - $display .= $display ? ', ' . $options[$data] : $options[$data]; - } - } - else { - $display = CRM_Utils_Array::value($value, $options); - } - break; + $display = is_array($value) ? implode(', ', $value) : (string) $value; - case 'Autocomplete-Select': - if ($data_type == 'ContactReference' && - $value - ) { - $display = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'display_name'); - } - elseif (is_array($value)) { - $display = NULL; - foreach ($value as $data) { - $display .= $display ? ', ' . $option[$data] : $option[$data]; - } - } - else { - $display = CRM_Utils_Array::value($value, $option); - } - break; + switch ($field['html_type']) { case 'Select': - if (is_array($value)) { - $display = NULL; - foreach ($value as $data) { - $display .= $display ? ', ' . $option[$data] : $option[$data]; - } - } - else { - $display = CRM_Utils_Array::value($value, $option); - } - break; - + case 'Autocomplete-Select': + case 'Radio': + case 'Select Country': + case 'Select State/Province': case 'CheckBox': case 'AdvMulti-Select': case 'Multi-Select': - if (is_array($value)) { - if ($html_type == 'CheckBox') { - // CRM-12989 fix - CRM_Utils_Array::formatArrayKeys($value); - } - - $checkedData = $value; - } - else { - $checkedData = explode(CRM_Core_DAO::VALUE_SEPARATOR, - substr($value, 1, -1) - ); - if ($html_type == 'CheckBox') { - $newData = array(); - foreach ($checkedData as $v) { - $v = str_replace(CRM_Core_DAO::VALUE_SEPARATOR, '', $v); - $newData[] = $v; - } - $checkedData = $newData; - } - } - - $v = array(); - foreach ($checkedData as $key => $val) { - $v[] = CRM_Utils_Array::value($val, $option); - } - if (!empty($v)) { - $display = implode(', ', $v); + case 'Multi-Select State/Province': + case 'Multi-Select Country': + if ($field['data_type'] == 'ContactReference' && $value) { + $display = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $value, 'display_name'); } - break; - - case 'Select Date': - if (is_array($value)) { + elseif (is_array($value)) { + $v = array(); foreach ($value as $key => $val) { - $display[$key] = CRM_Utils_Date::customFormat($val); + $v[] = CRM_Utils_Array::value($val, $field['options']); } + $display = implode(', ', $v); } else { - // remove time element display if time is not set - if (empty($option['attributes']['time_format'])) { - $value = substr($value, 0, 10); - } - $display = CRM_Utils_Date::customFormat($value); + $display = CRM_Utils_Array::value($value, $field['options'], ''); } break; - case 'Select State/Province': - case 'Multi-Select State/Province': - case 'Select Country': - case 'Multi-Select Country': - if (strstr($html_type, 'State/Province')) { - $option = CRM_Core_PseudoConstant::stateProvince(FALSE, FALSE); - } - else { - $option = CRM_Core_PseudoConstant::country(FALSE, FALSE); - } - // process multi-select state/country field values - if (!is_array($value)) { - $value = explode(CRM_Core_DAO::VALUE_SEPARATOR, $value); - } - - $display = NULL; - foreach ($value as $data) { - $display .= ($display && !empty($option[$data])) ? ', ' . $option[$data] : $option[$data]; + case 'Select Date': + $customFormat = NULL; + + // FIXME: Are there any legitimate reasons why $value would be an array? + // Or should we throw an exception here if it is? + $value = is_array($value) ? CRM_Utils_Array::first($value) : $value; + + $actualPHPFormats = CRM_Core_SelectValues::datePluginToPHPFormats(); + $format = CRM_Utils_Array::value('date_format', $field); + + if ($format) { + if (array_key_exists($format, $actualPHPFormats)) { + $customTimeFormat = (array) $actualPHPFormats[$format]; + switch (CRM_Utils_Array::value('time_format', $field)) { + case 1: + $customTimeFormat[] = 'g:iA'; + break; + + case 2: + $customTimeFormat[] = 'G:i'; + break; + + default: + // if time is not selected remove time from value + $value = substr($value, 0, 10); + } + $customFormat = implode(" ", $customTimeFormat); + } } + $display = CRM_Utils_Date::processDate($value, NULL, FALSE, $customFormat); break; case 'File': // In the context of displaying a profile, show file/image - if ($contactID && $value) { - $url = self::getFileURL($contactID, $fieldID, $value); - if ($url) { - $display = $url['file_url']; + if ($value) { + if ($entityId) { + $url = self::getFileURL($entityId, $field['id']); + if ($url) { + $display = $url['file_url']; + } + } + else { + // In other contexts show a paperclip icon + $icons = CRM_Core_BAO_File::paperIconAttachment('*', $value); + $display = $icons[$value]; } - } - // In other contexts show a paperclip icon - elseif ($value) { - $icons = CRM_Core_BAO_File::paperIconAttachment('*', $value); - $display = $icons[$value]; } break; case 'TextArea': - if (empty($value)) { - $display = ''; - } - else { - $display = is_array($value) ? nl2br(implode(', ', $value)) : nl2br($value); - } + $display = nl2br($display); break; - - case 'Link': - case 'Text': - if (empty($value)) { - $display = ''; - } - else { - $display = is_array($value) ? implode(', ', $value) : $value; - } } - return $display ? $display : $value; + return $display; } /** @@ -1405,17 +1328,6 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField { } break; - case 'Select Date': - if ($value) { - list($defaults[$elementName], $defaults[$elementName . '_time']) = CRM_Utils_Date::setDateDefaults( - $value, - NULL, - $customField->date_format, - $customField->time_format - ); - } - break; - case 'Autocomplete-Select': if ($customField->data_type == 'ContactReference') { if (is_numeric($value)) { @@ -1628,13 +1540,7 @@ SELECT id $customFields[$customFieldId]['html_type'] == 'AdvMulti-Select' ) { if ($value) { - // Note that only during merge this is not an array, - // and you can directly use value, CRM-4385 - if (is_array($value)) { - $value = CRM_Core_DAO::VALUE_SEPARATOR . implode(CRM_Core_DAO::VALUE_SEPARATOR, - array_values($value) - ) . CRM_Core_DAO::VALUE_SEPARATOR; - } + $value = CRM_Utils_Array::implodePadded($value); } else { $value = ''; @@ -2196,6 +2102,7 @@ ORDER BY html_type"; * @param int $entityID * @param string $customFieldExtends * @param bool $inline + * @param bool $checkPermissions * * @return array */ @@ -2203,7 +2110,8 @@ ORDER BY html_type"; &$params, $entityID, $customFieldExtends, - $inline = FALSE + $inline = FALSE, + $checkPermissions = TRUE ) { $customData = array(); @@ -2226,7 +2134,8 @@ ORDER BY html_type"; $customFieldExtends, $customFieldInfo[1], $entityID, - $inline + $inline, + $checkPermissions ); } } @@ -2234,61 +2143,8 @@ ORDER BY html_type"; } /** - * Build option. * - * @param array $field - * @param array $options - * - * @throws Exception */ - public static function buildOption($field, &$options) { - // Fixme - adding anything but options to the $options array is a bad idea - // What if an option had the key 'attributes'? - $options['attributes'] = array( - 'label' => $field['label'], - 'data_type' => $field['data_type'], - 'html_type' => $field['html_type'], - ); - - $optionGroupID = NULL; - if (($field['html_type'] == 'CheckBox' || - $field['html_type'] == 'Radio' || - $field['html_type'] == 'Select' || - $field['html_type'] == 'AdvMulti-Select' || - $field['html_type'] == 'Multi-Select' || - ($field['html_type'] == 'Autocomplete-Select' && $field['data_type'] != 'ContactReference') - ) - ) { - if ($field['option_group_id']) { - $optionGroupID = $field['option_group_id']; - } - elseif ($field['data_type'] != 'Boolean') { - CRM_Core_Error::fatal(); - } - } - - // build the cache for custom values with options (label => value) - if ($optionGroupID != NULL) { - $query = " -SELECT label, value - FROM civicrm_option_value - WHERE option_group_id = $optionGroupID -"; - - $dao = CRM_Core_DAO::executeQuery($query); - while ($dao->fetch()) { - if ($field['data_type'] == 'Int' || $field['data_type'] == 'Float') { - $num = round($dao->value, 2); - $options["$num"] = $dao->label; - } - else { - $options[$dao->value] = $dao->label; - } - } - - CRM_Utils_Hook::customFieldOptions($field['id'], $options); - } - } /** * Get custom field ID. @@ -2510,7 +2366,17 @@ WHERE cf.id = %1 AND cg.is_multiple = 1"; } /** - * Get options for field. + * Get api entity for this field + * + * @return string + */ + public function getEntity() { + $entity = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $this->custom_group_id, 'extends'); + return in_array($entity, array('Individual', 'Household', 'Organization')) ? 'Contact' : $entity; + } + + /** + * Set pseudoconstant properties for field metadata. * * @param array $field * @param string|null $optionGroupName