CRM-14996 - Support multi-valued fields for state/country chain-select
[civicrm-core.git] / CRM / Contact / Form / Search / Criteria.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.5 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2014 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
9 | |
10 | CiviCRM is free software; you can copy, modify, and distribute it |
11 | under the terms of the GNU Affero General Public License |
12 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
13 | |
14 | CiviCRM is distributed in the hope that it will be useful, but |
15 | WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
17 | See the GNU Affero General Public License for more details. |
18 | |
19 | You should have received a copy of the GNU Affero General Public |
20 | License and the CiviCRM Licensing Exception along |
21 | with this program; if not, contact CiviCRM LLC |
22 | at info[AT]civicrm[DOT]org. If you have questions about the |
23 | GNU Affero General Public License or the licensing of CiviCRM, |
24 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
25 +--------------------------------------------------------------------+
26 */
27
28 /**
29 *
30 * @package CRM
31 * @copyright CiviCRM LLC (c) 2004-2014
32 * $Id$
33 *
34 */
35 class CRM_Contact_Form_Search_Criteria {
36 /**
37 * @param $form CRM_Core_Form
38 */
39 static function basic(&$form) {
40 $form->addElement('hidden', 'hidden_basic', 1);
41
42 if ($form->_searchOptions['contactType']) {
43 // add checkboxes for contact type
44 //@todo FIXME - using the CRM_Core_DAO::VALUE_SEPARATOR creates invalid html - if you can find the form
45 // this is loaded onto then replace with something like '__' & test
46 $separator = CRM_Core_DAO::VALUE_SEPARATOR;
47 $contactTypes = CRM_Contact_BAO_ContactType::getSelectElements(FALSE, TRUE, $separator);
48
49 if ($contactTypes) {
50 $form->add('select', 'contact_type', ts('Contact Type(s)'), $contactTypes, FALSE,
51 array('id' => 'contact_type', 'multiple' => 'multiple', 'class' => 'crm-select2', 'style' => 'width: 100%;')
52 );
53 }
54 }
55
56 if ($form->_searchOptions['groups']) {
57 // multiselect for groups
58 if ($form->_group) {
59 // Arrange groups into hierarchical listing (child groups follow their parents and have indentation spacing in title)
60 $groupHierarchy = CRM_Contact_BAO_Group::getGroupsHierarchy($form->_group, NULL, '&nbsp;&nbsp;', TRUE);
61
62 $form->add('select', 'group', ts('Groups'), $groupHierarchy, FALSE,
63 array('id' => 'group', 'multiple' => 'multiple', 'class' => 'crm-select2')
64 );
65 $groupOptions = CRM_Core_BAO_OptionValue::getOptionValuesAssocArrayFromName('group_type');
66 $form->add('select', 'group_type', ts('Group Types'), $groupOptions, FALSE,
67 array('id' => 'group_type', 'multiple' => 'multiple', 'class' => 'crm-select2')
68 );
69 $form->add('hidden','group_search_selected','group');
70 }
71 }
72
73 if ($form->_searchOptions['tags']) {
74 // multiselect for categories
75 $contactTags = CRM_Core_BAO_Tag::getTags();
76
77 if ($contactTags) {
78 $form->add('select', 'contact_tags', ts('Tags'), $contactTags, FALSE,
79 array('id' => 'contact_tags', 'multiple' => 'multiple', 'class' => 'crm-select2', 'style' => 'width: 100%;')
80 );
81 }
82
83 $parentNames = CRM_Core_BAO_Tag::getTagSet('civicrm_contact');
84 CRM_Core_Form_Tag::buildQuickForm($form, $parentNames, 'civicrm_contact', NULL, TRUE, FALSE);
85
86 $used_for = CRM_Core_OptionGroup::values('tag_used_for');
87 $tagsTypes = array();
88 $showAllTagTypes = false;
89 foreach ($used_for as $key => $value) {
90 //check tags for every type and find if there are any defined
91 $tags = CRM_Core_BAO_Tag::getTagsUsedFor($key, FALSE, TRUE, NULL);
92 // check if there are tags other than contact type, if no - keep checkbox hidden on adv search
93 // we will hide searching contact by attachments tags until it will be implemented in core
94 if (count($tags) && $key != 'civicrm_file' && $key != 'civicrm_contact') {
95 //if tags exists then add type to display in adv search form help text
96 $tagsTypes[] = ts($value);
97 $showAllTagTypes = true;
98 }
99 }
100 $tagTypesText = implode(" or ", $tagsTypes);
101 if ($showAllTagTypes) {
102 $form->add('checkbox', 'all_tag_types', ts('Include tags used for %1', array(1 => $tagTypesText)));
103 $form->add('hidden','tag_types_text', $tagTypesText);
104 }
105 }
106
107 // add text box for last name, first name, street name, city
108 $form->addElement('text', 'sort_name', ts('Find...'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name'));
109
110 // add text box for last name, first name, street name, city
111 $form->add('text', 'email', ts('Contact Email'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name'));
112
113 //added contact source
114 $form->add('text', 'contact_source', ts('Contact Source'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'contact_source'));
115
116 //added job title
117 $form->addElement('text', 'job_title', ts('Job Title'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'job_title'));
118
119
120 //added internal ID
121 $form->addElement('text', 'contact_id', ts('Contact ID'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'id'));
122 $form->addRule('contact_id', ts('Please enter valid Contact ID'), 'positiveInteger');
123
124 //added external ID
125 $form->addElement('text', 'external_identifier', ts('External ID'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'external_identifier'));
126
127 if (CRM_Core_Permission::check('access deleted contacts') and CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'contact_undelete', NULL)) {
128 $form->add('checkbox', 'deleted_contacts', ts('Search in Trash') . '<br />' . ts('(deleted contacts)'));
129 }
130
131 // add checkbox for cms users only
132 $form->addYesNo('uf_user', ts('CMS User?'), TRUE);
133
134 // tag all search
135 $form->add('text', 'tag_search', ts('All Tags'));
136
137 // add search profiles
138
139 // FIXME: This is probably a part of profiles - need to be
140 // FIXME: eradicated from here when profiles are reworked.
141 $types = array('Participant', 'Contribution', 'Membership');
142
143 // get component profiles
144 $componentProfiles = array();
145 $componentProfiles = CRM_Core_BAO_UFGroup::getProfiles($types);
146
147 $ufGroups = CRM_Core_BAO_UFGroup::getModuleUFGroup('Search Profile', 1);
148 $accessibleUfGroups = CRM_Core_Permission::ufGroup(CRM_Core_Permission::VIEW);
149
150 $searchProfiles = array();
151 foreach ($ufGroups as $key => $var) {
152 if (!array_key_exists($key, $componentProfiles) && in_array($key, $accessibleUfGroups)) {
153 $searchProfiles[$key] = $var['title'];
154 }
155 }
156
157 $form->addElement('select',
158 'uf_group_id',
159 ts('Search Views'),
160 array(
161 '0' => ts('- default view -')) + $searchProfiles
162 );
163
164 $componentModes = CRM_Contact_Form_Search::getModeSelect();
165
166 // unset contributions or participants if user does not have
167 // permission on them
168 if (!CRM_Core_Permission::access('CiviContribute')) {
169 unset($componentModes['2']);
170 }
171
172 if (!CRM_Core_Permission::access('CiviEvent')) {
173 unset($componentModes['3']);
174 }
175
176 if (!CRM_Core_Permission::access('CiviMember')) {
177 unset($componentModes['5']);
178 }
179
180 if (!CRM_Core_Permission::check('view all activities')) {
181 unset($componentModes['4']);
182 }
183
184 if (count($componentModes) > 1) {
185 $form->addElement('select',
186 'component_mode',
187 ts('Display Results As'),
188 $componentModes
189 );
190 }
191
192 $form->addElement('select',
193 'operator',
194 ts('Search Operator'),
195 array('AND' => ts('AND'),
196 'OR' => ts('OR'),
197 )
198 );
199
200 // add the option to display relationships
201 $rTypes = CRM_Core_PseudoConstant::relationshipType();
202 $rSelect = array('' => ts('- Select Relationship Type-'));
203 foreach ($rTypes as $rid => $rValue) {
204 if ($rValue['label_a_b'] == $rValue['label_b_a']) {
205 $rSelect[$rid] = $rValue['label_a_b'];
206 }
207 else {
208 $rSelect["{$rid}_a_b"] = $rValue['label_a_b'];
209 $rSelect["{$rid}_b_a"] = $rValue['label_b_a'];
210 }
211 }
212
213 $form->addElement('select',
214 'display_relationship_type',
215 ts('Display Results as Relationship'),
216 $rSelect
217 );
218
219 // checkboxes for DO NOT phone, email, mail
220 // we take labels from SelectValues
221 $t = CRM_Core_SelectValues::privacy();
222 $form->add('select',
223 'privacy_options',
224 ts('Privacy'),
225 $t,
226 FALSE,
227 array(
228 'id' => 'privacy_options',
229 'multiple' => 'multiple',
230 'class' => 'crm-select2',
231 )
232 );
233
234 $form->addElement('select',
235 'privacy_operator',
236 ts('Operator'),
237 array('OR' => ts('OR'),
238 'AND' => ts('AND'),
239 )
240 );
241
242 $options = array(
243 1 => ts('Exclude'),
244 2 => ts('Include by Privacy Option(s)'),
245 );
246 $form->addRadio('privacy_toggle', ts('Privacy Options'), $options, array('allowClear' => FALSE));
247
248 // preferred communication method
249 $comm = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'preferred_communication_method');
250
251 $commPreff = array();
252 foreach ($comm as $k => $v) {
253 $commPreff[] = $form->createElement('advcheckbox', $k, NULL, $v);
254 }
255
256 $onHold[] = $form->createElement('advcheckbox', 'on_hold', NULL, ts(''));
257 $form->addGroup($onHold, 'email_on_hold', ts('Email On Hold'));
258
259 $form->addGroup($commPreff, 'preferred_communication_method', ts('Preferred Communication Method'));
260
261 //CRM-6138 Preferred Language
262 $form->addSelect('preferred_language', array('class' => 'twenty', 'option_url' => NULL));
263
264 // Phone search
265 $form->addElement('text', 'phone_numeric', ts('Phone Number'), CRM_Core_DAO::getAttribute('CRM_Core_DAO_Phone', 'phone'));
266 $locationType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
267 $phoneType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Phone', 'phone_type_id');
268 $form->add('select', 'phone_location_type_id', ts('Phone Location'), array('' => ts('- any -')) + $locationType);
269 $form->add('select', 'phone_phone_type_id', ts('Phone Type'), array('' => ts('- any -')) + $phoneType);
270 }
271
272
273 /**
274 * @param $form
275 */
276 static function location(&$form) {
277 // Build location criteria based on _submitValues if
278 // available; otherwise, use $form->_formValues.
279 $formValues = $form->_submitValues;
280
281 if (empty($formValues) && !empty($form->_formValues)) {
282 $formValues = $form->_formValues;
283 }
284
285 $form->addElement('hidden', 'hidden_location', 1);
286
287 $addressOptions = CRM_Core_BAO_Setting::valueOptions(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME,
288 'address_options', TRUE, NULL, TRUE
289 );
290
291 $attributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_Address');
292
293 $elements = array(
294 'street_address' => array(ts('Street Address'), $attributes['street_address'], NULL, NULL),
295 'city' => array(ts('City'), $attributes['city'], NULL, NULL),
296 'postal_code' => array(ts('Zip / Postal Code'), $attributes['postal_code'], NULL, NULL),
297 'county' => array(ts('County'), $attributes['county_id'], 'county', TRUE),
298 'state_province' => array(ts('State / Province'), $attributes['state_province_id'], 'stateProvince', TRUE),
299 'country' => array(ts('Country'), $attributes['country_id'], 'country', FALSE),
300 'address_name' => array(ts('Address Name'), $attributes['address_name'], NULL, NULL),
301 'street_number' => array(ts('Street Number'), $attributes['street_number'], NULL, NULL),
302 'street_name' => array(ts('Street Name'), $attributes['street_name'], NULL, NULL),
303 'street_unit' => array(ts('Apt/Unit/Suite'), $attributes['street_unit'], NULL, NULL),
304 );
305
306 $parseStreetAddress = CRM_Utils_Array::value('street_address_parsing', $addressOptions, 0);
307 $form->assign('parseStreetAddress', $parseStreetAddress);
308 foreach ($elements as $name => $v) {
309 list($title, $attributes, $select, $multiSelect) = $v;
310
311 if (in_array($name,
312 array('street_number', 'street_name', 'street_unit')
313 )) {
314 if (!$parseStreetAddress) {
315 continue;
316 }
317 }
318 elseif (!$addressOptions[$name]) {
319 continue;
320 }
321
322 if (!$attributes) {
323 $attributes = $attributes[$name];
324 }
325
326 if ($select) {
327 $stateCountryMap[] = array(
328 'state_province' => 'state_province',
329 'country' => 'country',
330 'county' => 'county',
331 );
332 if ($select == 'stateProvince') {
333 if (!empty($formValues['country'])) {
334 $selectElements = array('' => ts('- select -')) + CRM_Core_PseudoConstant::stateProvinceForCountry($formValues['country']);
335 }
336 else {
337 //if not setdefault any country
338 $selectElements = CRM_Core_PseudoConstant::$select();
339 }
340 $element = $form->add('select', $name, $title, $selectElements);
341 }
342 elseif ($select == 'country') {
343 $selectElements = array('' => ts('- any -')) + CRM_Core_PseudoConstant::$select();
344 $element = $form->add('select', $name, $title, $selectElements);
345 }
346 elseif ($select == 'county') {
347 if ( array_key_exists('state_province', $formValues) && !CRM_Utils_System::isNull($formValues['state_province'])) {
348 $selectElements = array('' => ts('- select -')) + CRM_Core_PseudoConstant::countyForState($formValues['state_province']);
349 }
350 else {
351 $selectElements = array('' => ts('- any -'));
352 }
353 $element = $form->add('select', $name, $title, $selectElements);
354 }
355 else {
356 $selectElements = array('' => ts('- any -')) + CRM_Core_PseudoConstant::$select();
357 $element = $form->add('select', $name, $title, $selectElements, FALSE, array('class' => 'crm-select2'));
358 }
359 if ($multiSelect) {
360 $element->setMultiple(TRUE);
361 }
362 }
363 else {
364 $form->addElement('text', $name, $title, $attributes);
365 }
366
367 if ($addressOptions['postal_code']) {
368 $form->addElement('text', 'postal_code_low', ts('Range-From'),
369 CRM_Utils_Array::value('postal_code', $attributes)
370 );
371 $form->addElement('text', 'postal_code_high', ts('To'),
372 CRM_Utils_Array::value('postal_code', $attributes)
373 );
374 }
375 }
376
377 CRM_Core_BAO_Address::addStateCountryMap($stateCountryMap);
378
379 // extend addresses with proximity search
380 $form->addElement('text', 'prox_distance', ts('Find contacts within'), array('class' => 'six'));
381 $form->addElement('select', 'prox_distance_unit', NULL, array('miles' => ts('Miles'), 'kilos' => ts('Kilometers')));
382
383 // is there another form rule that does decimals besides money ? ...
384 $form->addRule('prox_distance', ts('Please enter positive number as a distance'), 'numeric');
385
386 $worldRegions = array('' => '') + CRM_Core_PseudoConstant::worldRegion();
387 $form->addSelect('world_region', array('entity' => 'address', 'placeholder' => ts('- any -'), 'option_url' => NULL));
388
389 // checkboxes for location type
390 $location_type = array();
391 $locationType = CRM_Core_PseudoConstant::get('CRM_Core_DAO_Address', 'location_type_id');
392 foreach ($locationType as $locationTypeID => $locationTypeName) {
393 $location_type[] = $form->createElement('checkbox', $locationTypeID, NULL, $locationTypeName);
394 }
395 $form->addGroup($location_type, 'location_type', ts('Location Types'), '&nbsp;');
396
397 // custom data extending addresses -
398 $extends = array('Address');
399 $groupDetails = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, TRUE, $extends);
400 if ($groupDetails) {
401 $form->assign('addressGroupTree', $groupDetails);
402 foreach ($groupDetails as $group) {
403 foreach ($group['fields'] as $field) {
404 $elementName = 'custom_' . $field['id'];
405 CRM_Core_BAO_CustomField::addQuickFormElement($form,
406 $elementName,
407 $field['id'],
408 FALSE, FALSE, TRUE
409 );
410 }
411 }
412 }
413 }
414
415 /**
416 * @param $form
417 */
418 static function activity(&$form) {
419 $form->add('hidden', 'hidden_activity', 1);
420 CRM_Activity_BAO_Query::buildSearchForm($form);
421 }
422
423 /**
424 * @param $form
425 */
426 static function changeLog(&$form) {
427 $form->add('hidden', 'hidden_changeLog', 1);
428
429 // block for change log
430 $form->addElement('text', 'changed_by', ts('Modified By'), NULL);
431
432 $dates = array(1 => ts('Added'), 2 => ts('Modified'));
433 $form->addRadio('log_date', NULL, $dates, array('allowClear' => TRUE), '<br />');
434
435 CRM_Core_Form_Date::buildDateRange($form, 'log_date', 1, '_low', '_high', ts('From'), FALSE, FALSE);
436 }
437
438 /**
439 * @param $form
440 */
441 static function task(&$form) {
442 $form->add('hidden', 'hidden_task', 1);
443 }
444
445 /**
446 * @param $form
447 */
448 static function relationship(&$form) {
449 $form->add('hidden', 'hidden_relationship', 1);
450
451 $allRelationshipType = array();
452 $allRelationshipType = CRM_Contact_BAO_Relationship::getContactRelationshipType(NULL, NULL, NULL, NULL, TRUE);
453 $form->add('select', 'relation_type_id', ts('Relationship Type'), array('' => ts('- select -')) + $allRelationshipType, FALSE, array('class' => 'crm-select2'));
454 $form->addElement('text', 'relation_target_name', ts('Target Contact'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name'));
455 // relation status
456 $relStatusOption = array(ts('Active '), ts('Inactive '), ts('All'));
457 $form->addRadio('relation_status', ts('Relationship Status'), $relStatusOption);
458 $form->setDefaults(array('relation_status' => 0));
459 // relation permission
460 $relPermissionOption = array(ts('Any'), ts('Yes '), ts('No ') );
461 $form->addRadio('relation_permission', ts('Permissioned Relationship?'), $relPermissionOption);
462 $form->setDefaults(array('relation_permission' => 0));
463
464 //add the target group
465 if ($form->_group) {
466 $form->add('select', 'relation_target_group', ts('Target Contact(s) in Group'), $form->_group, FALSE,
467 array('id' => 'relation_target_group', 'multiple' => 'multiple', 'class' => 'crm-select2')
468 );
469 }
470 CRM_Core_Form_Date::buildDateRange($form, 'relation_start_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE);
471 CRM_Core_Form_Date::buildDateRange($form, 'relation_end_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE);
472
473 // Add reltionship dates
474 CRM_Core_Form_Date::buildDateRange($form, 'relation_date', 1, '_low', '_high', ts('From:'), FALSE, FALSE);
475
476 // add all the custom searchable fields
477 $relationship = array('Relationship');
478 $groupDetails = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, TRUE, $relationship);
479 if ($groupDetails) {
480 $form->assign('relationshipGroupTree', $groupDetails);
481 foreach ($groupDetails as $group) {
482 foreach ($group['fields'] as $field) {
483 $fieldId = $field['id'];
484 $elementName = 'custom_' . $fieldId;
485 CRM_Core_BAO_CustomField::addQuickFormElement($form,
486 $elementName,
487 $fieldId,
488 FALSE, FALSE, TRUE
489 );
490 }
491 }
492 }
493 }
494
495 /**
496 * @param $form
497 */
498 static function demographics(&$form) {
499 $form->add('hidden', 'hidden_demographics', 1);
500 // radio button for gender
501 $genderOptions = array();
502 $gender = CRM_Core_PseudoConstant::get('CRM_Contact_DAO_Contact', 'gender_id');
503 foreach ($gender as $key => $var) {
504 $genderOptions[$key] = $form->createElement('radio', NULL,
505 ts('Gender'), $var, $key,
506 array('id' => "civicrm_gender_{$var}_{$key}")
507 );
508 }
509 $form->addGroup($genderOptions, 'gender_id', ts('Gender'))->setAttribute('allowClear', TRUE);
510
511 CRM_Core_Form_Date::buildDateRange($form, 'birth_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth');
512
513 CRM_Core_Form_Date::buildDateRange($form, 'deceased_date', 1, '_low', '_high', ts('From'), FALSE, FALSE, 'birth');
514
515
516 // radio button for is_deceased
517 $form->addYesNo( 'is_deceased', ts('Deceased'), TRUE);
518 }
519
520 /**
521 * @param $form
522 */
523 static function notes(&$form) {
524 $form->add('hidden', 'hidden_notes', 1);
525
526 $options = array(
527 2 => ts('Body Only'),
528 3 => ts('Subject Only'),
529 6 => ts('Both'),
530 );
531 $form->addRadio('note_option', '', $options);
532
533 $form->addElement('text', 'note', ts('Note Text'), CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name'));
534
535 $form->setDefaults(array('note_option' => 6));
536 }
537
538 /**
539 * Generate the custom Data Fields based
540 * on the is_searchable
541 *
542 * @access private
543 *
544 * @param $form
545 *
546 * @return void
547 */
548 static function custom(&$form) {
549 $form->add('hidden', 'hidden_custom', 1);
550 $extends = array_merge(array('Contact', 'Individual', 'Household', 'Organization'),
551 CRM_Contact_BAO_ContactType::subTypes()
552 );
553 $groupDetails = CRM_Core_BAO_CustomGroup::getGroupDetail(NULL, TRUE,
554 $extends
555 );
556
557 $form->assign('groupTree', $groupDetails);
558
559 foreach ($groupDetails as $key => $group) {
560 $_groupTitle[$key] = $group['name'];
561 CRM_Core_ShowHideBlocks::links($form, $group['name'], '', '');
562
563 $groupId = $group['id'];
564 foreach ($group['fields'] as $field) {
565 $fieldId = $field['id'];
566 $elementName = 'custom_' . $fieldId;
567
568 CRM_Core_BAO_CustomField::addQuickFormElement($form,
569 $elementName,
570 $fieldId,
571 FALSE, FALSE, TRUE
572 );
573 }
574 }
575
576 //TODO: validate for only one state if prox_distance isset
577 }
578
579 /**
580 * @param $form
581 */
582 static function CiviCase(&$form) {
583 //Looks like obsolete code, since CiviCase is a component, but might be used by HRD
584 $form->add('hidden', 'hidden_CiviCase', 1);
585 CRM_Case_BAO_Query::buildSearchForm($form);
586 }
587 }
588