3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2018 |
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-2018
35 * Class CRM_Export_BAO_ExportProcessor
37 * Class to handle logic of export.
39 class CRM_Export_BAO_ExportProcessor
{
49 protected $exportMode;
52 * Array of fields in the main query.
56 protected $queryFields = [];
63 protected $queryOperator;
66 * Requested output fields.
68 * If set to NULL then it is 'primary fields only'
69 * which actually means pretty close to all fields!
73 protected $requestedFields;
76 * Key representing the head of household in the relationship array.
78 * e.g. ['8_b_a' => 'Household Member Is', '8_a_b = 'Household Member Of'.....]
82 protected $relationshipTypes = [];
85 * Array of properties to retrieve for relationships.
89 protected $relationshipReturnProperties = [];
94 protected $returnProperties = [];
97 * CRM_Export_BAO_ExportProcessor constructor.
99 * @param int $exportMode
100 * @param array|NULL $requestedFields
101 * @param string $queryOperator
103 public function __construct($exportMode, $requestedFields, $queryOperator) {
104 $this->setExportMode($exportMode);
105 $this->setQueryMode();
106 $this->setQueryOperator($queryOperator);
107 $this->setRequestedFields($requestedFields);
108 $this->setRelationshipTypes();
114 public function getRequestedFields() {
115 return $this->requestedFields
;
119 * @param array|null $requestedFields
121 public function setRequestedFields($requestedFields) {
122 $this->requestedFields
= $requestedFields;
129 public function getReturnProperties() {
130 return $this->returnProperties
;
134 * @param array $returnProperties
136 public function setReturnProperties($returnProperties) {
137 $this->returnProperties
= $returnProperties;
143 public function getRelationshipTypes() {
144 return $this->relationshipTypes
;
149 public function setRelationshipTypes() {
150 $this->relationshipTypes
= CRM_Contact_BAO_Relationship
::getContactRelationshipType(
166 public function isRelationshipTypeKey($fieldName) {
167 return array_key_exists($fieldName, $this->relationshipTypes
);
173 public function getQueryOperator() {
174 return $this->queryOperator
;
178 * @param string $queryOperator
180 public function setQueryOperator($queryOperator) {
181 $this->queryOperator
= $queryOperator;
187 public function getQueryFields() {
188 return $this->queryFields
;
192 * @param array $queryFields
194 public function setQueryFields($queryFields) {
195 $this->queryFields
= $queryFields;
201 public function getQueryMode() {
202 return $this->queryMode
;
206 * Set the query mode based on the export mode.
208 public function setQueryMode() {
210 switch ($this->getExportMode()) {
211 case CRM_Export_Form_Select
::CONTRIBUTE_EXPORT
:
212 $this->queryMode
= CRM_Contact_BAO_Query
::MODE_CONTRIBUTE
;
215 case CRM_Export_Form_Select
::EVENT_EXPORT
:
216 $this->queryMode
= CRM_Contact_BAO_Query
::MODE_EVENT
;
219 case CRM_Export_Form_Select
::MEMBER_EXPORT
:
220 $this->queryMode
= CRM_Contact_BAO_Query
::MODE_MEMBER
;
223 case CRM_Export_Form_Select
::PLEDGE_EXPORT
:
224 $this->queryMode
= CRM_Contact_BAO_Query
::MODE_PLEDGE
;
227 case CRM_Export_Form_Select
::CASE_EXPORT
:
228 $this->queryMode
= CRM_Contact_BAO_Query
::MODE_CASE
;
231 case CRM_Export_Form_Select
::GRANT_EXPORT
:
232 $this->queryMode
= CRM_Contact_BAO_Query
::MODE_GRANT
;
235 case CRM_Export_Form_Select
::ACTIVITY_EXPORT
:
236 $this->queryMode
= CRM_Contact_BAO_Query
::MODE_ACTIVITY
;
240 $this->queryMode
= CRM_Contact_BAO_Query
::MODE_CONTACTS
;
247 public function getExportMode() {
248 return $this->exportMode
;
252 * @param int $exportMode
254 public function setExportMode($exportMode) {
255 $this->exportMode
= $exportMode;
261 * @param $returnProperties
264 public function runQuery($params, $order, $returnProperties) {
265 $query = new CRM_Contact_BAO_Query($params, $returnProperties, NULL,
266 FALSE, FALSE, $this->getQueryMode(),
267 FALSE, TRUE, TRUE, NULL, $this->getQueryOperator()
272 $query->_sort
= $order;
273 list($select, $from, $where, $having) = $query->query();
274 $this->setQueryFields($query->_fields
);
275 return array($query, $select, $from, $where, $having);
279 * Get array of fields to return, over & above those defined in the main contact exportable fields.
281 * These include export mode specific fields & some fields apparently required as 'exportableFields'
282 * but not returned by the function of the same name.
285 * Array of fields to return in the format ['field_name' => 1,...]
287 public function getAdditionalReturnProperties() {
296 if ($this->getQueryMode() === CRM_Contact_BAO_Query
::MODE_CONTACTS
) {
297 $componentSpecificFields = [];
300 $componentSpecificFields = CRM_Contact_BAO_Query
::defaultReturnProperties($this->getQueryMode());
302 if ($this->getQueryMode() === CRM_Contact_BAO_Query
::MODE_PLEDGE
) {
303 $componentSpecificFields = array_merge($componentSpecificFields, CRM_Pledge_BAO_Query
::extraReturnProperties($this->getQueryMode()));
304 unset($componentSpecificFields['contribution_status_id']);
305 unset($componentSpecificFields['pledge_status_id']);
306 unset($componentSpecificFields['pledge_payment_status_id']);
308 if ($this->getQueryMode() === CRM_Contact_BAO_Query
::MODE_CASE
) {
309 $componentSpecificFields = array_merge($componentSpecificFields, CRM_Case_BAO_Query
::extraReturnProperties($this->getQueryMode()));
311 if ($this->getQueryMode() === CRM_Contact_BAO_Query
::MODE_CONTRIBUTE
) {
312 $componentSpecificFields = array_merge($componentSpecificFields, CRM_Contribute_BAO_Query
::softCreditReturnProperties(TRUE));
313 unset($componentSpecificFields['contribution_status_id']);
315 return array_merge(array_fill_keys($missing, 1), $componentSpecificFields);
319 * Should payment fields be appended to the export.
321 * (This is pretty hacky so hopefully this function won't last long - notice
322 * how obviously it should be part of the above function!).
324 public function isExportPaymentFields() {
325 if ($this->getRequestedFields() === NULL
326 && in_array($this->getQueryMode(), [
327 CRM_Contact_BAO_Query
::MODE_EVENT
,
328 CRM_Contact_BAO_Query
::MODE_MEMBER
,
329 CRM_Contact_BAO_Query
::MODE_PLEDGE
,
333 elseif ($this->isExportSpecifiedPaymentFields()) {
340 * Has specific payment fields been requested (as opposed to via all fields).
342 * If specific fields have been requested then they get added at various points.
346 public function isExportSpecifiedPaymentFields() {
347 if ($this->getRequestedFields() !== NULL && $this->hasRequestedComponentPaymentFields()) {
353 * Get the name of the id field in the table that connects contributions to the export entity.
355 public function getPaymentTableID() {
356 if ($this->getRequestedFields() === NULL) {
358 CRM_Contact_BAO_Query
::MODE_EVENT
=> 'participant_id',
359 CRM_Contact_BAO_Query
::MODE_MEMBER
=> 'membership_id',
360 CRM_Contact_BAO_Query
::MODE_PLEDGE
=> 'pledge_payment_id',
362 return isset($mapping[$this->getQueryMode()]) ?
$mapping[$this->getQueryMode()] : '';
364 elseif ($this->hasRequestedComponentPaymentFields()) {
365 return 'participant_id';
371 * Have component payment fields been requested.
375 protected function hasRequestedComponentPaymentFields() {
376 if ($this->getQueryMode() === CRM_Contact_BAO_Query
::MODE_EVENT
) {
377 $participantPaymentFields = array_intersect_key($this->getComponentPaymentFields(), $this->getReturnProperties());
378 if (!empty($participantPaymentFields)) {
386 * Get fields that indicate payment fields have been requested for a component.
390 protected function getComponentPaymentFields() {
392 'componentPaymentField_total_amount' => ts('Total Amount'),
393 'componentPaymentField_contribution_status' => ts('Contribution Status'),
394 'componentPaymentField_received_date' => ts('Date Received'),
395 'componentPaymentField_payment_instrument' => ts('Payment Method'),
396 'componentPaymentField_transaction_id' => ts('Transaction ID'),
401 * Get the default properties when not specified.
403 * In the UI this appears as 'Primary fields only' but in practice it's
404 * most of the kitchen sink and the hallway closet thrown in.
406 * Since CRM-952 custom fields are excluded, but no other form of mercy is shown.
410 public function getDefaultReturnProperties() {
411 $returnProperties = [];
412 $fields = CRM_Contact_BAO_Contact
::exportableFields('All', TRUE, TRUE);
413 $skippedFields = ($this->getQueryMode() === CRM_Contact_BAO_Query
::MODE_CONTACTS
) ?
[] : [
419 foreach ($fields as $key => $var) {
420 if ($key && (substr($key, 0, 6) != 'custom') && !in_array($key, $skippedFields)) {
421 $returnProperties[$key] = 1;
424 $returnProperties = array_merge($returnProperties, $this->getAdditionalReturnProperties());
425 return $returnProperties;
429 * Add the field to relationship return properties & return it.
431 * This function is doing both setting & getting which is yuck but it is an interim
434 * @param array $value
435 * @param string $relationshipKey
439 public function setRelationshipReturnProperties($value, $relationshipKey) {
440 $locationTypes = CRM_Core_PseudoConstant
::get('CRM_Core_DAO_Address', 'location_type_id');
441 $relPhoneTypeId = $relIMProviderId = NULL;
442 if (!empty($value[2])) {
443 $relationField = CRM_Utils_Array
::value(2, $value);
444 if (trim(CRM_Utils_Array
::value(3, $value))) {
445 $relLocTypeId = CRM_Utils_Array
::value(3, $value);
448 $relLocTypeId = 'Primary';
451 if ($relationField == 'phone') {
452 $relPhoneTypeId = CRM_Utils_Array
::value(4, $value);
454 elseif ($relationField == 'im') {
455 $relIMProviderId = CRM_Utils_Array
::value(4, $value);
458 elseif (!empty($value[4])) {
459 $relationField = CRM_Utils_Array
::value(4, $value);
460 $relLocTypeId = CRM_Utils_Array
::value(5, $value);
461 if ($relationField == 'phone') {
462 $relPhoneTypeId = CRM_Utils_Array
::value(6, $value);
464 elseif ($relationField == 'im') {
465 $relIMProviderId = CRM_Utils_Array
::value(6, $value);
468 if (in_array($relationField, $this->getValidLocationFields()) && is_numeric($relLocTypeId)) {
469 if ($relPhoneTypeId) {
470 $this->relationshipReturnProperties
[$relationshipKey]['location'][$locationTypes[$relLocTypeId]]['phone-' . $relPhoneTypeId] = 1;
472 elseif ($relIMProviderId) {
473 $this->relationshipReturnProperties
[$relationshipKey]['location'][$locationTypes[$relLocTypeId]]['im-' . $relIMProviderId] = 1;
476 $this->relationshipReturnProperties
[$relationshipKey]['location'][$locationTypes[$relLocTypeId]][$relationField] = 1;
480 $this->relationshipReturnProperties
[$relationshipKey][$relationField] = 1;
482 return $this->relationshipReturnProperties
[$relationshipKey];
486 * Get the default location fields to request.
490 public function getValidLocationFields() {
493 'supplemental_address_1',
494 'supplemental_address_2',
495 'supplemental_address_3',
498 'postal_code_suffix',
510 * Get the sql column definition for the given field.
516 public function getSqlColumnDefinition($field) {
517 $fieldName = $this->getMungedFieldName($field);
519 // early exit for master_id, CRM-12100
520 // in the DB it is an ID, but in the export, we retrive the display_name of the master record
521 // also for current_employer, CRM-16939
522 if ($fieldName == 'master_id' ||
$fieldName == 'current_employer') {
523 return "$fieldName varchar(128)";
526 if (substr($fieldName, -11) == 'campaign_id') {
528 return "$fieldName varchar(128)";
531 $queryFields = $this->getQueryFields();
532 $lookUp = ['prefix_id', 'suffix_id'];
533 // set the sql columns
534 if (isset($queryFields[$field]['type'])) {
535 switch ($queryFields[$field]['type']) {
536 case CRM_Utils_Type
::T_INT
:
537 case CRM_Utils_Type
::T_BOOLEAN
:
538 if (in_array($field, $lookUp)) {
539 return "$fieldName varchar(255)";
542 return "$fieldName varchar(16)";
545 case CRM_Utils_Type
::T_STRING
:
546 if (isset($queryFields[$field]['maxlength'])) {
547 return "$fieldName varchar({$queryFields[$field]['maxlength']})";
550 return "$fieldName varchar(255)";
553 case CRM_Utils_Type
::T_TEXT
:
554 case CRM_Utils_Type
::T_LONGTEXT
:
555 case CRM_Utils_Type
::T_BLOB
:
556 case CRM_Utils_Type
::T_MEDIUMBLOB
:
557 return "$fieldName longtext";
559 case CRM_Utils_Type
::T_FLOAT
:
560 case CRM_Utils_Type
::T_ENUM
:
561 case CRM_Utils_Type
::T_DATE
:
562 case CRM_Utils_Type
::T_TIME
:
563 case CRM_Utils_Type
::T_TIMESTAMP
:
564 case CRM_Utils_Type
::T_MONEY
:
565 case CRM_Utils_Type
::T_EMAIL
:
566 case CRM_Utils_Type
::T_URL
:
567 case CRM_Utils_Type
::T_CCNUM
:
569 return "$fieldName varchar(32)";
573 if (substr($fieldName, -3, 3) == '_id') {
574 return "$fieldName varchar(255)";
576 elseif (substr($fieldName, -5, 5) == '_note') {
577 return "$fieldName text";
586 if (in_array($fieldName, $changeFields)) {
587 return "$fieldName text";
590 // set the sql columns for custom data
591 if (isset($queryFields[$field]['data_type'])) {
593 switch ($queryFields[$field]['data_type']) {
595 // May be option labels, which could be up to 512 characters
596 $length = max(512, CRM_Utils_Array
::value('text_length', $queryFields[$field]));
597 return "$fieldName varchar($length)";
600 case 'StateProvince':
602 return "$fieldName varchar(255)";
605 return "$fieldName text";
608 return "$fieldName varchar(255)";
612 return "$fieldName text";
620 * Get the munged field name.
622 * @param string $field
625 public function getMungedFieldName($field) {
626 $fieldName = CRM_Utils_String
::munge(strtolower($field), '_', 64);
627 if ($fieldName == 'id') {
628 $fieldName = 'civicrm_primary_id';