Merge pull request #4935 from atif-shaikh/CRM-15804
[civicrm-core.git] / CRM / Utils / Migrate / ExportJSON.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
06b69b18 6 | Copyright CiviCRM LLC (c) 2004-2014 |
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 +--------------------------------------------------------------------+
26*/
27
28/**
29 *
30 * @package CRM
06b69b18 31 * @copyright CiviCRM LLC (c) 2004-2014
6a488035
TO
32 * $Id$
33 *
34 */
35class CRM_Utils_Migrate_ExportJSON {
7da04cde 36 const CHUNK_SIZE = 128;
6a488035
TO
37
38 protected $_contactIDs;
39
40 protected $_allContactIDs;
41
42 protected $_values;
43
44 protected $_discoverContacts = FALSE;
45
46 protected $_renameGroups = 1;
47
48 protected $_renameTags = 1;
49
3302658e
TO
50 protected $_sitePrefix = 'Site 1';
51
5bc392e6 52 /**
c490a46a 53 * @param array $params
5bc392e6 54 */
00be9182 55 public function __construct(&$params) {
6a488035
TO
56 foreach ($params as $name => $value) {
57 $varName = '_' . $name;
58 $this->$varName = $value;
59 }
60 }
61
62 /**
63 * Split a large array of contactIDs into more manageable smaller chunks
64 */
00be9182 65 public function &splitContactIDs(&$contactIDs) {
6a488035
TO
66 // contactIDs could be a real large array, so we split it up into
67 // smaller chunks and then general xml for each chunk
3302658e
TO
68 $chunks = array();
69 $current = 0;
6a488035 70 $chunks[$current] = array();
3302658e 71 $count = 0;
6a488035
TO
72
73 foreach ($contactIDs as $k => $v) {
74 $chunks[$current][$k] = $v;
75 $count++;
76
77 if ($count == self::CHUNK_SIZE) {
78 $current++;
79 $chunks[$current] = array();
80 $count = 0;
81 }
82 }
83
84 if (empty($chunks[$current])) {
85 unset($chunks[$current]);
86 }
87
88 return $chunks;
89 }
90
91 /**
92 * Given a set of contact IDs get the values
93 */
00be9182 94 public function getValues(&$contactIDs, &$additionalContactIDs) {
6a488035
TO
95
96 $this->contact($contactIDs);
97 $this->address($contactIDs);
98 $this->phone($contactIDs);
99 $this->email($contactIDs);
100 $this->im($contactIDs);
101 $this->website($contactIDs);
102 $this->note($contactIDs);
103
104 $this->group($contactIDs);
105 $this->groupContact($contactIDs);
106 $this->savedSearch($contactIDs);
107
108 $this->tag($contactIDs);
109 $this->entityTag($contactIDs);
110
111 $this->relationship($contactIDs, $additionalContactIDs);
112 $this->activity($contactIDs, $additionalContactIDs);
113 }
114
00be9182 115 public function metaData() {
6a488035
TO
116 $optionGroupVars = array(
117 'prefix_id' => 'individual_prefix',
118 'suffix_id' => 'individual_suffix',
119 'gender_id' => 'gender',
120 'mobile_provider' => 'mobile_provider',
121 'phone_type' => 'phone_type',
122 'activity_type' => 'activity_type',
123 'status_id' => 'activity_status_id',
124 'priority_id' => 'activity_priority_id',
125 'medium_id' => 'encounter_medium',
aa62b355 126 'communication_style_id' => 'communication_style',
6a488035
TO
127 'email_greeting' => 'email_greeting',
128 'postal_greeting' => 'postal_greeting',
129 'addressee_id' => 'addressee',
130 );
131 $this->optionGroup($optionGroupVars);
132
133 $auxilaryTables = array(
134 'civicrm_location_type' => 'CRM_Core_DAO_LocationType',
135 'civicrm_relationship_type' => 'CRM_Contact_DAO_RelationshipType',
136 );
137 $this->auxTable($auxilaryTables);
138 }
139
5bc392e6
EM
140 /**
141 * @param $tables
142 */
00be9182 143 public function auxTable($tables) {
6a488035 144 foreach ($tables as $tableName => $daoName) {
353ffa53 145 $fields = &$this->dbFields($daoName, TRUE);
6a488035
TO
146
147 $sql = "SELECT * from $tableName";
148 $this->sql($sql, $tableName, $fields);
149 }
150 }
151
5bc392e6
EM
152 /**
153 * @param $optionGroupVars
154 */
00be9182 155 public function optionGroup($optionGroupVars) {
6a488035
TO
156 $names = array_values($optionGroupVars);
157 $str = array();
158 foreach ($names as $name) {
159 $str[] = "'$name'";
160 }
161 $nameString = implode(",", $str);
162
163 $sql = "
164SELECT *
165FROM civicrm_option_group
166WHERE name IN ( $nameString )
167";
353ffa53 168 $fields = &$this->dbFields('CRM_Core_DAO_OptionGroup', TRUE);
6a488035
TO
169 $this->sql($sql, 'civicrm_option_group', $fields);
170
171 $sql = "
172SELECT v.*
173FROM civicrm_option_value v
174INNER JOIN civicrm_option_group g ON v.option_group_id = g.id
175WHERE g.name IN ( $nameString )
176";
353ffa53 177 $fields = &$this->dbFields('CRM_Core_DAO_OptionValue', TRUE);
6a488035
TO
178 $this->sql($sql, 'civicrm_option_value', $fields);
179 }
180
5bc392e6
EM
181 /**
182 * @param $ids
100fef9d 183 * @param string $tableName
5bc392e6
EM
184 * @param $fields
185 * @param $whereField
186 * @param null $additionalWhereCond
187 */
7c550ca0 188 public function table(
a3e55d9c
TO
189 &$ids,
190 $tableName,
191 &$fields,
192 $whereField,
193 $additionalWhereCond = NULL
6a488035
TO
194 ) {
195 if (empty($ids)) {
196 return;
197 }
198
199 $idString = implode(',', $ids);
200
201 $sql = "
202SELECT *
203 FROM $tableName
204 WHERE $whereField IN ( $idString )
205";
206
207 if ($additionalWhereCond) {
208 $sql .= " AND $additionalWhereCond";
209 }
210
211 $this->sql($sql, $tableName, $fields);
212 }
213
5bc392e6
EM
214 /**
215 * @param $sql
100fef9d 216 * @param string $tableName
5bc392e6
EM
217 * @param $fields
218 */
00be9182 219 public function sql($sql, $tableName, &$fields) {
353ffa53 220 $dao = &CRM_Core_DAO::executeQuery($sql);
6a488035
TO
221
222 while ($dao->fetch()) {
223 $value = array();
224 foreach ($fields as $name) {
225 if (empty($dao->$name)) {
226 $value[$name] = NULL;
227 }
228 else {
229 $value[$name] = $dao->$name;
230 }
231 }
232 $this->appendValue($dao->id, $tableName, $value);
233 }
234 $dao->free();
235 }
236
5bc392e6
EM
237 /**
238 * @param $contactIDs
239 */
00be9182 240 public function contact(&$contactIDs) {
353ffa53 241 $fields = &$this->dbFields('CRM_Contact_DAO_Contact', TRUE);
6a488035
TO
242 $this->table($contactIDs, 'civicrm_contact', $fields, 'id', NULL);
243 }
244
5bc392e6
EM
245 /**
246 * @param $contactIDs
247 */
00be9182 248 public function note(&$contactIDs) {
353ffa53 249 $fields = &$this->dbFields('CRM_Core_DAO_Note', TRUE);
6a488035
TO
250 $this->table($contactIDs, 'civicrm_note', $fields, 'entity_id', "entity_table = 'civicrm_contact'");
251 }
252
5bc392e6
EM
253 /**
254 * @param $contactIDs
255 */
00be9182 256 public function phone(&$contactIDs) {
353ffa53 257 $fields = &$this->dbFields('CRM_Core_DAO_Phone', TRUE);
6a488035
TO
258 $this->table($contactIDs, 'civicrm_phone', $fields, 'contact_id', NULL);
259 }
260
5bc392e6
EM
261 /**
262 * @param $contactIDs
263 */
00be9182 264 public function email(&$contactIDs) {
353ffa53 265 $fields = &$this->dbFields('CRM_Core_DAO_Email', TRUE);
6a488035
TO
266 $this->table($contactIDs, 'civicrm_email', $fields, 'contact_id', NULL);
267 }
268
5bc392e6
EM
269 /**
270 * @param $contactIDs
271 */
00be9182 272 public function im(&$contactIDs) {
353ffa53 273 $fields = &$this->dbFields('CRM_Core_DAO_IM', TRUE);
6a488035
TO
274 $this->table($contactIDs, 'civicrm_im', $fields, 'contact_id', NULL);
275 }
276
5bc392e6
EM
277 /**
278 * @param $contactIDs
279 */
00be9182 280 public function website(&$contactIDs) {
353ffa53 281 $fields = &$this->dbFields('CRM_Core_DAO_Website', TRUE);
6a488035
TO
282 $this->table($contactIDs, 'civicrm_website', $fields, 'contact_id', NULL);
283 }
284
5bc392e6
EM
285 /**
286 * @param $contactIDs
287 */
00be9182 288 public function address(&$contactIDs) {
353ffa53 289 $fields = &$this->dbFields('CRM_Core_DAO_Email', TRUE);
6a488035
TO
290 $this->table($contactIDs, 'civicrm_address', $fields, 'contact_id', NULL);
291 }
292
5bc392e6
EM
293 /**
294 * @param $contactIDs
295 */
00be9182 296 public function groupContact(&$contactIDs) {
353ffa53 297 $fields = &$this->dbFields('CRM_Contact_DAO_GroupContact', TRUE);
6a488035
TO
298 $this->table($contactIDs, 'civicrm_group_contact', $fields, 'contact_id', NULL);
299 }
300
5bc392e6 301 /**
4f1f1f2a
CW
302 * TODO - support group inheritance
303 * Parent child group ids are encoded in a text string
5bc392e6
EM
304 * @param $contactIDs
305 */
00be9182 306 public function group(&$contactIDs) {
6a488035
TO
307 // handle groups only once
308 static $_groupsHandled = array();
309
310 $ids = implode(',', $contactIDs);
311
312 $sql = "
313SELECT DISTINCT group_id
314FROM civicrm_group_contact
315WHERE contact_id IN ( $ids )
316";
317 $dao = CRM_Core_DAO::executeQuery($sql);
318 $groupIDs = array();
319 while ($dao->fetch()) {
320 if (!isset($_groupsHandled[$dao->group_id])) {
321 $groupIDs[] = $dao->group_id;
322 $_groupsHandled[$dao->group_id] = 1;
323 }
324 }
325
353ffa53 326 $fields = &$this->dbFields('CRM_Contact_DAO_Group', TRUE);
6a488035
TO
327 $this->table($groupIDs, 'civicrm_group', $fields, 'id');
328
329 $this->savedSearch($groupIDs);
330 }
331
5bc392e6 332 /**
4f1f1f2a 333 * TODO - support search builder and custom saved searches
5bc392e6
EM
334 * @param $groupIDs
335 */
00be9182 336 public function savedSearch(&$groupIDs) {
6a488035
TO
337 if (empty($groupIDs)) {
338 return;
339 }
340
341 $idString = implode(",", $groupIDs);
342 $sql = "
343SELECT s.*
344FROM civicrm_saved_search s
345INNER JOIN civicrm_group g on g.saved_search_id = s.id
346WHERE g.id IN ( $idString )
347";
348
353ffa53 349 $fields = &$this->dbFields('CRM_Contact_DAO_SavedSearch', TRUE);
6a488035
TO
350 $this->sql($sql, 'civicrm_saved_search', $fields);
351 }
352
5bc392e6
EM
353 /**
354 * @param $contactIDs
355 */
00be9182 356 public function entityTag(&$contactIDs) {
353ffa53 357 $fields = &$this->dbFields('CRM_Core_DAO_EntityTag', TRUE);
6a488035
TO
358 $this->table($contactIDs, 'civicrm_entity_tag', $fields, 'entity_id', "entity_table = 'civicrm_contact'");
359 }
360
5bc392e6
EM
361 /**
362 * @param $contactIDs
363 */
00be9182 364 public function tag(&$contactIDs) {
6a488035
TO
365 // handle tags only once
366 static $_tagsHandled = array();
367
368 $ids = implode(',', $contactIDs);
369
370 $sql = "
371SELECT DISTINCT tag_id
372FROM civicrm_entity_tag
373WHERE entity_id IN ( $ids )
374AND entity_table = 'civicrm_contact'
375";
376 $dao = CRM_Core_DAO::executeQuery($sql);
377 $tagIDs = array();
378 while ($dao->fetch()) {
379 if (!isset($_tagsHandled[$dao->tag_id])) {
380 $tagIDs[] = $dao->tag_id;
381 $_tagsHandled[$dao->tag_id] = 1;
382 }
383 }
384
353ffa53 385 $fields = &$this->dbFields('CRM_Core_DAO_Tag', TRUE);
6a488035
TO
386 $this->table($tagIDs, 'civicrm_tag', $fields, 'id');
387 }
388
5bc392e6
EM
389 /**
390 * @param $contactIDs
391 * @param $additionalContacts
392 */
00be9182 393 public function relationship(&$contactIDs, &$additionalContacts) {
6a488035
TO
394 // handle relationships only once
395 static $_relationshipsHandled = array();
396
397 $ids = implode(',', $contactIDs);
398
399 $sql = "(
400 SELECT r.*
401 FROM civicrm_relationship r
402 WHERE r.contact_id_a IN ( $ids )
403) UNION (
404 SELECT r.*
405 FROM civicrm_relationship r
406 WHERE r.contact_id_b IN ( $ids )
407)
408";
409
410 $fields = $this->dbFields('CRM_Contact_DAO_Relationship', TRUE);
353ffa53 411 $dao = &CRM_Core_DAO::executeQuery($sql);
6a488035
TO
412 while ($dao->fetch()) {
413 if (isset($_relationshipsHandled[$dao->id])) {
414 continue;
415 }
416 $_relationshipsHandled[$dao->id] = $dao->id;
417
418 $relationship = array();
419 foreach ($fields as $fld) {
420 if (empty($dao->$fld)) {
421 $relationship[$fld] = NULL;
422 }
423 else {
424 $relationship[$fld] = $dao->$fld;
425 }
426 }
427 $this->appendValue($dao->id, 'civicrm_relationship', $relationship);
428
429 $this->addAdditionalContacts(array(
3302658e 430 $dao->contact_id_a,
6a488035
TO
431 $dao->contact_id_b,
432 ),
433 $additionalContacts
434 );
435 }
436 $dao->free();
437 }
438
5bc392e6
EM
439 /**
440 * @param $contactIDs
441 * @param $additionalContacts
442 */
00be9182 443 public function activity(&$contactIDs, &$additionalContacts) {
6a488035 444 static $_activitiesHandled = array();
e7e657f0 445 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
9e74e3ce 446 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
447 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
6a488035
TO
448 $ids = implode(',', $contactIDs);
449
42d58fac
PJ
450 // query framing returning all contacts in valid activity
451 $sql = "
452SELECT a.*, ac.id as acID, ac.activity_id, ac.contact_id, ac.record_type_id
453FROM civicrm_activity a
454INNER JOIN civicrm_activity_contact ac ON ac.activity_id = a.id
455WHERE ac.contact_id IN ( $ids )
456 AND (a.activity_type_id != 3 AND a.activity_type_id != 20)
6a488035
TO
457";
458
353ffa53 459 $fields = &$this->dbFields('CRM_Activity_DAO_Activity', TRUE);
6a488035 460
353ffa53 461 $dao = &CRM_Core_DAO::executeQuery($sql);
6a488035 462 while ($dao->fetch()) {
42d58fac
PJ
463 // adding source, target and assignee contacts in additional contacts array
464 $this->addAdditionalContacts(array($dao->contact_id),
465 $additionalContacts
466 );
467
468 // append values of activity contacts
469 $activityContacts = array(
470 'id' => $dao->acID,
471 'contact_id' => $dao->contact_id,
472 'activity_id' => $dao->activity_id,
21dfd5f5 473 'record_type_id' => $dao->record_type_id,
42d58fac
PJ
474 );
475 $this->appendValue($dao->acID, 'civicrm_activity_contact', $activityContacts);
476
6a488035
TO
477 if (isset($_activitiesHandled[$dao->id])) {
478 continue;
479 }
480 $_activitiesHandled[$dao->id] = $dao->id;
6a488035
TO
481
482 $activity = array();
483 foreach ($fields as $fld) {
484 if (empty($dao->$fld)) {
485 $activity[$fld] = NULL;
486 }
487 else {
488 $activity[$fld] = $dao->$fld;
489 }
490 }
491
42d58fac 492 // append activity value
6a488035 493 $this->appendValue($dao->id, 'civicrm_activity', $activity);
6a488035
TO
494 }
495 $dao->free();
6a488035
TO
496 }
497
5bc392e6 498 /**
100fef9d
CW
499 * @param int $id
500 * @param string $name
5bc392e6
EM
501 * @param $value
502 */
00be9182 503 public function appendValue($id, $name, $value) {
6a488035
TO
504 if (empty($value)) {
505 return;
506 }
507
508 if (!isset($this->_values[$name])) {
509 $this->_values[$name] = array();
510 $this->_values[$name][] = array_keys($value);
511 }
512 $this->_values[$name][] = array_values($value);
513 }
514
5bc392e6 515 /**
c490a46a 516 * @param string $daoName
5bc392e6
EM
517 * @param bool $onlyKeys
518 *
519 * @return array
520 */
00be9182 521 public function dbFields($daoName, $onlyKeys = FALSE) {
6a488035
TO
522 static $_fieldsRetrieved = array();
523
524 if (!isset($_fieldsRetrieved[$daoName])) {
525 $_fieldsRetrieved[$daoName] = array();
526 $daoFile = str_replace('_',
353ffa53
TO
527 DIRECTORY_SEPARATOR,
528 $daoName
529 ) . '.php';
e7292422 530 include_once $daoFile;
6a488035 531
353ffa53 532 $daoFields = &$daoName::fields();
6a488035
TO
533
534 foreach ($daoFields as $key => & $value) {
535 $_fieldsRetrieved[$daoName][$value['name']] = array(
536 'uniqueName' => $key,
537 'type' => $value['type'],
538 'title' => CRM_Utils_Array::value('title', $value, NULL),
539 );
540 }
541 }
542
543 if ($onlyKeys) {
544 return array_keys($_fieldsRetrieved[$daoName]);
545 }
546 else {
547 return $_fieldsRetrieved[$daoName];
548 }
549 }
550
5bc392e6
EM
551 /**
552 * @param $contactIDs
553 * @param $additionalContacts
554 */
00be9182 555 public function addAdditionalContacts($contactIDs, &$additionalContacts) {
6a488035
TO
556 if (!$this->_discoverContacts) {
557 return;
558 }
559
560 foreach ($contactIDs as $cid) {
561 if ($cid &&
562 !isset($this->_allContactIDs[$cid]) &&
563 !isset($additionalContacts[$cid])
564 ) {
565 $additionalContacts[$cid] = $cid;
566 }
567 }
568 }
569
5bc392e6
EM
570 /**
571 * @param $contactIDs
572 */
00be9182 573 public function export(&$contactIDs) {
353ffa53 574 $chunks = &$this->splitContactIDs($contactIDs);
6a488035
TO
575
576 $additionalContactIDs = array();
577
578 foreach ($chunks as $chunk) {
579 $this->getValues($chunk, $additionalContactIDs);
580 }
581
582 if (!empty($additionalContactIDs)) {
583 $this->_allContactIDs = $this->_allContactIDs + $additionalContactIDs;
584 $this->export($additionalContactIDs);
585 }
586 }
587
5bc392e6 588 /**
100fef9d 589 * @param string $fileName
5bc392e6
EM
590 * @param null $lastExportTime
591 * @param bool $discoverContacts
592 */
7c550ca0 593 public function run(
a3e55d9c
TO
594 $fileName,
595 $lastExportTime = NULL,
596 $discoverContacts = FALSE
6a488035
TO
597 ) {
598 $this->_discoverContacts = $discoverContacts;
599
600 if (!$lastExportTime) {
601 $sql = "
602SELECT id
603FROM civicrm_contact
604";
605 }
606 else {
607 $sql = "(
608SELECT DISTINCT entity_id
609FROM civicrm_log
610WHERE entity_table = 'civicrm_contact'
611AND modified_date >= $lastExportTime
612) UNION (
613SELECT DISTINCT contact_id
614FROM civicrm_subscription_history
615WHERE date >= $lastExportTime
616)
617";
618 }
619
a3e55d9c 620 $dao = &CRM_Core_DAO::executeQuery($sql);
6a488035
TO
621
622 $contactIDs = array();
623 while ($dao->fetch()) {
624 $contactIDs[$dao->id] = $dao->id;
625 }
626
627 $this->_allContactIDs = $contactIDs;
628 $this->_values = array();
629
630 $this->metaData();
631
632 $this->export($contactIDs);
633
634 $json = json_encode($this->_values, JSON_NUMERIC_CHECK);
635 file_put_contents($fileName,
636 $json
637 );
638
639 // print_r( json_decode( $json ) );
640 }
641}