Revert "CRM-14920 - labels from custom search: honor sort order."
[civicrm-core.git] / CRM / Contact / Form / Task.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
e7112fa7 6 | Copyright CiviCRM LLC (c) 2004-2015 |
6a488035
TO
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
e7112fa7 31 * @copyright CiviCRM LLC (c) 2004-2015
6a488035
TO
32 */
33
34/**
5a409b50 35 * This class generates form components for search-result tasks.
6a488035
TO
36 */
37class CRM_Contact_Form_Task extends CRM_Core_Form {
38
39 /**
100fef9d 40 * The task being performed
6a488035
TO
41 *
42 * @var int
43 */
44 protected $_task;
45
46 /**
47 * The array that holds all the contact ids
48 *
49 * @var array
50 */
51 public $_contactIds;
52
53 /**
54 * The array that holds all the contact types
55 *
56 * @var array
57 */
58 public $_contactTypes;
59
60 /**
61 * The additional clause that we restrict the search with
62 *
63 * @var string
64 */
65 protected $_componentClause = NULL;
66
67 /**
68 * The name of the temp table where we store the contact IDs
69 *
70 * @var string
71 */
72 protected $_componentTable = NULL;
73
74 /**
75 * The array that holds all the component ids
76 *
77 * @var array
78 */
79 protected $_componentIds;
80
81 /**
82 * This includes the submitted values of the search form
83 */
84 static protected $_searchFormValues;
85
86 /**
d8689418 87 * Build all the data structures needed to build the form.
8ef12e64 88 */
00be9182 89 public function preProcess() {
6a488035
TO
90 self::preProcessCommon($this);
91 }
92
86538308 93 /**
d8689418
EM
94 * Common pre-processing function.
95 *
c490a46a 96 * @param CRM_Core_Form $form
86538308
EM
97 * @param bool $useTable
98 */
00be9182 99 public static function preProcessCommon(&$form, $useTable = FALSE) {
6a488035
TO
100
101 $form->_contactIds = array();
102 $form->_contactTypes = array();
103
104 // get the submitted values of the search form
105 // we'll need to get fv from either search or adv search in the future
106 $fragment = 'search';
107 if ($form->_action == CRM_Core_Action::ADVANCED) {
108 self::$_searchFormValues = $form->controller->exportValues('Advanced');
109 $fragment .= '/advanced';
110 }
111 elseif ($form->_action == CRM_Core_Action::PROFILE) {
112 self::$_searchFormValues = $form->controller->exportValues('Builder');
113 $fragment .= '/builder';
114 }
115 elseif ($form->_action == CRM_Core_Action::COPY) {
116 self::$_searchFormValues = $form->controller->exportValues('Custom');
117 $fragment .= '/custom';
118 }
119 else {
120 self::$_searchFormValues = $form->controller->exportValues('Basic');
121 }
122
123 //set the user context for redirection of task actions
124 $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $form);
125 $urlParams = 'force=1';
126 if (CRM_Utils_Rule::qfKey($qfKey)) {
127 $urlParams .= "&qfKey=$qfKey";
128 }
129
130 $cacheKey = "civicrm search {$qfKey}";
131
132 $url = CRM_Utils_System::url('civicrm/contact/' . $fragment, $urlParams);
133 $session = CRM_Core_Session::singleton();
134 $session->replaceUserContext($url);
135
136 $form->_task = CRM_Utils_Array::value('task', self::$_searchFormValues);
137 $crmContactTaskTasks = CRM_Contact_Task::taskTitles();
138 $form->assign('taskName', CRM_Utils_Array::value($form->_task, $crmContactTaskTasks));
139
140 if ($useTable) {
141 $form->_componentTable = CRM_Core_DAO::createTempTableName('civicrm_task_action', TRUE, $qfKey);
142 $sql = " DROP TABLE IF EXISTS {$form->_componentTable}";
143 CRM_Core_DAO::executeQuery($sql);
144
145 $sql = "CREATE TABLE {$form->_componentTable} ( contact_id int primary key) ENGINE=MyISAM DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci";
146 CRM_Core_DAO::executeQuery($sql);
147 }
148
149 // all contacts or action = save a search
150 if ((CRM_Utils_Array::value('radio_ts', self::$_searchFormValues) == 'ts_all') ||
151 ($form->_task == CRM_Contact_Task::SAVE_SEARCH)
152 ) {
153 $sortByCharacter = $form->get('sortByCharacter');
5d272ea2 154 $cacheKey = ($sortByCharacter && $sortByCharacter != 'all') ? "{$cacheKey}_alphabet" : $cacheKey;
8ef12e64 155
66ceb5d9 156 // since we don't store all contacts in prevnextcache, when user selects "all" use query to retrieve contacts
8e1a7c71 157 // rather than prevnext cache table for most of the task actions except export where we rebuild query to fetch
158 // final result set
159 if ($useTable) {
160 $allCids = CRM_Core_BAO_PrevNextCache::getSelection($cacheKey, "getall");
161 }
162 else {
163 $allCids[$cacheKey] = $form->getContactIds();
164 }
6a488035
TO
165
166 $form->_contactIds = array();
167 if ($useTable) {
168 $count = 0;
169 $insertString = array();
170 foreach ($allCids[$cacheKey] as $cid => $ignore) {
171 $count++;
172 $insertString[] = " ( {$cid} ) ";
173 if ($count % 200 == 0) {
174 $string = implode(',', $insertString);
175 $sql = "REPLACE INTO {$form->_componentTable} ( contact_id ) VALUES $string";
176 CRM_Core_DAO::executeQuery($sql);
177 $insertString = array();
178 }
179 }
180 if (!empty($insertString)) {
181 $string = implode(',', $insertString);
182 $sql = "REPLACE INTO {$form->_componentTable} ( contact_id ) VALUES $string";
183 CRM_Core_DAO::executeQuery($sql);
184 }
185 }
186 else {
187 // filter duplicates here
188 // CRM-7058
189 // might be better to do this in the query, but that logic is a bit complex
190 // and it decides when to use distinct based on input criteria, which needs
191 // to be fixed and optimized.
192
4c78f49e
JV
193 foreach ($allCids[$cacheKey] as $cid => $ignore) {
194 $form->_contactIds[] = $cid;
195 }
6a488035
TO
196 }
197 }
198 elseif (CRM_Utils_Array::value('radio_ts', self::$_searchFormValues) == 'ts_sel') {
199 // selected contacts only
200 // need to perform action on only selected contacts
201 $insertString = array();
202
b44e3f84 203 // refire sql in case of custom search
6a488035
TO
204 if ($form->_action == CRM_Core_Action::COPY) {
205 // selected contacts only
206 // need to perform action on only selected contacts
207 foreach (self::$_searchFormValues as $name => $value) {
208 if (substr($name, 0, CRM_Core_Form::CB_PREFIX_LEN) == CRM_Core_Form::CB_PREFIX) {
209 $contactID = substr($name, CRM_Core_Form::CB_PREFIX_LEN);
210 if ($useTable) {
211 $insertString[] = " ( {$contactID} ) ";
212 }
213 else {
214 $form->_contactIds[] = substr($name, CRM_Core_Form::CB_PREFIX_LEN);
215 }
216 }
217 }
218 }
219 else {
5d272ea2 220 // fetching selected contact ids of passed cache key
221 $selectedCids = CRM_Core_BAO_PrevNextCache::getSelection($cacheKey);
222 foreach ($selectedCids[$cacheKey] as $selectedCid => $ignore) {
223 if ($useTable) {
224 $insertString[] = " ( {$selectedCid} ) ";
225 }
226 else {
227 $form->_contactIds[] = $selectedCid;
228 }
6a488035
TO
229 }
230 }
8ef12e64 231
6a488035
TO
232 if (!empty($insertString)) {
233 $string = implode(',', $insertString);
234 $sql = "REPLACE INTO {$form->_componentTable} ( contact_id ) VALUES $string";
235 CRM_Core_DAO::executeQuery($sql);
236 }
237 }
238
239 //contact type for pick up profiles as per selected contact types with subtypes
240 //CRM-5521
241 if ($selectedTypes = CRM_Utils_Array::value('contact_type', self::$_searchFormValues)) {
242 if (!is_array($selectedTypes)) {
243 $selectedTypes = explode(' ', $selectedTypes);
244 }
245 foreach ($selectedTypes as $ct => $dontcare) {
246 if (strpos($ct, CRM_Core_DAO::VALUE_SEPARATOR) === FALSE) {
247 $form->_contactTypes[] = $ct;
248 }
249 else {
250 $separator = strpos($ct, CRM_Core_DAO::VALUE_SEPARATOR);
251 $form->_contactTypes[] = substr($ct, $separator + 1);
252 }
253 }
254 }
255
6a488035 256 if (CRM_Utils_Array::value('radio_ts', self::$_searchFormValues) == 'ts_sel'
5d272ea2 257 && ($form->_action != CRM_Core_Action::COPY)
258 ) {
6a488035 259 $sel = CRM_Utils_Array::value('radio_ts', self::$_searchFormValues);
5d272ea2 260 $form->assign('searchtype', $sel);
6a488035
TO
261 $result = CRM_Core_BAO_PrevNextCache::getSelectedContacts();
262 $form->assign("value", $result);
263 }
8ef12e64 264
6a488035
TO
265 if (!empty($form->_contactIds)) {
266 $form->_componentClause = ' contact_a.id IN ( ' . implode(',', $form->_contactIds) . ' ) ';
267 $form->assign('totalSelectedContacts', count($form->_contactIds));
268
269 $form->_componentIds = $form->_contactIds;
270 }
271 }
272
273 /**
d8689418
EM
274 * Get the contact id for custom search.
275 *
276 * we are not using prev/next table in case of custom search
6a488035
TO
277 */
278 public function getContactIds() {
279 // need to perform action on all contacts
280 // fire the query again and get the contact id's + display name
281 $sortID = NULL;
282 if ($this->get(CRM_Utils_Sort::SORT_ID)) {
283 $sortID = CRM_Utils_Sort::sortIDValue($this->get(CRM_Utils_Sort::SORT_ID),
284 $this->get(CRM_Utils_Sort::SORT_DIRECTION)
285 );
286 }
287
288 $selectorName = $this->controller->selectorName();
d3e86119 289 require_once str_replace('_', DIRECTORY_SEPARATOR, $selectorName) . '.php';
6a488035
TO
290
291 $fv = $this->get('formValues');
292 $customClass = $this->get('customSearchClass');
293 require_once 'CRM/Core/BAO/Mapping.php';
294 $returnProperties = CRM_Core_BAO_Mapping::returnProperties(self::$_searchFormValues);
295
5d272ea2 296 $selector = new $selectorName($customClass, $fv, NULL, $returnProperties);
6a488035
TO
297
298 $params = $this->get('queryParams');
299
300 // fix for CRM-5165
301 $sortByCharacter = $this->get('sortByCharacter');
a7cddb8c 302 if ($sortByCharacter && $sortByCharacter != 1) {
6a488035
TO
303 $params[] = array('sortByCharacter', '=', $sortByCharacter, 0, 0);
304 }
305 $queryOperator = $this->get('queryOperator');
306 if (!$queryOperator) {
307 $queryOperator = 'AND';
308 }
66ceb5d9 309 $dao = $selector->contactIDQuery($params, $this->_action, $sortID,
5d272ea2 310 CRM_Utils_Array::value('display_relationship_type', $fv),
6a488035
TO
311 $queryOperator
312 );
313
314 $contactIds = array();
5d272ea2 315 while ($dao->fetch()) {
4c78f49e 316 $contactIds[$dao->contact_id] = $dao->contact_id;
6a488035
TO
317 }
318
319 return $contactIds;
320 }
321
322
323 /**
ee0ce2ef 324 * Set default values for the form. Relationship that in edit/view action.
6a488035 325 *
ee0ce2ef 326 * The default values are retrieved from the database.
6a488035 327 *
e2046b33 328 * @return array
6a488035 329 */
00be9182 330 public function setDefaultValues() {
6a488035
TO
331 $defaults = array();
332 return $defaults;
333 }
334
335 /**
57507ae6 336 * Add the rules for form.
6a488035 337 */
00be9182 338 public function addRules() {
5d272ea2 339 }
6a488035
TO
340
341 /**
5c9ff055 342 * Build the form object.
6a488035
TO
343 */
344 public function buildQuickForm() {
345 $this->addDefaultButtons(ts('Confirm Action'));
346 }
347
348 /**
ee0ce2ef 349 * Process the form after the input has been submitted and validated.
6a488035 350 */
5d272ea2 351 public function postProcess() {
352 }
353
6a488035 354 /**
57507ae6
EM
355 * Simple shell that derived classes can call to add form buttons.
356 *
357 * Allows customized title for the main Submit
6a488035 358 *
77c5b619
TO
359 * @param string $title
360 * Title of the main button.
361 * @param string $nextType
362 * Button type for the form after processing.
77b97be7
EM
363 * @param string $backType
364 * @param bool $submitOnce
6a488035 365 */
00be9182 366 public function addDefaultButtons($title, $nextType = 'next', $backType = 'back', $submitOnce = FALSE) {
6a488035
TO
367 $this->addButtons(array(
368 array(
369 'type' => $nextType,
370 'name' => $title,
371 'isDefault' => TRUE,
372 ),
373 array(
374 'type' => $backType,
375 'name' => ts('Cancel'),
0291a521 376 'icon' => 'fa-times',
6a488035
TO
377 ),
378 )
379 );
380 }
57884c26
DJ
381
382 /**
100fef9d 383 * Replace ids of household members in $this->_contactIds with the id of their household.
57884c26 384 *
5c9ff055 385 * CRM-8338
57884c26
DJ
386 */
387 public function mergeContactIdsByHousehold() {
388 if (empty($this->_contactIds)) {
389 return;
390 }
391
392 $contactRelationshipTypes = CRM_Contact_BAO_Relationship::getContactRelationshipType(
393 NULL,
394 NULL,
395 NULL,
396 NULL,
397 TRUE,
398 'name',
399 FALSE
400 );
401
402 // Get Head of Household & Household Member relationships
403 $relationKeyMOH = CRM_Utils_Array::key('Household Member of', $contactRelationshipTypes);
404 $relationKeyHOH = CRM_Utils_Array::key('Head of Household for', $contactRelationshipTypes);
405 $householdRelationshipTypes = array(
406 $relationKeyMOH => $contactRelationshipTypes[$relationKeyMOH],
407 $relationKeyHOH => $contactRelationshipTypes[$relationKeyHOH],
408 );
409
410 $relID = implode(',', $this->_contactIds);
411
412 foreach ($householdRelationshipTypes as $rel => $dnt) {
413 list($id, $direction) = explode('_', $rel, 2);
414 // identify the relationship direction
415 $contactA = 'contact_id_a';
416 $contactB = 'contact_id_b';
417 if ($direction == 'b_a') {
418 $contactA = 'contact_id_b';
419 $contactB = 'contact_id_a';
420 }
421
422 // Find related households.
353ffa53 423 $relationSelect = "SELECT contact_household.id as household_id, {$contactA} as refContact ";
57884c26
DJ
424 $relationFrom = " FROM civicrm_contact contact_household
425 INNER JOIN civicrm_relationship crel ON crel.{$contactB} = contact_household.id AND crel.relationship_type_id = {$id} ";
426
427 // Check for active relationship status only.
353ffa53
TO
428 $today = date('Ymd');
429 $relationActive = " AND (crel.is_active = 1 AND ( crel.end_date is NULL OR crel.end_date >= {$today} ) )";
430 $relationWhere = " WHERE contact_household.is_deleted = 0 AND crel.{$contactA} IN ( {$relID} ) {$relationActive}";
431 $relationGroupBy = " GROUP BY crel.{$contactA}";
57884c26
DJ
432 $relationQueryString = "$relationSelect $relationFrom $relationWhere $relationGroupBy";
433
434 $householdsDAO = CRM_Core_DAO::executeQuery($relationQueryString);
435 while ($householdsDAO->fetch()) {
436 // Remove contact's id from $this->_contactIds and replace with their household's id.
437 foreach (array_keys($this->_contactIds, $householdsDAO->refContact) as $idKey) {
438 unset($this->_contactIds[$idKey]);
439 }
440 if (!in_array($householdsDAO->household_id, $this->_contactIds)) {
441 $this->_contactIds[] = $householdsDAO->household_id;
442 }
443 }
444 $householdsDAO->free();
445 }
446 }
96025800 447
36c74cd6
TO
448 /**
449 * Given this task's list of targets, produce a hidden group.
450 *
451 * @return array
452 * Array(0 => int $groupID, 1 => int|NULL $ssID).
453 * @throws Exception
454 */
455 public function createHiddenGroup() {
456 // Did the user select "All" matches or cherry-pick a few records?
457 $searchParams = $this->controller->exportValues();
458 if ($searchParams['radio_ts'] == 'ts_sel') {
459 // Create a static group.
460
461 $randID = md5(time() . rand(1, 1000)); // groups require a unique name
462 $grpTitle = "Hidden Group {$randID}";
463 $grpID = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Group', $grpTitle, 'id', 'title');
464
465 if (!$grpID) {
466 $groupParams = array(
467 'title' => $grpTitle,
468 'is_active' => 1,
469 'is_hidden' => 1,
470 'group_type' => array('2' => 1),
471 );
472
473 $group = CRM_Contact_BAO_Group::create($groupParams);
474 $grpID = $group->id;
475
476 CRM_Contact_BAO_GroupContact::addContactsToGroup($this->_contactIds, $group->id);
477
478 $newGroupTitle = "Hidden Group {$grpID}";
479 $groupParams = array(
480 'id' => $grpID,
481 'name' => CRM_Utils_String::titleToVar($newGroupTitle),
482 'title' => $newGroupTitle,
483 'group_type' => array('2' => 1),
484 );
485 $group = CRM_Contact_BAO_Group::create($groupParams);
486 }
487
488 // note at this point its a static group
489 return array($grpID, NULL);
490 }
491 else {
492 // Create a smart group.
493
494 $ssId = $this->get('ssID');
495 $hiddenSmartParams = array(
496 'group_type' => array('2' => 1),
497 'form_values' => $this->get('formValues'),
498 'saved_search_id' => $ssId,
499 'search_custom_id' => $this->get('customSearchID'),
500 'search_context' => $this->get('context'),
501 );
502
503 list($smartGroupId, $savedSearchId) = CRM_Contact_BAO_Group::createHiddenSmartGroup($hiddenSmartParams);
504 return array($smartGroupId, $savedSearchId);
505 }
506
507 }
508
6a488035 509}