3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2015
33 class CRM_Utils_Migrate_Export
{
35 const XML_VALUE_SEPARATOR
= ":;:;:;";
38 * @var array description of export field mapping
41 * 'exampleEntityMappingName' => array(
42 * 'data' => array(), // placeholder; this will get filled-in during execution
43 * 'name' => 'CustomGroup', // per-item XML tag name
44 * 'scope' => 'CustomGroups', // container XML tag name
45 * 'required' => FALSE, // whether we *must* find records of this type
46 * 'idNameFields' => array('id', 'name'), // name of the (local/autogenerated) "id" and (portable) "name" columns
47 * 'idNameMap' => array(), // placeholder; this will get filled-in during execution
53 public function __construct() {
55 'customGroup' => array(
57 'name' => 'CustomGroup',
58 'scope' => 'CustomGroups',
60 'idNameFields' => array('id', 'name'),
61 'idNameMap' => array(),
63 'customField' => array(
65 'name' => 'CustomField',
66 'scope' => 'CustomFields',
68 'idNameFields' => array('id', 'column_name'),
69 'idNameMap' => array(),
70 'mappedFields' => array(
71 array('optionGroup', 'option_group_id', 'option_group_name'),
72 array('customGroup', 'custom_group_id', 'custom_group_name'),
75 'optionGroup' => array(
77 'name' => 'OptionGroup',
78 'scope' => 'OptionGroups',
80 'idNameMap' => array(),
81 'idNameFields' => array('id', 'name'),
83 'relationshipType' => array(
85 'name' => 'RelationshipType',
86 'scope' => 'RelationshipTypes',
88 'idNameFields' => array('id', 'name_a_b'),
89 'idNameMap' => array(),
91 'locationType' => array(
93 'name' => 'LocationType',
94 'scope' => 'LocationTypes',
96 'idNameFields' => array('id', 'name'),
97 'idNameMap' => array(),
99 'optionValue' => array(
101 'name' => 'OptionValue',
102 'scope' => 'OptionValues',
104 'idNameMap' => array(),
105 'idNameFields' => array('value', 'name', 'prefix'),
106 'mappedFields' => array(
107 array('optionGroup', 'option_group_id', 'option_group_name'),
110 'profileGroup' => array(
112 'name' => 'ProfileGroup',
113 'scope' => 'ProfileGroups',
115 'idNameFields' => array('id', 'title'),
116 'idNameMap' => array(),
118 'profileField' => array(
120 'name' => 'ProfileField',
121 'scope' => 'ProfileFields',
123 'idNameMap' => array(),
124 'mappedFields' => array(
125 array('profileGroup', 'uf_group_id', 'profile_group_name'),
128 'profileJoin' => array(
130 'name' => 'ProfileJoin',
131 'scope' => 'ProfileJoins',
133 'idNameMap' => array(),
134 'mappedFields' => array(
135 array('profileGroup', 'uf_group_id', 'profile_group_name'),
138 'mappingGroup' => array(
140 'name' => 'MappingGroup',
141 'scope' => 'MappingGroups',
143 'idNameFields' => array('id', 'name'),
144 'idNameMap' => array(),
145 'mappedFields' => array(
146 array('optionValue', 'mapping_type_id', 'mapping_type_name', 'mapping_type'),
149 'mappingField' => array(
151 'name' => 'MappingField',
152 'scope' => 'MappingFields',
154 'idNameMap' => array(),
155 'mappedFields' => array(
156 array('mappingGroup', 'mapping_id', 'mapping_group_name'),
157 array('locationType', 'location_type_id', 'location_type_name'),
158 array('relationshipType', 'relationship_type_id', 'relationship_type_name'),
165 * Scan local customizations and build an in-memory representation.
167 public function build() {
168 // fetch the option group / values for
169 // activity type and event_type
171 $optionGroups = "( 'activity_type', 'event_type', 'mapping_type' )";
174 SELECT distinct(g.id), g.*
175 FROM civicrm_option_group g
176 WHERE g.name IN $optionGroups
178 $this->fetch('optionGroup', 'CRM_Core_DAO_OptionGroup', $sql);
181 SELECT distinct(g.id), g.*
182 FROM civicrm_option_group g,
183 civicrm_custom_field f,
184 civicrm_custom_group cg
185 WHERE f.option_group_id = g.id
186 AND f.custom_group_id = cg.id
189 $this->fetch('optionGroup', 'CRM_Core_DAO_OptionGroup', $sql);
192 SELECT v.*, g.name as prefix
193 FROM civicrm_option_value v,
194 civicrm_option_group g
195 WHERE v.option_group_id = g.id
196 AND g.name IN $optionGroups
199 $this->fetch('optionValue', 'CRM_Core_DAO_OptionValue', $sql);
202 SELECT distinct(v.id), v.*, g.name as prefix
203 FROM civicrm_option_value v,
204 civicrm_option_group g,
205 civicrm_custom_field f,
206 civicrm_custom_group cg
207 WHERE v.option_group_id = g.id
208 AND f.option_group_id = g.id
209 AND f.custom_group_id = cg.id
213 $this->fetch('optionValue', 'CRM_Core_DAO_OptionValue', $sql);
217 FROM civicrm_relationship_type rt
218 WHERE rt.is_active = 1
220 $this->fetch('relationshipType', 'CRM_Contact_DAO_RelationshipType', $sql);
224 FROM civicrm_location_type lt
225 WHERE lt.is_active = 1
227 $this->fetch('locationType', 'CRM_Core_DAO_LocationType', $sql);
231 FROM civicrm_custom_group cg
232 WHERE cg.is_active = 1
234 $this->fetch('customGroup', 'CRM_Core_DAO_CustomGroup', $sql);
238 FROM civicrm_custom_field f,
239 civicrm_custom_group cg
240 WHERE f.custom_group_id = cg.id
243 $this->fetch('customField', 'CRM_Core_DAO_CustomField', $sql);
245 $this->fetch('profileGroup', 'CRM_Core_DAO_UFGroup');
247 $this->fetch('profileField', 'CRM_Core_DAO_UFField');
252 WHERE entity_table IS NULL
253 AND entity_id IS NULL
255 $this->fetch('profileJoin', 'CRM_Core_DAO_UFJoin', $sql);
257 $this->fetch('mappingGroup', 'CRM_Core_DAO_Mapping');
259 $this->fetch('mappingField', 'CRM_Core_DAO_MappingField');
263 * Build custom groups.
265 * @param array $customGroupIds
266 * List of custom groups to export.
268 public function buildCustomGroups($customGroupIds) {
269 $customGroupIdsSql = implode(',', array_filter($customGroupIds, 'is_numeric'));
270 if (empty($customGroupIdsSql)) {
275 SELECT distinct(g.id), g.*
276 FROM civicrm_option_group g,
277 civicrm_custom_field f,
278 civicrm_custom_group cg
279 WHERE f.option_group_id = g.id
280 AND f.custom_group_id = cg.id
281 AND cg.id in ($customGroupIdsSql)
283 $this->fetch('optionGroup', 'CRM_Core_DAO_OptionGroup', $sql);
286 SELECT distinct(v.id), v.*, g.name as prefix
287 FROM civicrm_option_value v,
288 civicrm_option_group g,
289 civicrm_custom_field f,
290 civicrm_custom_group cg
291 WHERE v.option_group_id = g.id
292 AND f.option_group_id = g.id
293 AND f.custom_group_id = cg.id
294 AND cg.id in ($customGroupIdsSql)
297 $this->fetch('optionValue', 'CRM_Core_DAO_OptionValue', $sql);
301 FROM civicrm_custom_group cg
302 WHERE cg.id in ($customGroupIdsSql)
305 $this->fetch('customGroup', 'CRM_Core_DAO_CustomGroup', $sql);
309 FROM civicrm_custom_field f,
310 civicrm_custom_group cg
311 WHERE f.custom_group_id = cg.id
312 AND cg.id in ($customGroupIdsSql)
314 $this->fetch('customField', 'CRM_Core_DAO_CustomField', $sql);
318 * @param array $ufGroupIds
319 * List of custom groups to export.
321 public function buildUFGroups($ufGroupIds) {
322 $ufGroupIdsSql = implode(',', array_filter($ufGroupIds, 'is_numeric'));
323 if (empty($ufGroupIdsSql)) {
329 FROM civicrm_uf_group cg
330 WHERE cg.id IN ($ufGroupIdsSql)
333 $this->fetch('profileGroup', 'CRM_Core_DAO_UFGroup', $sql);
337 FROM civicrm_uf_field f,
339 WHERE f.uf_group_id = cg.id
340 AND cg.id IN ($ufGroupIdsSql)
342 $this->fetch('profileField', 'CRM_Core_DAO_UFField', $sql);
347 WHERE entity_table IS NULL
348 AND entity_id IS NULL
349 AND uf_group_id IN ($ufGroupIdsSql)
351 $this->fetch('profileJoin', 'CRM_Core_DAO_UFJoin', $sql);
355 * Render the in-memory representation as XML
360 public function toXML() {
361 $buffer = '<?xml version="1.0" encoding="iso-8859-1" ?>';
362 $buffer .= "\n\n<CustomData>\n";
363 foreach (array_keys($this->_xml
) as $key) {
364 if (!empty($this->_xml
[$key]['data'])) {
365 $buffer .= " <{$this->_xml[$key]['scope']}>\n";
366 foreach ($this->_xml
[$key]['data'] as $item) {
367 $buffer .= $this->renderKeyValueXML($this->_xml
[$key]['name'], $item);
369 $buffer .= " </{$this->_xml[$key]['scope']}>\n";
371 elseif ($this->_xml
[$key]['required']) {
372 CRM_Core_Error
::fatal("No records in DB for $key");
375 $buffer .= "</CustomData>\n";
380 * Generate an array-tree representation of the exported elements.
384 public function toArray() {
386 foreach (array_keys($this->_xml
) as $key) {
387 if (!empty($this->_xml
[$key]['data'])) {
388 $result[$this->_xml
[$key]['name']] = array_values($this->_xml
[$key]['data']);
395 * @param string $groupName
396 * @param string $daoName
399 public function fetch($groupName, $daoName, $sql = NULL) {
400 $idNameFields = isset($this->_xml
[$groupName]['idNameFields']) ?
$this->_xml
[$groupName]['idNameFields'] : NULL;
401 $mappedFields = isset($this->_xml
[$groupName]['mappedFields']) ?
$this->_xml
[$groupName]['mappedFields'] : NULL;
403 $dao = new $daoName();
411 while ($dao->fetch()) {
412 $this->_xml
[$groupName]['data'][$dao->id
] = $this->exportDAO($this->_xml
[$groupName]['name'], $dao, $mappedFields);
414 // index the id/name fields so that we can translate from FK ids to FK names
415 if (isset($idNameFields[2])) {
416 $this->_xml
[$groupName]['idNameMap'][$dao->{$idNameFields[2]} . '.' . $dao->{$idNameFields[0]}] = $dao->{$idNameFields[1]};
419 $this->_xml
[$groupName]['idNameMap'][$dao->{$idNameFields[0]}] = $dao->{$idNameFields[1]};
426 * Compute any fields of the entity defined by the $mappedFields specification
428 * @param array $mappedFields
429 * Each item is an array(0 => MappedEntityname, 1 => InputFieldName (id-field), 2 => OutputFieldName (name-field), 3 => OptionalPrefix).
430 * @param CRM_Core_DAO $dao
431 * The entity for which we want to prepare mapped fields.
435 public function computeMappedFields($mappedFields, $dao) {
436 $keyValues = array();
438 foreach ($mappedFields as $mappedField) {
439 if (isset($dao->{$mappedField[1]})) {
440 if (isset($mappedField[3])) {
441 $label = $this->_xml
[$mappedField[0]]['idNameMap']["{$mappedField[3]}." . $dao->{$mappedField[1]}];
444 $label = $this->_xml
[$mappedField[0]]['idNameMap'][$dao->{$mappedField[1]}];
446 $keyValues[$mappedField[2]] = $label;
454 * @param string $objectName
455 * Business-entity/xml-tag name.
456 * @param CRM_Core_DAO $object
457 * @param $mappedFields
461 public function exportDAO($objectName, $object, $mappedFields) {
462 $dbFields = &$object->fields();
464 // Filter the list of keys and values so that we only export interesting stuff
465 $keyValues = array();
466 foreach ($dbFields as $name => $dontCare) {
468 if ($name == 'id' ||
substr($name, -3, 3) == '_id') {
471 if (isset($object->$name) && $object->$name !== NULL) {
472 // hack for extends_entity_column_value
473 if ($name == 'extends_entity_column_value') {
474 if (in_array($object->extends, array(
483 if ($object->extends == 'Event') {
486 elseif ($object->extends == 'Activity') {
487 $key = 'activity_type';
489 elseif ($object->extends == 'Relationship') {
490 $key = 'relationship_type';
492 elseif ($object->extends == 'Case') {
495 $types = explode(CRM_Core_DAO
::VALUE_SEPARATOR
, substr($object->$name, 1, -1));
497 if (in_array($object->extends, array('Individual', 'Organization', 'Household'))) {
498 $key = 'contact_type';
502 foreach ($types as $type) {
503 if (in_array($key, array('activity_type', 'event_type', 'case_type'))) {
504 $ogID = CRM_Core_DAO
::getFieldValue('CRM_Core_DAO_OptionGroup', $key, 'id', 'name');
505 $ovParams = array('option_group_id' => $ogID, 'value' => $type);
506 CRM_Core_BAO_OptionValue
::retrieve($ovParams, $oValue);
507 $values[] = $oValue['name'];
510 $relTypeName = CRM_Core_DAO
::getFieldValue('CRM_Contact_BAO_RelationshipType', $type, 'name_a_b', 'id');
511 $values[] = $relTypeName;
515 $keyValues['extends_entity_column_value_option_group'] = $key;
516 $value = implode(',', $values);
517 $object->extends_entity_column_value
= $value;
520 echo "This extension: {$object->extends} is not yet handled";
525 $value = $object->$name;
526 if ($name == 'field_name') {
527 // hack for profile field_name
528 if (substr($value, 0, 7) == 'custom_') {
529 $cfID = substr($value, 7);
530 list($tableName, $columnName, $groupID) = CRM_Core_BAO_CustomField
::getTableColumnGroup($cfID);
531 $value = "custom.{$tableName}.{$columnName}";
534 $keyValues[$name] = $value;
538 $keyValues +
= $this->computeMappedFields($mappedFields, $object);
544 * @param string $tagName
545 * @param array $keyValues
550 public function renderKeyValueXML($tagName, $keyValues) {
551 $xml = " <$tagName>";
552 foreach ($keyValues as $k => $v) {
553 $xml .= "\n " . $this->renderTextTag($k, str_replace(CRM_Core_DAO
::VALUE_SEPARATOR
, self
::XML_VALUE_SEPARATOR
, $v));
555 $xml .= "\n </$tagName>\n";
560 * @param string $name
562 * @param string $value
564 * @param string $prefix
570 public function renderTextTag($name, $value, $prefix = '') {
571 if (!preg_match('/^[a-zA-Z0-9\_]+$/', $name)) {
572 throw new Exception("Malformed tag name: $name");
574 return $prefix . "<$name>" . htmlentities($value) . "</$name>";