Merge remote-tracking branch 'upstream/4.4' into 4.4-master-2014-02-25-17-38-49
[civicrm-core.git] / CRM / Campaign / BAO / Query.php
CommitLineData
6a488035
TO
1<?php
2
3/*
4 +--------------------------------------------------------------------+
232624b1 5 | CiviCRM version 4.4 |
6a488035
TO
6 +--------------------------------------------------------------------+
7 | Copyright CiviCRM LLC (c) 2004-2013 |
8 +--------------------------------------------------------------------+
9 | This file is a part of CiviCRM. |
10 | |
11 | CiviCRM is free software; you can copy, modify, and distribute it |
12 | under the terms of the GNU Affero General Public License |
13 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
14 | |
15 | CiviCRM is distributed in the hope that it will be useful, but |
16 | WITHOUT ANY WARRANTY; without even the implied warranty of |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
18 | See the GNU Affero General Public License for more details. |
19 | |
20 | You should have received a copy of the GNU Affero General Public |
21 | License and the CiviCRM Licensing Exception along |
22 | with this program; if not, contact CiviCRM LLC |
23 | at info[AT]civicrm[DOT]org. If you have questions about the |
24 | GNU Affero General Public License or the licensing of CiviCRM, |
25 | see the CiviCRM license FAQ at http://civicrm.org/licensing |
26 +--------------------------------------------------------------------+
27*/
28
29/**
30 *
31 * @package CRM
32 * @copyright CiviCRM LLC (c) 2004-2013
33 * $Id$
34 *
35 */
36class CRM_Campaign_BAO_Query {
37 //since normal activity clause clause get collides.
38 CONST
39 CIVICRM_ACTIVITY = 'civicrm_survey_activity',
40 CIVICRM_ACTIVITY_TARGET = 'civicrm_survey_activity_target',
41 CIVICRM_ACTIVITY_ASSIGNMENT = 'civicrm_survey_activity_assignment';
42
43 /**
44 * static field for all the campaign fields
45 *
46 * @var array
47 * @static
48 */
49 static $_campaignFields = NULL;
50
51 static $_applySurveyClause = FALSE;
52
53 /**
54 * Function get the fields for campaign.
55 *
56 * @return array self::$_campaignFields an associative array of campaign fields
57 * @static
58 */
59 static function &getFields() {
60 if (!isset(self::$_campaignFields)) {
61 self::$_campaignFields = array();
62 }
63
64 return self::$_campaignFields;
65 }
66
67 /**
68 * if survey, campaign are involved, add the specific fields.
69 *
70 * @return void
71 * @access public
72 */
73 static function select(&$query) {
74 self::$_applySurveyClause = FALSE;
75 if (is_array($query->_params)) {
76 foreach ($query->_params as $values) {
77 if (!is_array($values) || count($values) != 5) {
78 continue;
79 }
80
81 list($name, $op, $value, $grouping, $wildcard) = $values;
82 if ($name == 'campaign_survey_id') {
83 self::$_applySurveyClause = TRUE;
84 break;
85 }
86 }
87 }
82a43d71
CW
88 // CRM-13810 Translate campaign_id to label for search builder
89 if (is_array($query->_select)) {
90 foreach($query->_select as $field => $queryString) {
91 if (substr($field, -11) == 'campaign_id') {
92 $query->_pseudoConstantsSelect[$field] = array(
93 'pseudoField' => 'campaign_id',
94 'idCol' => $field,
95 'bao' => 'CRM_Activity_BAO_Activity',
96 );
97 }
98 }
99 }
100
6a488035
TO
101
102 //get survey clause in force,
103 //only when we have survey id.
104 if (!self::$_applySurveyClause) {
105 return;
106 }
107
108 //all below tables are require to fetch result.
109
110 //1. get survey activity target table in.
e7a308ff 111 $query->_select['survey_activity_target_contact_id'] = 'civicrm_activity_target.contact_id as survey_activity_target_contact_id';
6a488035
TO
112 $query->_select['survey_activity_target_id'] = 'civicrm_activity_target.id as survey_activity_target_id';
113 $query->_element['survey_activity_target_id'] = 1;
114 $query->_element['survey_activity_target_contact_id'] = 1;
115 $query->_tables[self::CIVICRM_ACTIVITY_TARGET] = 1;
116 $query->_whereTables[self::CIVICRM_ACTIVITY_TARGET] = 1;
117
118 //2. get survey activity table in.
119 $query->_select['survey_activity_id'] = 'civicrm_activity.id as survey_activity_id';
120 $query->_element['survey_activity_id'] = 1;
121 $query->_tables[self::CIVICRM_ACTIVITY] = 1;
122 $query->_whereTables[self::CIVICRM_ACTIVITY] = 1;
123
124 //3. get the assignee table in.
125 $query->_select['survey_interviewer_id'] = 'civicrm_activity_assignment.id as survey_interviewer_id';
126 $query->_element['survey_interviewer_id'] = 1;
127 $query->_tables[self::CIVICRM_ACTIVITY_ASSIGNMENT] = 1;
128 $query->_whereTables[self::CIVICRM_ACTIVITY_ASSIGNMENT] = 1;
129
130 //4. get survey table.
131 $query->_select['campaign_survey_id'] = 'civicrm_survey.id as campaign_survey_id';
132 $query->_element['campaign_survey_id'] = 1;
133 $query->_tables['civicrm_survey'] = 1;
134 $query->_whereTables['civicrm_survey'] = 1;
135
136 //5. get campaign table.
137 $query->_select['campaign_id'] = 'civicrm_campaign.id as campaign_id';
138 $query->_element['campaign_id'] = 1;
139 $query->_tables['civicrm_campaign'] = 1;
140 $query->_whereTables['civicrm_campaign'] = 1;
141 }
142
143 static function where(&$query) {
144 //get survey clause in force,
145 //only when we have survey id.
146 if (!self::$_applySurveyClause) {
147 return;
148 }
149
150 $grouping = NULL;
151 foreach (array_keys($query->_params) as $id) {
395d8dc6 152 if ($query->_mode == CRM_Contact_BAO_Query::MODE_CONTACTS) {
6a488035
TO
153 $query->_useDistinct = TRUE;
154 }
155
156 self::whereClauseSingle($query->_params[$id], $query);
157 }
158 }
159
160 static function whereClauseSingle(&$values, &$query) {
161 //get survey clause in force,
162 //only when we have survey id.
163 if (!self::$_applySurveyClause) {
164 return;
165 }
166
167 list($name, $op, $value, $grouping, $wildcard) = $values;
168
169 switch ($name) {
170 case 'campaign_survey_id':
171 $query->_qill[$grouping][] = ts('Survey - %1', array(1 => CRM_Core_DAO::getFieldValue('CRM_Campaign_DAO_Survey', $value, 'title')));
172
173 $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_activity.source_record_id',
e7a308ff 174 $op, $value, 'Integer'
6a488035
TO
175 );
176 $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_survey.id',
e7a308ff 177 $op, $value, 'Integer'
6a488035
TO
178 );
179 return;
180
181 case 'survey_status_id':
182 $activityStatus = CRM_Core_PseudoConstant::activityStatus();
183
184 $query->_qill[$grouping][] = ts('Survey Status - %1', array(1 => $activityStatus[$value]));
185 $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_activity.status_id',
e7a308ff 186 $op, $value, 'Integer'
6a488035
TO
187 );
188 return;
189
190 case 'campaign_search_voter_for':
91da6cd5 191 if (in_array($value, array('release', 'interview'))) {
6a488035
TO
192 $query->_where[$grouping][] = '(civicrm_activity.is_deleted = 0 OR civicrm_activity.is_deleted IS NULL)';
193 }
194 return;
195
196 case 'survey_interviewer_id':
197 $surveyInterviewerName = NULL;
198 foreach ($query->_params as $paramValues) {
199 if (CRM_Utils_Array::value(0, $paramValues) == 'survey_interviewer_name') {
200 $surveyInterviewerName = CRM_Utils_Array::value(2, $paramValues);
201 break;
202 }
203 }
204 $query->_qill[$grouping][] = ts('Survey Interviewer - %1', array(1 => $surveyInterviewerName));
e7a308ff 205 $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause('civicrm_activity_assignment.contact_id',
206 $op, $value, 'Integer'
6a488035
TO
207 );
208 return;
209 }
210 }
211
212 static function from($name, $mode, $side) {
213 $from = NULL;
214 //get survey clause in force,
215 //only when we have survey id.
216 if (!self::$_applySurveyClause) {
217 return $from;
218 }
219
e7e657f0 220 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
9e74e3ce 221 $sourceID = CRM_Utils_Array::key('Activity Source', $activityContacts);
222 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
223 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
8ef12e64 224
6a488035
TO
225 switch ($name) {
226 case self::CIVICRM_ACTIVITY_TARGET:
e7a308ff 227 $from = " INNER JOIN civicrm_activity_contact civicrm_activity_target
9e74e3ce 228 ON ( civicrm_activity_target.contact_id = contact_a.id AND civicrm_activity_target.record_type_id = $targetID) ";
6a488035
TO
229 break;
230
231 case self::CIVICRM_ACTIVITY:
232 $surveyActivityTypes = CRM_Campaign_PseudoConstant::activityType();
233 $surveyKeys = "(" . implode(',', array_keys($surveyActivityTypes)) . ")";
5b5da1ef 234 $from = " INNER JOIN civicrm_activity ON ( civicrm_activity.id = civicrm_activity_target.activity_id
6a488035
TO
235 AND civicrm_activity.activity_type_id IN $surveyKeys ) ";
236 break;
237
238 case self::CIVICRM_ACTIVITY_ASSIGNMENT:
239 $from = "
e7a308ff 240INNER JOIN civicrm_activity_contact civicrm_activity_assignment ON ( civicrm_activity.id = civicrm_activity_assignment.activity_id AND
9e74e3ce 241civicrm_activity_assignment.record_type_id = $assigneeID ) ";
6a488035
TO
242 break;
243
244 case 'civicrm_survey':
245 $from = " INNER JOIN civicrm_survey ON ( civicrm_survey.id = civicrm_activity.source_record_id ) ";
246 break;
247
248 case 'civicrm_campaign':
249 $from = " $side JOIN civicrm_campaign ON ( civicrm_campaign.id = civicrm_survey.campaign_id ) ";
250 break;
251 }
252
253 return $from;
254 }
255
256 static function defaultReturnProperties($mode,
257 $includeCustomFields = TRUE
258 ) {
259 $properties = NULL;
260 if ($mode & CRM_Contact_BAO_Query::MODE_CAMPAIGN) {
261 $properties = array(
262 'contact_id' => 1,
263 'contact_type' => 1,
264 'contact_sub_type' => 1,
265 'sort_name' => 1,
266 'display_name' => 1,
267 'street_unit' => 1,
268 'street_name' => 1,
269 'street_number' => 1,
270 'street_address' => 1,
271 'city' => 1,
272 'postal_code' => 1,
273 'state_province' => 1,
274 'country' => 1,
275 'email' => 1,
276 'phone' => 1,
277 'survey_activity_target_id' => 1,
278 'survey_activity_id' => 1,
279 'survey_status_id' => 1,
280 'campaign_survey_id' => 1,
281 'campaign_id' => 1,
282 'survey_interviewer_id' => 1,
283 'survey_activity_target_contact_id' => 1,
284 );
285 }
286
287 return $properties;
288 }
289
290 static function tableNames(&$tables) {}
291 static function searchAction(&$row, $id) {}
292
293 static function info(&$tables) {
294 //get survey clause in force,
295 //only when we have survey id.
296 if (!self::$_applySurveyClause) {
297 return;
298 }
299
300 $weight = end($tables);
301 $tables[self::CIVICRM_ACTIVITY_TARGET] = ++$weight;
302 $tables[self::CIVICRM_ACTIVITY] = ++$weight;
303 $tables[self::CIVICRM_ACTIVITY_ASSIGNMENT] = ++$weight;
304 $tables['civicrm_survey'] = ++$weight;
305 $tables['civicrm_campaign'] = ++$weight;
306 }
307
308 /**
309 * add all the elements shared between,
310 * normal voter search and voter listing (GOTV form)
311 *
312 * @access public
313 *
314 * @return void
315 * @static
316 */
317 static function buildSearchForm(&$form) {
318
319 $attributes = CRM_Core_DAO::getAttribute('CRM_Core_DAO_Address');
320 $className = CRM_Utils_System::getClassName($form);
321
322 $form->add('text', 'sort_name', ts('Contact Name'),
323 CRM_Core_DAO::getAttribute('CRM_Contact_DAO_Contact', 'sort_name')
324 );
325 $form->add('text', 'street_name', ts('Street Name'), $attributes['street_name']);
326 $form->add('text', 'street_number', ts('Street Number'), $attributes['street_number']);
327 $form->add('text', 'street_unit', ts('Street Unit'), $attributes['street_unit']);
328 $form->add('text', 'street_address', ts('Street Address'), $attributes['street_address']);
329 $form->add('text', 'city', ts('City'), $attributes['city']);
330 $form->add('text', 'postal_code', ts('Zip / Postal Code'), $attributes['postal_code']);
331
332 $contactTypes = CRM_Contact_BAO_ContactType::getSelectElements();
333 $form->add('select', 'contact_type', ts('Contact Type(s)'), $contactTypes, FALSE,
ab345ca5 334 array('id' => 'contact_type', 'multiple' => 'multiple', 'class' => 'crm-select2')
6a488035
TO
335 );
336 $groups = CRM_Core_PseudoConstant::group();
337 $form->add('select', 'group', ts('Groups'), $groups, FALSE,
ab345ca5 338 array('id' => 'group', 'multiple' => 'multiple', 'class' => 'crm-select2')
6a488035
TO
339 );
340
341 $showInterviewer = FALSE;
342 if (CRM_Core_Permission::check('administer CiviCampaign')) {
343 $showInterviewer = TRUE;
344 }
345 $form->assign('showInterviewer', $showInterviewer);
346
347 if ($showInterviewer ||
348 $className == 'CRM_Campaign_Form_Gotv'
349 ) {
350 //autocomplete url
351 $dataUrl = CRM_Utils_System::url('civicrm/ajax/rest',
352 'className=CRM_Contact_Page_AJAX&fnName=getContactList&json=1&reset=1',
353 FALSE, NULL, FALSE
354 );
355
356 $form->assign('dataUrl', $dataUrl);
357 $form->add('text', 'survey_interviewer_name', ts('Interviewer'));
358 $form->add('hidden', 'survey_interviewer_id', '', array('id' => 'survey_interviewer_id'));
359
360 $userId = NULL;
361 if (isset($form->_interviewerId) && $form->_interviewerId) {
362 $userId = $form->_interviewerId;
363 }
364 if (!$userId) {
395d8dc6 365 $session = CRM_Core_Session::singleton();
6a488035
TO
366 $userId = $session->get('userID');
367 }
368 if ($userId) {
369 $defaults = array();
370 $defaults['survey_interviewer_id'] = $userId;
371 $defaults['survey_interviewer_name'] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact',
372 $userId,
373 'sort_name',
374 'id'
375 );
376 $form->setDefaults($defaults);
377 }
378 }
379
380 //build ward and precinct custom fields.
381 $query = '
382 SELECT fld.id, fld.label
383 FROM civicrm_custom_field fld
384INNER JOIN civicrm_custom_group grp on fld.custom_group_id = grp.id
385 WHERE grp.name = %1';
386 $dao = CRM_Core_DAO::executeQuery($query, array(1 => array('Voter_Info', 'String')));
387 $customSearchFields = array();
388 while ($dao->fetch()) {
389 foreach (array(
390 'ward', 'precinct') as $name) {
391 if (stripos($name, $dao->label) !== FALSE) {
392 $fieldId = $dao->id;
393 $fieldName = 'custom_' . $dao->id;
394 $customSearchFields[$name] = $fieldName;
395 CRM_Core_BAO_CustomField::addQuickFormElement($form, $fieldName, $fieldId, FALSE, FALSE);
396 break;
397 }
398 }
399 }
400 $form->assign('customSearchFields', $customSearchFields);
401
402 $surveys = CRM_Campaign_BAO_Survey::getSurveys();
403
404 if (empty($surveys) &&
405 ($className == 'CRM_Campaign_Form_Search')
406 ) {
407 CRM_Core_Error::statusBounce(ts('Could not find survey for %1 respondents.',
408 array(1 => $form->get('op'))
409 ),
410 CRM_Utils_System::url('civicrm/survey/add',
411 'reset=1&action=add'
412 )
413 );
414 }
415
416 //CRM-7406 --
417 //If survey had associated campaign and
418 //campaign has some contact groups, don't
419 //allow to search the contacts those are not
420 //in given campaign groups ( ie not in constituents )
421 $groupJs = NULL;
422 if ($form->get('searchVoterFor') == 'reserve') {
423 $groupJs = array('onChange' => "buildCampaignGroups( );return false;");
424 }
425 $form->add('select', 'campaign_survey_id', ts('Survey'), $surveys, TRUE, $groupJs);
426 }
427
428 /*
429 * Retrieve all valid voter ids,
430 * and build respective clause to restrict search.
431 *
432 * @param array $criteria an array
433 * @return $voterClause as a string
434 * @static
435 */
a019ffee 436 static public function voterClause($params) {
6a488035
TO
437 $voterClause = array();
438 $fromClause = $whereClause = NULL;
439 if (!is_array($params) || empty($params)) {
440 return $voterClause;
441 }
442 $surveyId = CRM_Utils_Array::value('campaign_survey_id', $params);
443 $interviewerId = CRM_Utils_Array::value('survey_interviewer_id', $params);
444 $searchVoterFor = CRM_Utils_Array::value('campaign_search_voter_for', $params);
445
446 //get the survey activities.
447 $activityStatus = CRM_Core_PseudoConstant::activityStatus('name');
448 $status = array('Scheduled');
449 if ($searchVoterFor == 'reserve') {
450 $status[] = 'Completed';
451 }
452
453 $completedStatusId = NULL;
454 foreach ($status as $name) {
455 if ($statusId = array_search($name, $activityStatus)) {
456 $statusIds[] = $statusId;
457 if ($name == 'Completed') {
458 $completedStatusId = $statusId;
459 }
460 }
461 }
462
463 $voterActValues = CRM_Campaign_BAO_Survey::getSurveyVoterInfo($surveyId, NULL, $statusIds);
464
465 if (!empty($voterActValues)) {
466 $operator = 'IN';
467 $voterIds = array_keys($voterActValues);
468 if ($searchVoterFor == 'reserve') {
469 $operator = 'NOT IN';
470 //filter out recontact survey contacts.
471 $recontactInterval = CRM_Core_DAO::getFieldValue('CRM_Campaign_DAO_Survey',
472 $surveyId, 'recontact_interval'
473 );
474 $recontactInterval = unserialize($recontactInterval);
475 if ($surveyId &&
476 is_array($recontactInterval) &&
477 !empty($recontactInterval)
478 ) {
479 $voterIds = array();
480 foreach ($voterActValues as $values) {
481 $numOfDays = CRM_Utils_Array::value($values['result'], $recontactInterval);
482 if ($numOfDays &&
483 $values['status_id'] == $completedStatusId
484 ) {
485 $recontactIntSeconds = $numOfDays * 24 * 3600;
486 $actDateTimeSeconds = CRM_Utils_Date::unixTime($values['activity_date_time']);
487 $totalSeconds = $recontactIntSeconds + $actDateTimeSeconds;
488 //don't consider completed survey activity
489 //unless it fulfill recontact interval criteria.
490 if ($totalSeconds <= time()) {
491 continue;
492 }
493 }
494 $voterIds[$values['voter_id']] = $values['voter_id'];
495 }
496 }
497 }
498
499 //lets dump these ids in tmp table and
500 //use appropriate join depend on operator.
501 if (!empty($voterIds)) {
502 $voterIdCount = count($voterIds);
503
504 //create temporary table to store voter ids.
505 $tempTableName = CRM_Core_DAO::createTempTableName('civicrm_survey_respondent');
506 CRM_Core_DAO::executeQuery("DROP TABLE IF EXISTS {$tempTableName}");
507
508 $query = "
509 CREATE TEMPORARY TABLE {$tempTableName} (
510 id int unsigned NOT NULL AUTO_INCREMENT,
511 survey_contact_id int unsigned NOT NULL,
512 PRIMARY KEY ( id )
513);
514";
515 CRM_Core_DAO::executeQuery($query);
516
517 $batch = 100;
518 $insertedCount = 0;
519 do {
520 $processIds = $voterIds;
521 $insertIds = array_splice($processIds, $insertedCount, $batch);
522 if (!empty($insertIds)) {
523 $insertSQL = "INSERT IGNORE INTO {$tempTableName}( survey_contact_id )
524 VALUES (" . implode('),(', $insertIds) . ');';
525 CRM_Core_DAO::executeQuery($insertSQL);
526 }
527 $insertedCount += $batch;
528 } while ($insertedCount < $voterIdCount);
529
530 if ($operator == 'IN') {
531 $fromClause = " INNER JOIN {$tempTableName} ON ( {$tempTableName}.survey_contact_id = contact_a.id )";
532 }
533 else {
534 $fromClause = " LEFT JOIN {$tempTableName} ON ( {$tempTableName}.survey_contact_id = contact_a.id )";
535 $whereClause = "( {$tempTableName}.survey_contact_id IS NULL )";
536 }
537 }
538 }
539 $voterClause = array(
540 'fromClause' => $fromClause,
541 'whereClause' => $whereClause,
542 );
543
544 return $voterClause;
545 }
546
547 /**
548 * Build the campaign clause for component serach.
549 *
550 **/
551 public static function componentSearchClause(&$params, &$query) {
552 $op = CRM_Utils_Array::value('op', $params, '=');
553 $campaign = CRM_Utils_Array::value('campaign', $params);
554 $tableName = CRM_Utils_Array::value('tableName', $params);
555 $grouping = CRM_Utils_Array::value('grouping', $params);
556 if (CRM_Utils_System::isNull($campaign) || empty($tableName)) {
557 return;
558 }
559
560 // fixme - what is the purpose of this code? $campaign should be
561 // an integer, not an array
562 if (is_array($campaign)) {
563 foreach (array(
564 'current_campaign', 'past_campaign') as $ignore) {
565 $index = array_search($ignore, $campaign);
566 if ($index !== FALSE)unset($campaign[$index]);
567 }
568 }
569
570 $allCampaigns = CRM_Campaign_BAO_Campaign::getCampaigns(NULL, NULL, FALSE, FALSE, FALSE, TRUE);
571
572 $campaignIds = $campaignTitles = array();
573 if (is_array($campaign)) {
574 foreach ($campaign as $campId) {
575 $campaignIds[$campId] = $campId;
576 $campaignTitles[$campId] = $allCampaigns[$campId];
577 }
578 if (count($campaignIds) > 1) {
579 $op = 'IN';
580 $campaignIds = '(' . implode(',', $campaignIds) . ')';
581 }
582 else {
583 $campaignIds = reset($campaignIds);
584 }
585 }
586 else {
587 $campaignIds = $campaign;
588 if (array_key_exists($campaignIds, $allCampaigns)) {
589 $campaignTitles[$campaignIds] = $allCampaigns[$campaignIds];
590 }
591 }
592 $query->_qill[$grouping][] = ts('Campaigns %1',
593 array(1 => $op)
594 ) . ' ' . implode(' ' . ts('or') . ' ', $campaignTitles);
595 $query->_where[$grouping][] = CRM_Contact_BAO_Query::buildClause("{$tableName}.campaign_id",
596 $op,
597 $campaignIds,
598 'Integer'
599 );
600 $query->_tables[$tableName] = $query->_whereTables[$tableName] = 1;
601 }
602}
603