Merge pull request #4808 from colemanw/CRM-15759
[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 {
36 CONST CHUNK_SIZE = 128;
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 */
3302658e 55 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 */
65 function &splitContactIDs(&$contactIDs) {
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 */
94 function getValues(&$contactIDs, &$additionalContactIDs) {
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
115 function metaData() {
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 */
6a488035
TO
143 function auxTable($tables) {
144 foreach ($tables as $tableName => $daoName) {
3302658e 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 */
6a488035
TO
155 function optionGroup($optionGroupVars) {
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";
3302658e 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";
3302658e 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 */
6a488035 188 function table(&$ids,
3302658e
TO
189 $tableName,
190 &$fields,
191 $whereField,
192 $additionalWhereCond = NULL
6a488035
TO
193 ) {
194 if (empty($ids)) {
195 return;
196 }
197
198 $idString = implode(',', $ids);
199
200 $sql = "
201SELECT *
202 FROM $tableName
203 WHERE $whereField IN ( $idString )
204";
205
206 if ($additionalWhereCond) {
207 $sql .= " AND $additionalWhereCond";
208 }
209
210 $this->sql($sql, $tableName, $fields);
211 }
212
5bc392e6
EM
213 /**
214 * @param $sql
100fef9d 215 * @param string $tableName
5bc392e6
EM
216 * @param $fields
217 */
6a488035 218 function sql($sql, $tableName, &$fields) {
3302658e 219 $dao = & CRM_Core_DAO::executeQuery($sql);
6a488035
TO
220
221 while ($dao->fetch()) {
222 $value = array();
223 foreach ($fields as $name) {
224 if (empty($dao->$name)) {
225 $value[$name] = NULL;
226 }
227 else {
228 $value[$name] = $dao->$name;
229 }
230 }
231 $this->appendValue($dao->id, $tableName, $value);
232 }
233 $dao->free();
234 }
235
5bc392e6
EM
236 /**
237 * @param $contactIDs
238 */
6a488035 239 function contact(&$contactIDs) {
3302658e 240 $fields = & $this->dbFields('CRM_Contact_DAO_Contact', TRUE);
6a488035
TO
241 $this->table($contactIDs, 'civicrm_contact', $fields, 'id', NULL);
242 }
243
5bc392e6
EM
244 /**
245 * @param $contactIDs
246 */
6a488035 247 function note(&$contactIDs) {
3302658e 248 $fields = & $this->dbFields('CRM_Core_DAO_Note', TRUE);
6a488035
TO
249 $this->table($contactIDs, 'civicrm_note', $fields, 'entity_id', "entity_table = 'civicrm_contact'");
250 }
251
5bc392e6
EM
252 /**
253 * @param $contactIDs
254 */
6a488035 255 function phone(&$contactIDs) {
3302658e 256 $fields = & $this->dbFields('CRM_Core_DAO_Phone', TRUE);
6a488035
TO
257 $this->table($contactIDs, 'civicrm_phone', $fields, 'contact_id', NULL);
258 }
259
5bc392e6
EM
260 /**
261 * @param $contactIDs
262 */
6a488035 263 function email(&$contactIDs) {
3302658e 264 $fields = & $this->dbFields('CRM_Core_DAO_Email', TRUE);
6a488035
TO
265 $this->table($contactIDs, 'civicrm_email', $fields, 'contact_id', NULL);
266 }
267
5bc392e6
EM
268 /**
269 * @param $contactIDs
270 */
6a488035 271 function im(&$contactIDs) {
3302658e 272 $fields = & $this->dbFields('CRM_Core_DAO_IM', TRUE);
6a488035
TO
273 $this->table($contactIDs, 'civicrm_im', $fields, 'contact_id', NULL);
274 }
275
5bc392e6
EM
276 /**
277 * @param $contactIDs
278 */
6a488035 279 function website(&$contactIDs) {
3302658e 280 $fields = & $this->dbFields('CRM_Core_DAO_Website', TRUE);
6a488035
TO
281 $this->table($contactIDs, 'civicrm_website', $fields, 'contact_id', NULL);
282 }
283
5bc392e6
EM
284 /**
285 * @param $contactIDs
286 */
6a488035 287 function address(&$contactIDs) {
3302658e 288 $fields = & $this->dbFields('CRM_Core_DAO_Email', TRUE);
6a488035
TO
289 $this->table($contactIDs, 'civicrm_address', $fields, 'contact_id', NULL);
290 }
291
5bc392e6
EM
292 /**
293 * @param $contactIDs
294 */
6a488035 295 function groupContact(&$contactIDs) {
3302658e 296 $fields = & $this->dbFields('CRM_Contact_DAO_GroupContact', TRUE);
6a488035
TO
297 $this->table($contactIDs, 'civicrm_group_contact', $fields, 'contact_id', NULL);
298 }
299
300 // TODO - support group inheritance
301 // Parent child group ids are encoded in a text string
5bc392e6
EM
302 /**
303 * @param $contactIDs
304 */
6a488035
TO
305 function group(&$contactIDs) {
306 // handle groups only once
307 static $_groupsHandled = array();
308
309 $ids = implode(',', $contactIDs);
310
311 $sql = "
312SELECT DISTINCT group_id
313FROM civicrm_group_contact
314WHERE contact_id IN ( $ids )
315";
316 $dao = CRM_Core_DAO::executeQuery($sql);
317 $groupIDs = array();
318 while ($dao->fetch()) {
319 if (!isset($_groupsHandled[$dao->group_id])) {
320 $groupIDs[] = $dao->group_id;
321 $_groupsHandled[$dao->group_id] = 1;
322 }
323 }
324
3302658e 325 $fields = & $this->dbFields('CRM_Contact_DAO_Group', TRUE);
6a488035
TO
326 $this->table($groupIDs, 'civicrm_group', $fields, 'id');
327
328 $this->savedSearch($groupIDs);
329 }
330
331 // TODO - support search builder and custom saved searches
5bc392e6
EM
332 /**
333 * @param $groupIDs
334 */
6a488035
TO
335 function savedSearch(&$groupIDs) {
336 if (empty($groupIDs)) {
337 return;
338 }
339
340 $idString = implode(",", $groupIDs);
341 $sql = "
342SELECT s.*
343FROM civicrm_saved_search s
344INNER JOIN civicrm_group g on g.saved_search_id = s.id
345WHERE g.id IN ( $idString )
346";
347
3302658e 348 $fields = & $this->dbFields('CRM_Contact_DAO_SavedSearch', TRUE);
6a488035
TO
349 $this->sql($sql, 'civicrm_saved_search', $fields);
350 }
351
5bc392e6
EM
352 /**
353 * @param $contactIDs
354 */
6a488035 355 function entityTag(&$contactIDs) {
3302658e 356 $fields = & $this->dbFields('CRM_Core_DAO_EntityTag', TRUE);
6a488035
TO
357 $this->table($contactIDs, 'civicrm_entity_tag', $fields, 'entity_id', "entity_table = 'civicrm_contact'");
358 }
359
5bc392e6
EM
360 /**
361 * @param $contactIDs
362 */
6a488035
TO
363 function tag(&$contactIDs) {
364 // handle tags only once
365 static $_tagsHandled = array();
366
367 $ids = implode(',', $contactIDs);
368
369 $sql = "
370SELECT DISTINCT tag_id
371FROM civicrm_entity_tag
372WHERE entity_id IN ( $ids )
373AND entity_table = 'civicrm_contact'
374";
375 $dao = CRM_Core_DAO::executeQuery($sql);
376 $tagIDs = array();
377 while ($dao->fetch()) {
378 if (!isset($_tagsHandled[$dao->tag_id])) {
379 $tagIDs[] = $dao->tag_id;
380 $_tagsHandled[$dao->tag_id] = 1;
381 }
382 }
383
3302658e 384 $fields = & $this->dbFields('CRM_Core_DAO_Tag', TRUE);
6a488035
TO
385 $this->table($tagIDs, 'civicrm_tag', $fields, 'id');
386 }
387
5bc392e6
EM
388 /**
389 * @param $contactIDs
390 * @param $additionalContacts
391 */
6a488035
TO
392 function relationship(&$contactIDs, &$additionalContacts) {
393 // handle relationships only once
394 static $_relationshipsHandled = array();
395
396 $ids = implode(',', $contactIDs);
397
398 $sql = "(
399 SELECT r.*
400 FROM civicrm_relationship r
401 WHERE r.contact_id_a IN ( $ids )
402) UNION (
403 SELECT r.*
404 FROM civicrm_relationship r
405 WHERE r.contact_id_b IN ( $ids )
406)
407";
408
409 $fields = $this->dbFields('CRM_Contact_DAO_Relationship', TRUE);
3302658e 410 $dao = & CRM_Core_DAO::executeQuery($sql);
6a488035
TO
411 while ($dao->fetch()) {
412 if (isset($_relationshipsHandled[$dao->id])) {
413 continue;
414 }
415 $_relationshipsHandled[$dao->id] = $dao->id;
416
417 $relationship = array();
418 foreach ($fields as $fld) {
419 if (empty($dao->$fld)) {
420 $relationship[$fld] = NULL;
421 }
422 else {
423 $relationship[$fld] = $dao->$fld;
424 }
425 }
426 $this->appendValue($dao->id, 'civicrm_relationship', $relationship);
427
428 $this->addAdditionalContacts(array(
3302658e 429 $dao->contact_id_a,
6a488035
TO
430 $dao->contact_id_b,
431 ),
432 $additionalContacts
433 );
434 }
435 $dao->free();
436 }
437
5bc392e6
EM
438 /**
439 * @param $contactIDs
440 * @param $additionalContacts
441 */
6a488035
TO
442 function activity(&$contactIDs, &$additionalContacts) {
443 static $_activitiesHandled = array();
e7e657f0 444 $activityContacts = CRM_Core_OptionGroup::values('activity_contacts', FALSE, FALSE, FALSE, NULL, 'name');
9e74e3ce 445 $assigneeID = CRM_Utils_Array::key('Activity Assignees', $activityContacts);
446 $targetID = CRM_Utils_Array::key('Activity Targets', $activityContacts);
6a488035
TO
447 $ids = implode(',', $contactIDs);
448
42d58fac
PJ
449 // query framing returning all contacts in valid activity
450 $sql = "
451SELECT a.*, ac.id as acID, ac.activity_id, ac.contact_id, ac.record_type_id
452FROM civicrm_activity a
453INNER JOIN civicrm_activity_contact ac ON ac.activity_id = a.id
454WHERE ac.contact_id IN ( $ids )
455 AND (a.activity_type_id != 3 AND a.activity_type_id != 20)
6a488035
TO
456";
457
3302658e 458 $fields = & $this->dbFields('CRM_Activity_DAO_Activity', TRUE);
6a488035 459
3302658e 460 $dao = & CRM_Core_DAO::executeQuery($sql);
6a488035 461 while ($dao->fetch()) {
42d58fac
PJ
462 // adding source, target and assignee contacts in additional contacts array
463 $this->addAdditionalContacts(array($dao->contact_id),
464 $additionalContacts
465 );
466
467 // append values of activity contacts
468 $activityContacts = array(
469 'id' => $dao->acID,
470 'contact_id' => $dao->contact_id,
471 'activity_id' => $dao->activity_id,
472 'record_type_id' => $dao->record_type_id
473 );
474 $this->appendValue($dao->acID, 'civicrm_activity_contact', $activityContacts);
475
6a488035
TO
476 if (isset($_activitiesHandled[$dao->id])) {
477 continue;
478 }
479 $_activitiesHandled[$dao->id] = $dao->id;
6a488035
TO
480
481 $activity = array();
482 foreach ($fields as $fld) {
483 if (empty($dao->$fld)) {
484 $activity[$fld] = NULL;
485 }
486 else {
487 $activity[$fld] = $dao->$fld;
488 }
489 }
490
42d58fac 491 // append activity value
6a488035 492 $this->appendValue($dao->id, 'civicrm_activity', $activity);
6a488035
TO
493 }
494 $dao->free();
6a488035
TO
495 }
496
5bc392e6 497 /**
100fef9d
CW
498 * @param int $id
499 * @param string $name
5bc392e6
EM
500 * @param $value
501 */
6a488035
TO
502 function appendValue($id, $name, $value) {
503 if (empty($value)) {
504 return;
505 }
506
507 if (!isset($this->_values[$name])) {
508 $this->_values[$name] = array();
509 $this->_values[$name][] = array_keys($value);
510 }
511 $this->_values[$name][] = array_values($value);
512 }
513
5bc392e6 514 /**
c490a46a 515 * @param string $daoName
5bc392e6
EM
516 * @param bool $onlyKeys
517 *
518 * @return array
519 */
6a488035
TO
520 function dbFields($daoName, $onlyKeys = FALSE) {
521 static $_fieldsRetrieved = array();
522
523 if (!isset($_fieldsRetrieved[$daoName])) {
524 $_fieldsRetrieved[$daoName] = array();
525 $daoFile = str_replace('_',
526 DIRECTORY_SEPARATOR,
527 $daoName
528 ) . '.php';
529 include_once ($daoFile);
530
3302658e 531 $daoFields = & $daoName::fields();
6a488035
TO
532
533 foreach ($daoFields as $key => & $value) {
534 $_fieldsRetrieved[$daoName][$value['name']] = array(
535 'uniqueName' => $key,
536 'type' => $value['type'],
537 'title' => CRM_Utils_Array::value('title', $value, NULL),
538 );
539 }
540 }
541
542 if ($onlyKeys) {
543 return array_keys($_fieldsRetrieved[$daoName]);
544 }
545 else {
546 return $_fieldsRetrieved[$daoName];
547 }
548 }
549
5bc392e6
EM
550 /**
551 * @param $contactIDs
552 * @param $additionalContacts
553 */
6a488035
TO
554 function addAdditionalContacts($contactIDs, &$additionalContacts) {
555 if (!$this->_discoverContacts) {
556 return;
557 }
558
559 foreach ($contactIDs as $cid) {
560 if ($cid &&
561 !isset($this->_allContactIDs[$cid]) &&
562 !isset($additionalContacts[$cid])
563 ) {
564 $additionalContacts[$cid] = $cid;
565 }
566 }
567 }
568
5bc392e6
EM
569 /**
570 * @param $contactIDs
571 */
6a488035 572 function export(&$contactIDs) {
3302658e 573 $chunks = & $this->splitContactIDs($contactIDs);
6a488035
TO
574
575 $additionalContactIDs = array();
576
577 foreach ($chunks as $chunk) {
578 $this->getValues($chunk, $additionalContactIDs);
579 }
580
581 if (!empty($additionalContactIDs)) {
582 $this->_allContactIDs = $this->_allContactIDs + $additionalContactIDs;
583 $this->export($additionalContactIDs);
584 }
585 }
586
5bc392e6 587 /**
100fef9d 588 * @param string $fileName
5bc392e6
EM
589 * @param null $lastExportTime
590 * @param bool $discoverContacts
591 */
6a488035 592 function run($fileName,
3302658e
TO
593 $lastExportTime = NULL,
594 $discoverContacts = FALSE
6a488035
TO
595 ) {
596 $this->_discoverContacts = $discoverContacts;
597
598 if (!$lastExportTime) {
599 $sql = "
600SELECT id
601FROM civicrm_contact
602";
603 }
604 else {
605 $sql = "(
606SELECT DISTINCT entity_id
607FROM civicrm_log
608WHERE entity_table = 'civicrm_contact'
609AND modified_date >= $lastExportTime
610) UNION (
611SELECT DISTINCT contact_id
612FROM civicrm_subscription_history
613WHERE date >= $lastExportTime
614)
615";
616 }
617
618
3302658e 619 $dao = & CRM_Core_DAO::executeQuery($sql);
6a488035
TO
620
621 $contactIDs = array();
622 while ($dao->fetch()) {
623 $contactIDs[$dao->id] = $dao->id;
624 }
625
626 $this->_allContactIDs = $contactIDs;
627 $this->_values = array();
628
629 $this->metaData();
630
631 $this->export($contactIDs);
632
633 $json = json_encode($this->_values, JSON_NUMERIC_CHECK);
634 file_put_contents($fileName,
635 $json
636 );
637
638 // print_r( json_decode( $json ) );
639 }
640}
641