3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * This class generates form components for Options.
21 class CRM_Admin_Form_Options
extends CRM_Admin_Form
{
24 * The option group name.
31 * The option group name in display format (capitalized, without underscores...etc)
38 * Is this Option Group Domain Specific
41 protected $_domainSpecific = FALSE;
46 public $submitOnce = TRUE;
51 public function preProcess() {
53 $session = CRM_Core_Session
::singleton();
54 if (!$this->_gName
&& !empty($this->urlPath
[3])) {
55 $this->_gName
= $this->urlPath
[3];
57 if (!$this->_gName
&& !empty($_GET['gid'])) {
58 $this->_gName
= CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionGroup', (int) $_GET['gid'], 'name');
61 $this->set('gName', $this->_gName
);
64 $this->_gName
= $this->get('gName');
66 $this->_gid
= CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionGroup',
71 $this->_gLabel
= CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionGroup', $this->_gid
, 'title');
72 $this->_domainSpecific
= CRM_Core_OptionGroup
::isDomainOptionGroup($this->_gName
);
73 $url = "civicrm/admin/options/{$this->_gName}";
76 if (($this->_action
& CRM_Core_Action
::DELETE
) &&
77 in_array($this->_gName
, ['email_greeting', 'postal_greeting', 'addressee'])
79 // Don't allow delete if the option value belongs to addressee, postal or email greetings and is in use.
80 $findValue = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id
, 'value');
81 $queryParam = [1 => [$findValue, 'Integer']];
82 $columnName = $this->_gName
. "_id";
83 $sql = "SELECT count(id) FROM civicrm_contact WHERE " . $columnName . " = %1";
84 $isInUse = CRM_Core_DAO
::singleValueQuery($sql, $queryParam);
86 $scriptURL = "<a href='" . CRM_Utils_System
::docURL2('Update Greetings and Address Data for Contacts', TRUE, NULL, NULL, NULL, "wiki") . "'>" . ts('Learn more about a script that can automatically update contact addressee and greeting options.') . "</a>";
87 CRM_Core_Session
::setStatus(ts('The selected %1 option has <strong>not been deleted</strong> because it is currently in use. Please update these contacts to use a different format before deleting this option. %2', [
90 ]), ts('Sorry'), 'error');
91 $redirect = CRM_Utils_System
::url($url, $params);
92 CRM_Utils_System
::redirect($redirect);
96 $session->pushUserContext(CRM_Utils_System
::url($url, $params));
97 $this->assign('id', $this->_id
);
99 if ($this->_id
&& CRM_Core_OptionGroup
::isDomainOptionGroup($this->_gName
)) {
100 $domainID = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id
, 'domain_id', 'id');
101 if (CRM_Core_Config
::domainID() != $domainID) {
102 CRM_Core_Error
::statusBounce(ts('You do not have permission to access this page.'));
108 * Set default values for the form.
110 public function setDefaultValues() {
111 $defaults = parent
::setDefaultValues();
113 // Default weight & value
114 $fieldValues = ['option_group_id' => $this->_gid
];
115 foreach (['weight', 'value'] as $field) {
116 if (!isset($defaults[$field]) ||
$defaults[$field] === '') {
117 $defaults[$field] = CRM_Utils_Weight
::getDefaultWeight('CRM_Core_DAO_OptionValue', $fieldValues, $field);
121 // setDefault of contact types for email greeting, postal greeting, addressee, CRM-4575
122 if (in_array($this->_gName
, [
127 $defaults['contactOptions'] = (CRM_Utils_Array
::value('filter', $defaults)) ?
$defaults['filter'] : NULL;
130 if ($this->_gName
== 'payment_instrument' && $this->_id
) {
131 $defaults['financial_account_id'] = CRM_Contribute_PseudoConstant
::getRelationalFinancialAccount($this->_id
, NULL, 'civicrm_option_value');
133 if (empty($this->_id
) ||
!CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id
, 'color')) {
134 $defaults['color'] = '#ffffff';
140 * Build the form object.
142 public function buildQuickForm() {
143 parent
::buildQuickForm();
144 $this->setPageTitle(ts('%1 Option', [1 => $this->_gLabel
]));
146 if ($this->_action
& CRM_Core_Action
::DELETE
) {
150 $optionGroup = \Civi\Api4\OptionGroup
::get(FALSE)
151 ->addWhere('id', '=', $this->_gid
)
152 ->execute()->first();
154 $this->applyFilter('__ALL__', 'trim');
158 $isReserved = (bool) CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id
, 'is_reserved');
164 CRM_Core_DAO
::getAttribute('CRM_Core_DAO_OptionValue', 'label'),
168 if ($this->_gName
!= 'activity_type') {
172 CRM_Core_DAO
::getAttribute('CRM_Core_DAO_OptionValue', 'value'),
175 $this->addRule('value',
176 ts('This Value already exists in the database for this option group. Please select a different Value.'),
178 ['CRM_Core_DAO_OptionValue', $this->_id
, $this->_gid
, 'value', $this->_domainSpecific
]
182 // Add icon & color if this option group supports it.
183 if ($optionGroup['option_value_fields'] && in_array('icon', $optionGroup['option_value_fields'])) {
184 $this->add('text', 'icon', ts('Icon'), ['class' => 'crm-icon-picker', 'title' => ts('Choose Icon'), 'allowClear' => TRUE]);
186 if ($optionGroup['option_value_fields'] && in_array('color', $optionGroup['option_value_fields'])) {
187 $this->add('color', 'color', ts('Color'));
190 if (!in_array($this->_gName
, ['email_greeting', 'postal_greeting', 'addressee'])
193 $this->addRule('label',
194 ts('This Label already exists in the database for this option group. Please select a different Label.'),
196 ['CRM_Core_DAO_OptionValue', $this->_id
, $this->_gid
, 'label', $this->_domainSpecific
]
200 if ($this->_gName
== 'case_status') {
202 'Opened' => ts('Opened'),
203 'Closed' => ts('Closed'),
206 $grouping = $this->add('select',
216 if ($this->_gName
== 'payment_instrument') {
217 $accountType = CRM_Core_PseudoConstant
::accountOptionValues('financial_account_type', NULL, " AND v.name = 'Asset' ");
218 $financialAccount = CRM_Contribute_PseudoConstant
::financialAccount(NULL, key($accountType));
220 $this->add('select', 'financial_account_id', ts('Financial Account'),
221 ['' => ts('- select -')] +
$financialAccount,
226 if ($this->_gName
== 'activity_status') {
231 CRM_Activity_BAO_Activity
::INCOMPLETE
=> ts('Incomplete'),
232 CRM_Activity_BAO_Activity
::COMPLETED
=> ts('Completed'),
233 CRM_Activity_BAO_Activity
::CANCELLED
=> ts('Cancelled'),
237 if ($this->_gName
== 'redaction_rule') {
238 $this->add('checkbox',
240 ts('Regular Expression?')
243 if ($this->_gName
== 'participant_listing') {
247 CRM_Core_DAO
::getAttribute('CRM_Core_DAO_OptionValue', 'description')
251 // Hard-coding attributes here since description is still stored as varchar and not text in the schema. dgg
252 $this->add('wysiwyg', 'description',
254 ['rows' => 4, 'cols' => 80],
255 $this->_gName
== 'custom_search'
259 if ($this->_gName
== 'event_badge') {
263 CRM_Core_DAO
::getAttribute('CRM_Core_DAO_OptionValue', 'name')
270 CRM_Core_DAO
::getAttribute('CRM_Core_DAO_OptionValue', 'weight'),
273 $this->addRule('weight', ts('is a numeric field'), 'numeric');
275 // If CiviCase enabled AND "Add" mode OR "edit" mode for non-reserved activities, only allow user to pick Core or CiviCase component.
276 // FIXME: Each component should define whether adding new activity types is allowed.
277 if ($this->_gName
== 'activity_type' && CRM_Core_Component
::isEnabled("CiviCase") &&
278 (($this->_action
& CRM_Core_Action
::ADD
) ||
!$isReserved)
280 $caseID = CRM_Core_Component
::getComponentID('CiviCase');
281 $components = ['' => ts('Contacts AND Cases'), $caseID => ts('Cases Only')];
289 $enabled = $this->add('checkbox', 'is_active', ts('Enabled?'));
295 // fix for CRM-3552, CRM-4575
296 $showIsDefaultGroups = [
300 'from_email_address',
304 'payment_instrument',
305 'communication_style',
310 if (in_array($this->_gName
, $showIsDefaultGroups)) {
311 $this->assign('showDefault', TRUE);
312 $this->add('checkbox', 'is_default', ts('Default Option?'));
315 // get contact type for which user want to create a new greeting/addressee type, CRM-4575
316 if (in_array($this->_gName
, ['email_greeting', 'postal_greeting', 'addressee'])
320 1 => ts('Individual'),
321 2 => ts('Household'),
322 3 => ts('Organization'),
323 4 => ts('Multiple Contact Merge'),
325 $this->add('select', 'contactOptions', ts('Contact Type'), ['' => '-select-'] +
$values, TRUE);
326 $this->assign('showContactFilter', TRUE);
329 if ($this->_gName
== 'participant_status') {
330 // For Participant Status options, expose the 'filter' field to track which statuses are "Counted", and the Visibility field
331 $this->add('checkbox', 'filter', ts('Counted?'));
332 $this->add('select', 'visibility_id', ts('Visibility'), CRM_Core_PseudoConstant
::visibility());
334 if ($this->_gName
== 'participant_role') {
335 // For Participant Role options, expose the 'filter' field to track which statuses are "Counted"
336 $this->add('checkbox', 'filter', ts('Counted?'));
339 $this->addFormRule(['CRM_Admin_Form_Options', 'formRule'], $this);
345 * @param array $fields
346 * The input form values.
347 * @param array $files
348 * The uploaded files if any.
350 * Current form object.
353 * array of errors / empty array.
354 * @throws \CRM_Core_Exception
356 public static function formRule($fields, $files, $self) {
358 if ($self->_gName
== 'case_status' && empty($fields['grouping'])) {
359 $errors['grouping'] = ts('Status class is a required field');
362 if (in_array($self->_gName
, ['email_greeting', 'postal_greeting', 'addressee'])
363 && empty($self->_defaultValues
['is_reserved'])
365 $label = $fields['label'];
366 $condition = " AND v.label = '{$label}' ";
367 $values = CRM_Core_OptionGroup
::values($self->_gName
, FALSE, FALSE, FALSE, $condition, 'filter');
368 $checkContactOptions = TRUE;
370 if ($self->_id
&& ($self->_defaultValues
['contactOptions'] == $fields['contactOptions'])) {
371 $checkContactOptions = FALSE;
374 if ($checkContactOptions && in_array($fields['contactOptions'], $values)) {
375 $errors['label'] = ts('This Label already exists in the database for the selected contact type.');
379 if ($self->_gName
== 'from_email_address') {
380 $formEmail = CRM_Utils_Mail
::pluckEmailFromHeader($fields['label']);
381 if (!CRM_Utils_Rule
::email($formEmail)) {
382 $errors['label'] = ts('Please enter a valid email address.');
385 $formName = explode('"', $fields['label']);
386 if (empty($formName[1]) ||
count($formName) != 3) {
387 $errors['label'] = ts('Please follow the proper format for From Email Address');
391 $dataType = self
::getOptionGroupDataType($self->_gName
);
392 if ($dataType && $self->_gName
!== 'activity_type') {
393 $validate = CRM_Utils_Type
::validate($fields['value'], $dataType, FALSE);
394 if ($validate === FALSE) {
395 CRM_Core_Session
::setStatus(
396 ts('Data Type of the value field for this option value does not match %1.', [1 => $dataType]),
397 ts('Value field Data Type mismatch'));
404 * Get the DataType for a specified Option Group.
406 * @param string $optionGroupName name of the option group
408 * @return string|null
410 public static function getOptionGroupDataType($optionGroupName) {
411 $optionGroupId = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionGroup', $optionGroupName, 'id', 'name');
413 $dataType = CRM_Core_BAO_OptionGroup
::getDataType($optionGroupId);
418 * Process the form submission.
420 public function postProcess() {
421 if ($this->_action
& CRM_Core_Action
::DELETE
) {
422 $fieldValues = ['option_group_id' => $this->_gid
];
423 CRM_Utils_Weight
::delWeight('CRM_Core_DAO_OptionValue', $this->_id
, $fieldValues);
425 if (CRM_Core_BAO_OptionValue
::del($this->_id
)) {
426 if ($this->_gName
== 'phone_type') {
427 CRM_Core_BAO_Phone
::setOptionToNull(CRM_Utils_Array
::value('value', $this->_defaultValues
));
430 CRM_Core_Session
::setStatus(ts('Selected %1 type has been deleted.', [1 => $this->_gLabel
]), ts('Record Deleted'), 'success');
433 CRM_Core_Session
::setStatus(ts('Selected %1 type has not been deleted.', [1 => $this->_gLabel
]), ts('Sorry'), 'error');
434 CRM_Utils_Weight
::correctDuplicateWeights('CRM_Core_DAO_OptionValue', $fieldValues);
438 $params = $this->exportValues();
440 // allow multiple defaults within group.
441 $allowMultiDefaults = ['email_greeting', 'postal_greeting', 'addressee', 'from_email_address'];
442 if (in_array($this->_gName
, $allowMultiDefaults)) {
443 if ($this->_gName
== 'from_email_address') {
444 $params['reset_default_for'] = ['domain_id' => CRM_Core_Config
::domainID()];
446 elseif ($filter = CRM_Utils_Array
::value('contactOptions', $params)) {
447 $params['filter'] = $filter;
448 $params['reset_default_for'] = ['filter' => "0, " . $params['filter']];
451 //make sure we only have a single space, CRM-6977 and dev/mail/15
452 if ($this->_gName
== 'from_email_address') {
453 $params['label'] = $this->sanitizeFromEmailAddress($params['label']);
457 // set value of filter if not present in params
458 if ($this->_id
&& !array_key_exists('filter', $params)) {
459 if ($this->_gName
== 'participant_role') {
460 $params['filter'] = 0;
463 $params['filter'] = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionValue', $this->_id
, 'filter', 'id');
467 if (isset($params['color']) && strtolower($params['color']) == '#ffffff') {
468 $params['color'] = 'null';
471 $optionValue = CRM_Core_OptionValue
::addOptionValue($params, $this->_gName
, $this->_action
, $this->_id
);
473 CRM_Core_Session
::setStatus(ts('The %1 \'%2\' has been saved.', [
475 2 => $optionValue->label
,
476 ]), ts('Saved'), 'success');
478 $this->ajaxResponse
['optionValue'] = $optionValue->toArray();
482 public function sanitizeFromEmailAddress($email) {
483 preg_match("/^\"(.*)\" *<([^@>]*@[^@>]*)>$/", $email, $parts);
484 return "\"{$parts[1]}\" <$parts[2]>";