Merge pull request #12010 from JMAConsulting/dev-core-70
[civicrm-core.git] / CRM / Contact / Form / Search / Custom / FullText.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
fee14197 4 | CiviCRM version 5 |
6a488035 5 +--------------------------------------------------------------------+
8c9251b3 6 | Copyright CiviCRM LLC (c) 2004-2018 |
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
8c9251b3 31 * @copyright CiviCRM LLC (c) 2004-2018
6a488035 32 */
17da84f5 33class CRM_Contact_Form_Search_Custom_FullText extends CRM_Contact_Form_Search_Custom_Base implements CRM_Contact_Form_Search_Interface {
6a488035
TO
34
35 const LIMIT = 10;
36
4f5de903
TO
37 /**
38 * @var array CRM_Contact_Form_Search_Custom_FullText_AbstractPartialQuery
39 */
40 protected $_partialQueries = NULL;
41
6a488035
TO
42 protected $_formValues;
43
44 protected $_columns;
45
46 protected $_text = NULL;
47
6a488035
TO
48 protected $_table = NULL;
49
50 protected $_tableName = NULL;
51
52 protected $_entityIDTableName = NULL;
53
54 protected $_tableFields = NULL;
55
7296606d
TO
56 /**
57 * @var array|null NULL if no limit; or array(0 => $limit, 1 => $offset)
58 */
6a488035
TO
59 protected $_limitClause = NULL;
60
7296606d
TO
61 /**
62 * @var array|null NULL if no limit; or array(0 => $limit, 1 => $offset)
63 */
6a488035
TO
64 protected $_limitRowClause = NULL;
65
7296606d
TO
66 /**
67 * @var array|null NULL if no limit; or array(0 => $limit, 1 => $offset)
68 */
6a488035
TO
69 protected $_limitDetailClause = NULL;
70
400c14ba 71 protected $_limitNumber = 10;
6a488035
TO
72 protected $_limitNumberPlus1 = 11; // this should be one more than self::LIMIT
73
74 protected $_foundRows = array();
75
86538308 76 /**
5a409b50 77 * Class constructor.
78 *
79 * @param array $formValues
86538308 80 */
00be9182 81 public function __construct(&$formValues) {
4f5de903
TO
82 $this->_partialQueries = array(
83 new CRM_Contact_Form_Search_Custom_FullText_Contact(),
84 new CRM_Contact_Form_Search_Custom_FullText_Activity(),
85 new CRM_Contact_Form_Search_Custom_FullText_Case(),
86 new CRM_Contact_Form_Search_Custom_FullText_Contribution(),
87 new CRM_Contact_Form_Search_Custom_FullText_Participant(),
88 new CRM_Contact_Form_Search_Custom_FullText_Membership(),
89 );
90
ca4269f3
TO
91 $formValues['table'] = $this->getFieldValue($formValues, 'table', 'String');
92 $this->_table = $formValues['table'];
c054cb65 93
ca4269f3 94 $formValues['text'] = trim($this->getFieldValue($formValues, 'text', 'String', ''));
7ad557a0 95 $this->_text = $formValues['text'];
6a488035
TO
96
97 if (!$this->_table) {
7296606d
TO
98 $this->_limitClause = array($this->_limitNumberPlus1, NULL);
99 $this->_limitRowClause = $this->_limitDetailClause = array($this->_limitNumber, NULL);
6a488035
TO
100 }
101 else {
102 // when there is table specified, we would like to use the pager. But since
103 // 1. this custom search has slightly different structure ,
104 // 2. we are in constructor right now,
105 // we 'll use a small hack -
7278c86e
BS
106 $rowCount = CRM_Utils_Array::value('crmRowCount', $_REQUEST, CRM_Utils_Pager::ROWCOUNT);
107 $pageId = CRM_Utils_Array::value('crmPID', $_REQUEST, 1);
6a488035
TO
108 $offset = ($pageId - 1) * $rowCount;
109 $this->_limitClause = NULL;
7296606d
TO
110 $this->_limitRowClause = array($rowCount, NULL);
111 $this->_limitDetailClause = array($rowCount, $offset);
6a488035 112 }
82de5f35 113
114 $this->_formValues = $formValues;
6a488035
TO
115 }
116
ca4269f3
TO
117 /**
118 * Get a value from $formValues. If missing, get it from the request.
119 *
120 * @param $formValues
121 * @param $field
122 * @param $type
123 * @param null $default
124 * @return mixed|null
125 */
126 public function getFieldValue($formValues, $field, $type, $default = NULL) {
127 $value = CRM_Utils_Array::value($field, $formValues);
128 if (!$value) {
129 return CRM_Utils_Request::retrieve($field, $type, CRM_Core_DAO::$_nullObject, FALSE, $default);
130 }
131 return $value;
132 }
133
00be9182 134 public function __destruct() {
6a488035
TO
135 }
136
00be9182 137 public function initialize() {
6a488035
TO
138 static $initialized = FALSE;
139
140 if (!$initialized) {
141 $initialized = TRUE;
142
143 $this->buildTempTable();
144
145 $this->fillTable();
146 }
147 }
148
00be9182 149 public function buildTempTable() {
6a488035
TO
150 $randomNum = md5(uniqid());
151 $this->_tableName = "civicrm_temp_custom_details_{$randomNum}";
152
153 $this->_tableFields = array(
154 'id' => 'int unsigned NOT NULL AUTO_INCREMENT',
155 'table_name' => 'varchar(16)',
156 'contact_id' => 'int unsigned',
157 'sort_name' => 'varchar(128)',
1e12e492 158 'display_name' => 'varchar(128)',
6a488035
TO
159 'assignee_contact_id' => 'int unsigned',
160 'assignee_sort_name' => 'varchar(128)',
161 'target_contact_id' => 'int unsigned',
162 'target_sort_name' => 'varchar(128)',
163 'activity_id' => 'int unsigned',
164 'activity_type_id' => 'int unsigned',
91da6cd5 165 'record_type' => 'varchar(16)',
6a488035
TO
166 'client_id' => 'int unsigned',
167 'case_id' => 'int unsigned',
168 'case_start_date' => 'datetime',
169 'case_end_date' => 'datetime',
170 'case_is_deleted' => 'tinyint',
171 'subject' => 'varchar(255)',
172 'details' => 'varchar(255)',
173 'contribution_id' => 'int unsigned',
400c14ba 174 'financial_type' => 'varchar(255)',
6a488035
TO
175 'contribution_page' => 'varchar(255)',
176 'contribution_receive_date' => 'datetime',
177 'contribution_total_amount' => 'decimal(20,2)',
178 'contribution_trxn_Id' => 'varchar(255)',
179 'contribution_source' => 'varchar(255)',
180 'contribution_status' => 'varchar(255)',
181 'contribution_check_number' => 'varchar(255)',
182 'participant_id' => 'int unsigned',
183 'event_title' => 'varchar(255)',
184 'participant_fee_level' => 'varchar(255)',
185 'participant_fee_amount' => 'int unsigned',
186 'participant_source' => 'varchar(255)',
187 'participant_register_date' => 'datetime',
188 'participant_status' => 'varchar(255)',
189 'participant_role' => 'varchar(255)',
190 'membership_id' => 'int unsigned',
191 'membership_fee' => 'int unsigned',
192 'membership_type' => 'varchar(255)',
193 'membership_start_date' => 'datetime',
194 'membership_end_date' => 'datetime',
195 'membership_source' => 'varchar(255)',
196 'membership_status' => 'varchar(255)',
cac9c01d
TO
197 // We may have multiple files to list on one record.
198 // The temporary-table approach can't store full details for all of them
199 'file_ids' => 'varchar(255)', // comma-separate id listing
6a488035
TO
200 );
201
202 $sql = "
203CREATE TEMPORARY TABLE {$this->_tableName} (
204";
205
206 foreach ($this->_tableFields as $name => $desc) {
207 $sql .= "$name $desc,\n";
208 }
209
210 $sql .= "
211 PRIMARY KEY ( id )
212) ENGINE=HEAP DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci
213";
214 CRM_Core_DAO::executeQuery($sql);
215
216 $this->_entityIDTableName = "civicrm_temp_custom_entityID_{$randomNum}";
217 $sql = "
218CREATE TEMPORARY TABLE {$this->_entityIDTableName} (
219 id int unsigned NOT NULL AUTO_INCREMENT,
220 entity_id int unsigned NOT NULL,
221
222 UNIQUE INDEX unique_entity_id ( entity_id ),
223 PRIMARY KEY ( id )
224) ENGINE=HEAP DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci
225";
226 CRM_Core_DAO::executeQuery($sql);
21ca2cb6 227
228 if (!empty($this->_formValues['is_unit_test'])) {
229 $this->_tableNameForTest = $this->_tableName;
230 }
6a488035
TO
231 }
232
00be9182 233 public function fillTable() {
4f5de903
TO
234 foreach ($this->_partialQueries as $partialQuery) {
235 /** @var $partialQuery CRM_Contact_Form_Search_Custom_FullText_AbstractPartialQuery */
236 if (!$this->_table || $this->_table == $partialQuery->getName()) {
237 if ($partialQuery->isActive()) {
90873dba
TO
238 $result = $partialQuery->fillTempTable($this->_text, $this->_entityIDTableName, $this->_tableName, $this->_limitClause, $this->_limitDetailClause);
239 $this->_foundRows[$partialQuery->getName()] = $result['count'];
4f5de903
TO
240 }
241 }
6a488035
TO
242 }
243
244 $this->filterACLContacts();
245 }
246
00be9182 247 public function filterACLContacts() {
6a488035
TO
248 if (CRM_Core_Permission::check('view all contacts')) {
249 CRM_Core_DAO::executeQuery("DELETE FROM {$this->_tableName} WHERE contact_id IN (SELECT id FROM civicrm_contact WHERE is_deleted = 1)");
250 return;
251 }
252
253 $session = CRM_Core_Session::singleton();
254 $contactID = $session->get('userID');
255 if (!$contactID) {
256 $contactID = 0;
257 }
258
259 CRM_Contact_BAO_Contact_Permission::cache($contactID);
260
261 $params = array(1 => array($contactID, 'Integer'));
262
263 $sql = "
264DELETE t.*
265FROM {$this->_tableName} t
21ca2cb6 266WHERE NOT EXISTS ( SELECT c.contact_id
6a488035
TO
267 FROM civicrm_acl_contact_cache c
268 WHERE c.user_id = %1 AND t.contact_id = c.contact_id )
269";
270 CRM_Core_DAO::executeQuery($sql, $params);
271
272 $sql = "
273DELETE t.*
274FROM {$this->_tableName} t
275WHERE t.table_name = 'Activity' AND
21ca2cb6 276 NOT EXISTS ( SELECT c.contact_id
6a488035
TO
277 FROM civicrm_acl_contact_cache c
278 WHERE c.user_id = %1 AND ( t.target_contact_id = c.contact_id OR t.target_contact_id IS NULL ) )
279";
280 CRM_Core_DAO::executeQuery($sql, $params);
281
282 $sql = "
283DELETE t.*
284FROM {$this->_tableName} t
285WHERE t.table_name = 'Activity' AND
21ca2cb6 286 NOT EXISTS ( SELECT c.contact_id
6a488035
TO
287 FROM civicrm_acl_contact_cache c
288 WHERE c.user_id = %1 AND ( t.assignee_contact_id = c.contact_id OR t.assignee_contact_id IS NULL ) )
289";
290 CRM_Core_DAO::executeQuery($sql, $params);
291 }
292
86538308 293 /**
c054cb65 294 * @param CRM_Core_Form $form
86538308 295 */
00be9182 296 public function buildForm(&$form) {
6a488035
TO
297 $config = CRM_Core_Config::singleton();
298
299 $form->applyFilter('__ALL__', 'trim');
300 $form->add('text',
301 'text',
302 ts('Find'),
303 TRUE
304 );
305
306 // also add a select box to allow the search to be constrained
307 $tables = array('' => ts('All tables'));
4f5de903
TO
308 foreach ($this->_partialQueries as $partialQuery) {
309 /** @var $partialQuery CRM_Contact_Form_Search_Custom_FullText_AbstractPartialQuery */
310 if ($partialQuery->isActive()) {
311 $tables[$partialQuery->getName()] = $partialQuery->getLabel();
312 }
6a488035
TO
313 }
314
481a74f4 315 $form->add('select', 'table', ts('Tables'), $tables);
6a488035
TO
316
317 $form->assign('csID', $form->get('csid'));
318
319 // also add the limit constant
320 $form->assign('limit', self::LIMIT);
321
82de5f35 322 // set form defaults
323 if (!empty($form->_formValues)) {
324 $defaults = array();
325
326 if (isset($form->_formValues['text'])) {
327 $defaults['text'] = $form->_formValues['text'];
328 }
329
330 if (isset($form->_formValues['table'])) {
331 $defaults['table'] = $form->_formValues['table'];
332 $form->assign('table', $form->_formValues['table']);
333 }
334
335 $form->setDefaults($defaults);
336 }
337
6a488035
TO
338 /**
339 * You can define a custom title for the search form
340 */
341 $this->setTitle(ts('Full-text Search'));
cac9c01d
TO
342
343 $searchService = CRM_Core_BAO_File::getSearchService();
344 $form->assign('allowFileSearch', !empty($searchService) && CRM_Core_Permission::check('access uploaded files'));
6a488035
TO
345 }
346
86538308
EM
347 /**
348 * @return array
349 */
00be9182 350 public function &columns() {
6a488035 351 $this->_columns = array(
7b99ead3 352 ts('Contact ID') => 'contact_id',
6a488035
TO
353 ts('Name') => 'sort_name',
354 );
355
356 return $this->_columns;
357 }
358
86538308
EM
359 /**
360 * @return array
361 */
00be9182 362 public function summary() {
6a488035
TO
363 $this->initialize();
364
4f5de903
TO
365 $summary = array();
366 foreach ($this->_partialQueries as $partialQuery) {
367 /** @var $partialQuery CRM_Contact_Form_Search_Custom_FullText_AbstractPartialQuery */
368 $summary[$partialQuery->getName()] = array();
369 }
6a488035 370
6a488035
TO
371 // now iterate through the table and add entries to the relevant section
372 $sql = "SELECT * FROM {$this->_tableName}";
373 if ($this->_table) {
7296606d 374 $sql .= " {$this->toLimit($this->_limitRowClause)} ";
6a488035
TO
375 }
376 $dao = CRM_Core_DAO::executeQuery($sql);
377
378 $activityTypes = CRM_Core_PseudoConstant::activityType(TRUE, TRUE);
379 $roleIds = CRM_Event_PseudoConstant::participantRole();
380 while ($dao->fetch()) {
381 $row = array();
382 foreach ($this->_tableFields as $name => $dontCare) {
383 if ($name != 'activity_type_id') {
384 $row[$name] = $dao->$name;
385 }
386 else {
387 $row['activity_type'] = CRM_Utils_Array::value($dao->$name, $activityTypes);
388 }
389 }
390 if (isset($row['participant_role'])) {
391 $participantRole = explode(CRM_Core_DAO::VALUE_SEPARATOR, $row['participant_role']);
392 $viewRoles = array();
c054cb65 393 foreach ($participantRole as $v) {
6a488035
TO
394 $viewRoles[] = $roleIds[$v];
395 }
396 $row['participant_role'] = implode(', ', $viewRoles);
397 }
cac9c01d
TO
398 if (!empty($row['file_ids'])) {
399 $fileIds = (explode(',', $row['file_ids']));
400 $fileHtml = '';
401 foreach ($fileIds as $fileId) {
402 $paperclip = CRM_Core_BAO_File::paperIconAttachment('*', $fileId);
403 if ($paperclip) {
404 $fileHtml .= implode('', $paperclip);
405 }
406 }
407 $row['fileHtml'] = $fileHtml;
408 }
6a488035
TO
409 $summary[$dao->table_name][] = $row;
410 }
411
412 $summary['Count'] = array();
413 foreach (array_keys($summary) as $table) {
414 $summary['Count'][$table] = CRM_Utils_Array::value($table, $this->_foundRows);
415 if ($summary['Count'][$table] >= self::LIMIT) {
416 $summary['addShowAllLink'][$table] = TRUE;
417 }
418 else {
419 $summary['addShowAllLink'][$table] = FALSE;
420 }
421 }
422
423 return $summary;
424 }
425
86538308
EM
426 /**
427 * @return null|string
428 */
00be9182 429 public function count() {
6a488035
TO
430 $this->initialize();
431
432 if ($this->_table) {
433 return $this->_foundRows[$this->_table];
434 }
435 else {
436 return CRM_Core_DAO::singleValueQuery("SELECT count(id) FROM {$this->_tableName}");
437 }
438 }
439
86538308
EM
440 /**
441 * @param int $offset
442 * @param int $rowcount
443 * @param null $sort
444 * @param bool $returnSQL
445 *
446 * @return null|string
447 */
00be9182 448 public function contactIDs($offset = 0, $rowcount = 0, $sort = NULL, $returnSQL = FALSE) {
6a488035
TO
449 $this->initialize();
450
bbce9d40 451 if ($returnSQL) {
c054cb65 452 return $this->all($offset, $rowcount, $sort, FALSE, TRUE);
bbce9d40
DL
453 }
454 else {
1e12e492 455 return CRM_Core_DAO::singleValueQuery("SELECT contact_id FROM {$this->_tableName}");
bbce9d40 456 }
6a488035
TO
457 }
458
86538308
EM
459 /**
460 * @param int $offset
461 * @param int $rowcount
462 * @param null $sort
463 * @param bool $includeContactIDs
464 * @param bool $justIDs
465 *
466 * @return string
467 */
00be9182 468 public function all($offset = 0, $rowcount = 0, $sort = NULL, $includeContactIDs = FALSE, $justIDs = FALSE) {
6a488035
TO
469 $this->initialize();
470
471 if ($justIDs) {
1e12e492 472 $select = "contact_a.id as contact_id";
6a488035
TO
473 }
474 else {
475 $select = "
476 contact_a.contact_id as contact_id ,
477 contact_a.sort_name as sort_name
478";
479 }
480
481 $sql = "
482SELECT $select
483FROM {$this->_tableName} contact_a
7296606d 484 {$this->toLimit($this->_limitRowClause)}
6a488035
TO
485";
486 return $sql;
487 }
488
86538308
EM
489 /**
490 * @return null
491 */
00be9182 492 public function from() {
6a488035
TO
493 return NULL;
494 }
495
86538308
EM
496 /**
497 * @param bool $includeContactIDs
498 *
499 * @return null
500 */
00be9182 501 public function where($includeContactIDs = FALSE) {
6a488035
TO
502 return NULL;
503 }
504
86538308
EM
505 /**
506 * @return string
507 */
00be9182 508 public function templateFile() {
6a488035
TO
509 return 'CRM/Contact/Form/Search/Custom/FullText.tpl';
510 }
511
86538308
EM
512 /**
513 * @return array
514 */
00be9182 515 public function setDefaultValues() {
6a488035
TO
516 return array();
517 }
518
86538308
EM
519 /**
520 * @param $row
521 */
00be9182 522 public function alterRow(&$row) {
400c14ba 523 }
6a488035 524
86538308
EM
525 /**
526 * @param $title
527 */
00be9182 528 public function setTitle($title) {
6a488035
TO
529 if ($title) {
530 CRM_Utils_System::setTitle($title);
531 }
532 }
7296606d
TO
533
534 /**
535 * @param int|array $limit
a6c01b45
CW
536 * @return string
537 * SQL
7296606d
TO
538 * @see CRM_Contact_Form_Search_Custom_FullText_AbstractPartialQuery::toLimit
539 */
540 public function toLimit($limit) {
541 if (is_array($limit)) {
542 list ($limit, $offset) = $limit;
543 }
544 if (empty($limit)) {
545 return '';
546 }
547 $result = "LIMIT {$limit}";
548 if ($offset) {
549 $result .= " OFFSET {$offset}";
550 }
551 return $result;
552 }
96025800 553
6a488035 554}