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