Issues (3627)

Integration/SugarcrmIntegration.php (18 issues)

1
<?php
2
3
/*
4
 * @copyright   2014 Mautic Contributors. All rights reserved
5
 * @author      Mautic
6
 *
7
 * @link        http://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace MauticPlugin\MauticCrmBundle\Integration;
13
14
use Doctrine\ORM\EntityManager;
15
use Mautic\CoreBundle\Form\Type\ButtonGroupType;
16
use Mautic\CoreBundle\Helper\CacheStorageHelper;
17
use Mautic\CoreBundle\Helper\EncryptionHelper;
18
use Mautic\CoreBundle\Helper\PathsHelper;
19
use Mautic\CoreBundle\Model\NotificationModel;
20
use Mautic\LeadBundle\Entity\Company;
21
use Mautic\LeadBundle\Entity\Lead;
22
use Mautic\LeadBundle\Model\CompanyModel;
23
use Mautic\LeadBundle\Model\DoNotContact;
24
use Mautic\LeadBundle\Model\FieldModel;
25
use Mautic\LeadBundle\Model\LeadModel;
26
use Mautic\PluginBundle\Entity\IntegrationEntity;
27
use Mautic\PluginBundle\Entity\IntegrationEntityRepository;
28
use Mautic\PluginBundle\Exception\ApiErrorException;
29
use Mautic\PluginBundle\Model\IntegrationEntityModel;
30
use Mautic\UserBundle\Model\UserModel;
31
use Monolog\Logger;
32
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
33
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
34
use Symfony\Component\Form\FormBuilder;
35
use Symfony\Component\HttpFoundation\RequestStack;
36
use Symfony\Component\HttpFoundation\Session\Session;
37
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
38
use Symfony\Component\Routing\Router;
39
use Symfony\Component\Translation\TranslatorInterface;
40
use Symfony\Component\Validator\Constraints\NotBlank;
41
42
class SugarcrmIntegration extends CrmAbstractIntegration
43
{
44
    private $objects = [
45
        'Leads',
46
        'Contacts',
47
        'Accounts',
48
    ];
49
50
    private $sugarDncKeys = ['email_opt_out', 'invalid_email'];
51
    private $authorizationError;
52
    private $userModel;
53
54
    /**
55
     * @var DoNotContact
56
     */
57
    protected $doNotContactModel;
58
59
    public function __construct(
60
        EventDispatcherInterface $eventDispatcher,
61
        CacheStorageHelper $cacheStorageHelper,
62
        EntityManager $entityManager,
63
        Session $session,
64
        RequestStack $requestStack,
65
        Router $router,
66
        TranslatorInterface $translator,
67
        Logger $logger,
68
        EncryptionHelper $encryptionHelper,
69
        LeadModel $leadModel,
70
        CompanyModel $companyModel,
71
        PathsHelper $pathsHelper,
72
        NotificationModel $notificationModel,
73
        FieldModel $fieldModel,
74
        IntegrationEntityModel $integrationEntityModel,
75
        DoNotContact $doNotContactModel,
76
        UserModel $userModel
77
    ) {
78
        $this->doNotContactModel = $doNotContactModel;
79
        $this->userModel         = $userModel;
80
81
        parent::__construct(
82
            $eventDispatcher,
83
            $cacheStorageHelper,
84
            $entityManager,
85
            $session,
86
            $requestStack,
87
            $router,
88
            $translator,
89
            $logger,
90
            $encryptionHelper,
91
            $leadModel,
92
            $companyModel,
93
            $pathsHelper,
94
            $notificationModel,
95
            $fieldModel,
96
            $integrationEntityModel,
97
            $doNotContactModel
98
        );
99
    }
100
101
    /**
102
     * Returns the name of the social integration that must match the name of the file.
103
     *
104
     * @return string
105
     */
106
    public function getName()
107
    {
108
        return 'Sugarcrm';
109
    }
110
111
    /**
112
     * @return array
113
     */
114
    public function getSupportedFeatures()
115
    {
116
        //Version 6.x supports all features
117
        if (isset($this->keys['version']) && '6' == $this->keys['version']) {
118
            return ['push_lead', 'get_leads', 'push_leads'];
119
        }
120
        //Only push_lead is currently supported for version 7
121
        return ['push_lead', 'get_leads', 'push_leads'];
122
    }
123
124
    /**
125
     * Get the array key for clientId.
126
     *
127
     * @return string
128
     */
129
    public function getClientIdKey()
130
    {
131
        return 'client_id';
132
    }
133
134
    /**
135
     * Get the array key for client secret.
136
     *
137
     * @return string
138
     */
139
    public function getClientSecretKey()
140
    {
141
        return 'client_secret';
142
    }
143
144
    /**
145
     * {@inheritdoc}
146
     *
147
     * @return array
148
     */
149
    public function getSecretKeys()
150
    {
151
        return [
152
            'client_secret',
153
            'password',
154
        ];
155
    }
156
157
    /**
158
     * Get the array key for the auth token.
159
     *
160
     * @return string
161
     */
162
    public function getAuthTokenKey()
163
    {
164
        return (isset($this->keys['version']) && '6' == $this->keys['version']) ? 'id' : 'access_token';
165
    }
166
167
    /**
168
     * SugarCRM 7 refresh tokens.
169
     */
170
    public function getRefreshTokenKeys()
171
    {
172
        return [
173
            'refresh_token',
174
            'expires',
175
        ];
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     *
181
     * @return string
182
     */
183
    public function getAccessTokenUrl()
184
    {
185
        $apiUrl = ('6' == $this->keys['version']) ? 'service/v4_1/rest.php' : 'rest/v10/oauth2/token';
186
187
        return sprintf('%s/%s', $this->keys['sugarcrm_url'], $apiUrl);
188
    }
189
190
    /**
191
     * {@inheritdoc}
192
     *
193
     * @return string
194
     */
195
    public function getAuthLoginUrl()
196
    {
197
        return $this->router->generate('mautic_integration_auth_callback', ['integration' => $this->getName()]);
198
    }
199
200
    /**
201
     * Retrieves and stores tokens returned from oAuthLogin.
202
     *
203
     * @param array $settings
204
     * @param array $parameters
205
     *
206
     * @return array
207
     */
208
    public function authCallback($settings = [], $parameters = [])
209
    {
210
        if (isset($this->keys['version']) && '6' == $this->keys['version']) {
211
            $success = $this->isAuthorized();
212
            if (!$success) {
213
                return $this->authorizationError;
214
            } else {
215
                return false;
216
            }
217
        } else {
218
            $settings = [
219
                'grant_type'         => 'password',
220
                'ignore_redirecturi' => true,
221
            ];
222
            $parameters = [
223
                'username' => $this->keys['username'],
224
                'password' => $this->keys['password'],
225
                'platform' => 'base',
226
            ];
227
228
            return parent::authCallback($settings, $parameters);
229
        }
230
    }
231
232
    /**
233
     * {@inheritdoc}
234
     *
235
     * @return array
236
     */
237
    public function getRequiredKeyFields()
238
    {
239
        return [
240
            'sugarcrm_url'  => 'mautic.sugarcrm.form.url',
241
            'client_id'     => 'mautic.sugarcrm.form.clientkey',
242
            'client_secret' => 'mautic.sugarcrm.form.clientsecret',
243
            'username'      => 'mautic.sugarcrm.form.username',
244
            'password'      => 'mautic.sugarcrm.form.password',
245
        ];
246
    }
247
248
    /**
249
     * Get available company fields for choices in the config UI.
250
     *
251
     * @param array $settings
252
     *
253
     * @return array
254
     */
255
    public function getFormCompanyFields($settings = [])
256
    {
257
        return $this->getFormFieldsByObject('company', $settings);
258
    }
259
260
    /**
261
     * Get available fields for choices in the config UI.
262
     *
263
     * @param array $settings
264
     *
265
     * @return array
266
     */
267
    public function getFormLeadFields($settings = [])
268
    {
269
        if (!$this->isAuthorized()) {
270
            return [];
271
        }
272
273
        if (isset($settings['feature_settings']['objects'])) {
274
            // combine keys with values
275
            $settings['feature_settings']['objects'] = array_combine(
276
                array_values($settings['feature_settings']['objects']),
277
                $settings['feature_settings']['objects']
278
            );
279
        }
280
281
        // unset company object
282
        if (isset($settings['feature_settings']['objects']['company'])) {
283
            unset($settings['feature_settings']['objects']['company']);
284
        }
285
286
        if (empty($settings['feature_settings']['objects'])) {
287
            // BC force add Leads and Contacts from Integration
288
            $settings['feature_settings']['objects']['Leads']    = 'Leads';
289
            $settings['feature_settings']['objects']['Contacts'] = 'Contacts';
290
        }
291
292
        $fields = [];
293
        // merge all arrays from level 1
294
        $fieldsromObjects = $this->getAvailableLeadFields($settings);
295
        foreach ($fieldsromObjects as $fieldsFromObject) {
296
            $fields = array_merge($fields, $fieldsFromObject);
297
        }
298
299
        return $fields;
300
    }
301
302
    /**
303
     * @param array $settings
304
     *
305
     * @return array
306
     *
307
     * @throws \Exception
308
     */
309
    public function getAvailableLeadFields($settings = [])
310
    {
311
        $sugarFields       = [];
312
        $silenceExceptions = (isset($settings['silence_exceptions'])) ? $settings['silence_exceptions'] : true;
313
        $sugarObjects      = [];
314
315
        if (!empty($settings['feature_settings']['objects'])) {
316
            $sugarObjects = $settings['feature_settings']['objects'];
317
        } else {
318
            $sugarObjects['Leads']                   = 'Leads';
319
            $sugarObjects['Contacts']                = 'Contacts';
320
            $settings['feature_settings']['objects'] = $sugarObjects;
321
        }
322
323
        $isRequired = function (array $field, $object) {
324
            switch (true) {
325
                case 'Leads' === $object && ('webtolead_email1' === $field['name'] || 'email1' === $field['name']):
326
                case 'Contacts' === $object && 'email1' === $field['name']:
327
                case 'id' !== $field['name'] && !empty($field['required']):
328
                    return true;
329
                default:
330
                    return false;
331
            }
332
        };
333
334
        try {
335
            if (!empty($sugarObjects) and is_array($sugarObjects)) {
336
                foreach ($sugarObjects as $sObject) {
337
                    if ('Accounts' === $sObject) {
338
                        // Match Sugar object to Mautic's
339
                        $sObject = 'company';
340
                    }
341
                    $sObject = trim($sObject);
342
                    if ($this->isAuthorized()) {
343
                        // Check the cache first
344
                        $settings['cache_suffix'] = $cacheSuffix = '.'.$sObject;
345
                        if ($fields = parent::getAvailableLeadFields($settings)) {
346
                            if (('company' === $sObject && isset($fields['id'])) || isset($fields['id__'.$sObject])) {
347
                                $sugarFields[$sObject] = $fields;
348
                                continue;
349
                            }
350
                        }
351
                        if (!isset($sugarFields[$sObject])) {
352
                            $fields = $this->getApiHelper()->getLeadFields($sObject);
353
354
                            if (null != $fields && !empty($fields)) {
355
                                if (isset($fields['module_fields']) && !empty($fields['module_fields'])) {
356
                                    //6.x/community
357
358
                                    foreach ($fields['module_fields'] as $fieldInfo) {
359
                                        if (isset($fieldInfo['name']) && (!in_array($fieldInfo['type'], ['id', 'assigned_user_name', 'link', 'relate']) || ('id' == $fieldInfo['type'] && 'id' == $fieldInfo['name'])
360
                                            )
361
                                        ) {
362
                                            $type      = 'string';
363
                                            $fieldName = (false === strpos($fieldInfo['name'],
364
                                                    'webtolead_email')) ? $fieldInfo['name'] : str_replace('webtolead_',
365
                                                '', $fieldInfo['name']);
366
                                            // make these congruent as some come in with colons and some do not
367
                                            $label = str_replace(':', '', $fieldInfo['label']);
368
                                            if ('company' !== $sObject) {
369
                                                $sugarFields[$sObject][$fieldName.'__'.$sObject] = [
370
                                                    'type'        => $type,
371
                                                    'label'       => $sObject.'-'.$label,
372
                                                    'required'    => $isRequired($fieldInfo, $sObject),
373
                                                    'group'       => $sObject,
374
                                                    'optionLabel' => $fieldInfo['label'],
375
                                                ];
376
                                            } else {
377
                                                $sugarFields[$sObject][$fieldName] = [
378
                                                    'type'     => $type,
379
                                                    'label'    => $label,
380
                                                    'required' => $isRequired($fieldInfo, $sObject),
381
                                                ];
382
                                            }
383
                                        }
384
                                    }
385
                                } elseif (isset($fields['fields']) && !empty($fields['fields'])) {
386
                                    //7.x
387
                                    foreach ($fields['fields'] as $fieldInfo) {
388
                                        if (isset($fieldInfo['name']) && empty($fieldInfo['readonly'])
389
                                            && (!in_array(
390
                                                    $fieldInfo['type'],
391
                                                    ['id', 'team_list', 'link', 'relate']
392
                                                )
393
                                                ||
394
                                                ('id' == $fieldInfo['type'] && 'id' == $fieldInfo['name'])
395
                                            )
396
                                        ) {
397
                                            if (!empty($fieldInfo['comment'])) {
398
                                                $label = $fieldInfo['comment'];
399
                                            } elseif (!empty($fieldInfo['help'])) {
400
                                                $label = $fieldInfo['help'];
401
                                            } else {
402
                                                $label = ucfirst(str_replace('_', ' ', $fieldInfo['name']));
403
                                            }
404
                                            // make these congruent as some come in with colons and some do not
405
                                            $label = str_replace(':', '', $label);
406
407
                                            $fieldName = (false === strpos($fieldInfo['name'], 'webtolead_email'))
408
                                                ? $fieldInfo['name']
409
                                                : str_replace(
410
                                                    'webtolead_',
411
                                                    '',
412
                                                    $fieldInfo['name']
413
                                                );
414
415
                                            $type = 'string';
416
                                            if ('company' !== $sObject) {
417
                                                $sugarFields[$sObject][$fieldName.'__'.$sObject] = [
418
                                                    'type'        => $type,
419
                                                    'label'       => $sObject.'-'.$label,
420
                                                    'required'    => $isRequired($fieldInfo, $sObject),
421
                                                    'group'       => $sObject,
422
                                                    'optionLabel' => $label,
423
                                                ];
424
                                            } else {
425
                                                $sugarFields[$sObject][$fieldName] = [
426
                                                    'type'     => $type,
427
                                                    'label'    => $label,
428
                                                    'required' => $isRequired($fieldInfo, $sObject),
429
                                                ];
430
                                            }
431
                                        }
432
                                    }
433
                                }
434
                            }
435
                            $this->cache->set('leadFields'.$cacheSuffix, $sugarFields[$sObject]);
436
                        }
437
                    } else {
438
                        throw new ApiErrorException($this->authorizationError);
439
                    }
440
                }
441
            }
442
        } catch (\Exception $e) {
443
            $this->logIntegrationError($e);
444
445
            if (!$silenceExceptions) {
446
                throw $e;
447
            }
448
        }
449
450
        return $sugarFields;
451
    }
452
453
    /**
454
     * @param $params
455
     *
456
     * @return mixed
457
     */
458
    public function getFetchQuery($params)
459
    {
460
        return $params;
461
    }
462
463
    /**
464
     * @param array      $params
465
     * @param array|null $query
466
     *
467
     * @return int|null
468
     */
469
    public function getCompanies($params = [], $query = null, $executed = null)
0 ignored issues
show
The parameter $query is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

469
    public function getCompanies($params = [], /** @scrutinizer ignore-unused */ $query = null, $executed = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
The parameter $executed is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

469
    public function getCompanies($params = [], $query = null, /** @scrutinizer ignore-unused */ $executed = null)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
470
    {
471
        $executed = null;
472
473
        $sugarObject           = 'Accounts';
474
        $params['max_results'] = 100;
475
        if (!isset($params['offset'])) {
476
            //First call
477
            $params['offset'] = 0;
478
        }
479
480
        $query = $params;
481
482
        try {
483
            if ($this->isAuthorized()) {
484
                $result           = $this->getApiHelper()->getLeads($query, $sugarObject);
0 ignored issues
show
The method getLeads() does not exist on MauticPlugin\MauticCrmBundle\Api\CrmApi. It seems like you code against a sub-type of MauticPlugin\MauticCrmBundle\Api\CrmApi such as MauticPlugin\MauticCrmBundle\Api\DynamicsApi or MauticPlugin\MauticCrmBundle\Api\SalesforceApi or MauticPlugin\MauticCrmBundle\Api\ZohoApi or MauticPlugin\MauticCrmBundle\Api\SugarcrmApi. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

484
                $result           = $this->getApiHelper()->/** @scrutinizer ignore-call */ getLeads($query, $sugarObject);
Loading history...
485
                $params['offset'] = $result['next_offset'];
486
                $executed += $this->amendLeadDataBeforeMauticPopulate($result, $sugarObject);
487
                if (
488
                    (isset($result['total_count']) && $result['total_count'] > $params['offset'])   //Sugar 6
489
                    || (!isset($result['total_count']) && $params['offset'] > -1)) {            //Sugar 7
490
                    $result = null;
0 ignored issues
show
The assignment to $result is dead and can be removed.
Loading history...
491
                    $executed += $this->getCompanies($params, null, $executed);
492
                }
493
494
                return $executed;
495
            }
496
        } catch (\Exception $e) {
497
            $this->logIntegrationError($e);
498
        }
499
500
        return $executed;
501
    }
502
503
    /**
504
     * @param array $params
505
     *
506
     * @return int|null
507
     *
508
     * @throws \Exception
509
     *                    To be modified
510
     */
511
    public function pushLeadActivity($params = [])
512
    {
513
        $executed = null;
514
515
        $query  = $this->getFetchQuery($params);
516
        $config = $this->mergeConfigToFeatureSettings([]);
517
518
        /** @var SugarApi $apiHelper */
519
        $apiHelper = $this->getApiHelper();
520
521
        $sugarObjects[] = 'Leads';
0 ignored issues
show
Comprehensibility Best Practice introduced by
$sugarObjects was never initialized. Although not strictly required by PHP, it is generally a good practice to add $sugarObjects = array(); before regardless.
Loading history...
522
        if (isset($config['objects']) && !empty($config['objects'])) {
523
            $sugarObjects = $config['objects'];
524
        }
525
526
        /** @var IntegrationEntityRepository $integrationEntityRepo */
527
        $integrationEntityRepo = $this->em->getRepository('MauticPluginBundle:IntegrationEntity');
528
        $startDate             = new \DateTime($query['start']);
529
        $endDate               = new \DateTime($query['end']);
530
        $limit                 = 100;
531
532
        foreach ($sugarObjects as $object) {
533
            try {
534
                if ($this->isAuthorized()) {
535
                    // Get first batch
536
                    $start    = 0;
537
                    $sugarIds = $integrationEntityRepo->getIntegrationsEntityId(
538
                        'Sugarcrm',
539
                        $object,
540
                        'lead',
541
                        null,
542
                        $startDate->format('Y-m-d H:i:s'),
543
                        $endDate->format('Y-m-d H:i:s'),
544
                        true,
545
                        $start,
546
                        $limit
547
                    );
548
549
                    while (!empty($sugarIds)) {
550
                        $executed += count($sugarIds);
551
552
                        // Extract a list of lead Ids
553
                        $leadIds = [];
554
                        foreach ($sugarIds as $ids) {
555
                            $leadIds[] = $ids['internal_entity_id'];
556
                        }
557
558
                        // Collect lead activity for this batch
559
                        $leadActivity = $this->getLeadData(
560
                            $startDate,
561
                            $endDate,
562
                            $leadIds
563
                        );
564
565
                        $sugarLeadData = [];
566
                        foreach ($sugarIds as $ids) {
567
                            $leadId = $ids['internal_entity_id'];
568
                            if (isset($leadActivity[$leadId])) {
569
                                $sugarId                            = $ids['integration_entity_id'];
570
                                $sugarLeadData[$sugarId]            = $leadActivity[$leadId];
571
                                $sugarLeadData[$sugarId]['id']      = $ids['integration_entity_id'];
572
                                $sugarLeadData[$sugarId]['leadId']  = $ids['internal_entity_id'];
573
                                $sugarLeadData[$sugarId]['leadUrl'] = $this->router->generate(
574
                                    'mautic_plugin_timeline_view',
575
                                    ['integration' => 'Sugarcrm', 'leadId' => $leadId],
576
                                    UrlGeneratorInterface::ABSOLUTE_URL
577
                                );
578
                            }
579
                        }
580
581
                        if (!empty($sugarLeadData)) {
582
                            $apiHelper->createLeadActivity($sugarLeadData, $object);
583
                        }
584
585
                        // Get the next batch
586
                        $start += $limit;
587
                        $sugarIds = $integrationEntityRepo->getIntegrationsEntityId(
588
                            'Sugarcrm',
589
                            $object,
590
                            'lead',
591
                            null,
592
                            $startDate->format('Y-m-d H:i:s'),
593
                            $endDate->format('Y-m-d H:i:s'),
594
                            true,
595
                            $start,
596
                            $limit
597
                        );
598
                    }
599
                }
600
            } catch (\Exception $e) {
601
                $this->logIntegrationError($e);
602
            }
603
        }
604
605
        return $executed;
606
    }
607
608
    /**
609
     * @param array      $params
610
     * @param array|null $query
611
     *
612
     * @return int|null
613
     */
614
    public function getLeads($params = [], $query = null, &$executed = null, $result = [], $object = 'Leads')
615
    {
616
        $params['max_results'] = 100;
617
        $config                = $this->mergeConfigToFeatureSettings([]);
0 ignored issues
show
The assignment to $config is dead and can be removed.
Loading history...
618
619
        if (!isset($params['offset'])) {
620
            //First call
621
            $params['offset'] = 0;
622
        }
623
        $query = $params;
624
625
        try {
626
            if ($this->isAuthorized()) {
627
                if ('Activity' !== $object and 'company' !== $object) {
628
                    $result           = $this->getApiHelper()->getLeads($query, $object);
629
                    $params['offset'] = $result['next_offset'];
630
                    $executed += $this->amendLeadDataBeforeMauticPopulate($result, $object);
631
                    if (
632
                        (isset($result['total_count']) && $result['total_count'] > $params['offset'])   //Sugar 6
633
                        || (!isset($result['total_count']) && $params['offset'] > -1)) {            //Sugar 7
634
                        $params['object'] = $object;
635
                        $executed += $this->getLeads($params, null, $executed, [], $object);
636
                    }
637
                }
638
639
                return $executed;
640
            }
641
        } catch (\Exception $e) {
642
            $this->logIntegrationError($e);
643
        }
644
645
        return $executed;
646
    }
647
648
    /**
649
     * @param $response
650
     *
651
     * @return string
652
     */
653
    public function getErrorsFromResponse($response)
654
    {
655
        if ('6' == $this->keys['version']) {
656
            if (!empty($response['name'])) {
657
                return $response['description'];
658
            } else {
659
                return $this->translator->trans('mautic.integration.error.genericerror', [], 'flashes');
660
            }
661
        } else {
662
            return parent::getErrorsFromResponse($response);
663
        }
664
    }
665
666
    /**
667
     * {@inheritdoc}
668
     *
669
     * @return string
670
     */
671
    public function getAuthenticationType()
672
    {
673
        return (isset($this->keys['version']) && '6' == $this->keys['version']) ? 'rest' : 'oauth2';
674
    }
675
676
    /**
677
     * {@inheritdoc}
678
     *
679
     * @return bool
680
     */
681
    public function getDataPriority()
682
    {
683
        return true;
684
    }
685
686
    /**
687
     * @param $url
688
     * @param $parameters
689
     * @param $method
690
     * @param $settings
691
     * @param $authType
692
     *
693
     * @return array
694
     */
695
    public function prepareRequest($url, $parameters, $method, $settings, $authType)
696
    {
697
        if ('oauth2' == $authType && empty($settings['authorize_session']) && isset($this->keys['access_token'])) {
698
            // Append the access token as the oauth-token header
699
            $headers = [
700
                "oauth-token: {$this->keys['access_token']}",
701
            ];
702
703
            return [$parameters, $headers];
704
        } else {
705
            return parent::prepareRequest($url, $parameters, $method, $settings, $authType);
706
        }
707
    }
708
709
    /**
710
     * {@inheritdoc}
711
     *
712
     * @return bool
713
     */
714
    public function isAuthorized()
715
    {
716
        if (!$this->isConfigured()) {
717
            return false;
718
        }
719
720
        if (!isset($this->keys['version'])) {
721
            return false;
722
        }
723
724
        if ('6' == $this->keys['version']) {
725
            $loginParams = [
726
                'user_auth' => [
727
                    'user_name' => $this->keys['username'],
728
                    'password'  => md5($this->keys['password']),
729
                    'version'   => '1',
730
                ],
731
                'application_name' => 'Mautic',
732
                'name_value_list'  => [],
733
                'method'           => 'login',
734
                'input_type'       => 'JSON',
735
                'response_type'    => 'JSON',
736
            ];
737
            $parameters = [
738
                'method'        => 'login',
739
                'input_type'    => 'JSON',
740
                'response_type' => 'JSON',
741
                'rest_data'     => json_encode($loginParams),
742
            ];
743
744
            $settings['auth_type']         = 'rest';
745
            $settings['authorize_session'] = true;
746
747
            $response = $this->makeRequest($this->getAccessTokenUrl(), $parameters, 'GET', $settings);
748
749
            unset($response['module'], $response['name_value_list']);
750
            $error = $this->extractAuthKeys($response, 'id');
751
752
            $this->authorizationError = $error;
753
754
            return empty($error);
755
        } else {
756
            if ($this->isConfigured()) {
757
                // SugarCRM 7 uses password grant type so login each time to ensure session is valid
758
                $this->authCallback();
759
            }
760
761
            return parent::isAuthorized();
762
        }
763
    }
764
765
    /**
766
     * {@inheritdoc}
767
     *
768
     * @param $data
769
     */
770
    public function prepareResponseForExtraction($data)
771
    {
772
        // Extract expiry and set expires for 7.x
773
        if (is_array($data) && isset($data['expires_in'])) {
774
            $data['expires'] = $data['expires_in'] + time();
775
        }
776
777
        return $data;
778
    }
779
780
    /**
781
     * Amend mapped lead data before creating to Mautic.
782
     *
783
     * @param array  $data
784
     * @param string $object
785
     *
786
     * @return int
787
     */
788
    public function amendLeadDataBeforeMauticPopulate($data, $object)
789
    {
790
        $settings['feature_settings']['objects'][] = $object;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$settings was never initialized. Although not strictly required by PHP, it is generally a good practice to add $settings = array(); before regardless.
Loading history...
791
        $fields                                    = array_keys($this->getAvailableLeadFields($settings));
792
        $params['fields']                          = implode(',', $fields);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
793
794
        $count  = 0;
795
        $entity = null;
796
797
        /** @var IntegrationEntityRepository $integrationEntityRepo */
798
        $integrationEntityRepo = $this->em->getRepository('MauticPluginBundle:IntegrationEntity');
799
        $companyRepo           = $this->em->getRepository('MauticLeadBundle:Company');
800
801
        $sugarRejectedLeads = [];
802
        if (isset($data['entry_list'])) {
803
            $SUGAR_VERSION     = '6';
804
            $RECORDS_LIST_NAME = 'entry_list';
805
            $MODULE_FIELD_NAME = 'module_name';
806
        }
807
        if (isset($data['records'])) {
808
            $SUGAR_VERSION     = '7';
809
            $RECORDS_LIST_NAME = 'records';
810
            $MODULE_FIELD_NAME = '_module';
811
        }
812
813
        if (isset($data[$RECORDS_LIST_NAME]) and 'Activity' !== $object) {
814
            //Get assigned user ids
815
            $assignedUserIds            = [];
816
            $onwerEmailByAssignedUserId = [];
817
            if ('Leads' == $object || 'Contacts' == $object || 'Accounts' == $object) {
818
                foreach ($data[$RECORDS_LIST_NAME] as $record) {
819
                    if ('6' == $SUGAR_VERSION) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $SUGAR_VERSION does not seem to be defined for all execution paths leading up to this point.
Loading history...
820
                        foreach ($record['name_value_list'] as $item) {
821
                            if ('assigned_user_id' == $item['name'] && $item['value'] && '' != $item['value']) {
822
                                $assignedUserIds[] = $item['value'];
823
                            }
824
                        }
825
                    } else {
826
                        if (isset($record['assigned_user_id']) && '' != $record['assigned_user_id']) {
827
                            $assignedUserIds[] = $record['assigned_user_id'];
828
                        }
829
                    }
830
                }
831
            }
832
            if (!empty($assignedUserIds)) {
833
                $assignedUserIds            = array_unique($assignedUserIds);
834
                $onwerEmailByAssignedUserId = $this->getApiHelper()->getEmailBySugarUserId(['ids' => $assignedUserIds]);
0 ignored issues
show
The method getEmailBySugarUserId() does not exist on MauticPlugin\MauticCrmBundle\Api\CrmApi. It seems like you code against a sub-type of MauticPlugin\MauticCrmBundle\Api\CrmApi such as MauticPlugin\MauticCrmBundle\Api\SugarcrmApi. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

834
                $onwerEmailByAssignedUserId = $this->getApiHelper()->/** @scrutinizer ignore-call */ getEmailBySugarUserId(['ids' => $assignedUserIds]);
Loading history...
835
            }
836
837
            //Get all leads emails
838
            $checkEmailsInSugar = [];
839
            if ('Leads' == $object) {
840
                if ('6' == $SUGAR_VERSION) {
841
                    foreach ($data[$RECORDS_LIST_NAME] as $record) {
842
                        foreach ($record['name_value_list'] as $item) {
843
                            if ('email1' == $item['name'] && $item['value'] && '' != $item['value']) {
844
                                $checkEmailsInSugar[] = $item['value'];
845
                            }
846
                        }
847
                    }
848
                } else {
849
                    if (isset($record['email1']) && '' != $record['email1']) {
850
                        $checkEmailsInSugar[] = $record['email1'];
851
                    }
852
                }
853
            }
854
            if (!empty($checkEmailsInSugar)) {
855
                $sugarLeads = $this->getApiHelper()->getLeads(['checkemail_contacts' => $checkEmailsInSugar, 'offset' => 0, 'max_results' => 1000], 'Contacts');
856
                if (isset($sugarLeads[$RECORDS_LIST_NAME])) {
857
                    foreach ($sugarLeads[$RECORDS_LIST_NAME] as $record) {
858
                        $sugarLeadRecord = [];
0 ignored issues
show
The assignment to $sugarLeadRecord is dead and can be removed.
Loading history...
859
                        if ('6' == $SUGAR_VERSION) {
860
                            foreach ($record['name_value_list'] as $item) {
861
                                if ('email1' == $item['name'] && $item['value'] && '' != $item['value']) {
862
                                    $sugarRejectedLeads[] = $item['value'];
863
                                }
864
                            }
865
                        } else {
866
                            if (isset($record['email1']) && '' != $record['email1']) {
867
                                $sugarRejectedLeads[] = $record['email1'];
868
                            }
869
                        }
870
                    }
871
                }
872
            }
873
874
            foreach ($data[$RECORDS_LIST_NAME] as $record) {
875
                $integrationEntities = [];
876
                $dataObject          = [];
877
                if (isset($record[$MODULE_FIELD_NAME]) && 'Accounts' == $record[$MODULE_FIELD_NAME]) {
878
                    $newName = '';
879
                } else {
880
                    $newName = '__'.$object;
881
                }
882
                if ('6' == $SUGAR_VERSION) {
883
                    foreach ($record['name_value_list'] as $item) {
884
                        if ('Activity' !== $object) {
885
                            if ($this->checkIfSugarCrmMultiSelectString($item['value'])) {
886
                                $convertedMultiSelectString         = $this->convertSuiteCrmToMauticMultiSelect($item['value']);
887
                                $dataObject[$item['name'].$newName] = $convertedMultiSelectString;
888
                            } else {
889
                                $dataObject[$item['name'].$newName] = $item['value'];
890
                            }
891
                            if ('date_entered' == $item['name']) {
892
                                $itemDateEntered = new \DateTime($item['value']);
0 ignored issues
show
The assignment to $itemDateEntered is dead and can be removed.
Loading history...
893
                            }
894
                            if ('date_modified' == $item['name']) {
895
                                $itemDateModified = new \DateTime($item['value']);
0 ignored issues
show
The assignment to $itemDateModified is dead and can be removed.
Loading history...
896
                            }
897
                        }
898
                    }
899
                } else {
900
                    if ('Activity' !== $object) {
901
                        if (isset($record['date_entered']) && '' != $record['date_entered']) {
902
                            $itemDateEntered = new \DateTime($record['date_entered']);
903
                        }
904
                        if (isset($record['date_modified']) && '' != $record['date_modified']) {
905
                            $itemDateEntered = new \DateTime($record['date_modified']);
906
                        }
907
                        foreach ($record as $k => $item) {
908
                            $dataObject[$k.$newName] = $item;
909
                        }
910
                    }
911
                }
912
                if ('Leads' == $object && isset($dataObject['email1__Leads']) && null != $dataObject['email1__Leads']
913
                    && '' != $dataObject['email1__Leads'] && in_array($dataObject['email1__Leads'], $sugarRejectedLeads)) {
914
                    continue; //Lead email is already in Sugar Contacts. Do not carry on
915
                }
916
917
                if (!empty($dataObject)) {
918
                    if ('Leads' == $object or 'Contacts' == $object) {
919
                        if (isset($dataObject['assigned_user_id'.'__'.$object])) {
920
                            $auid = $dataObject['assigned_user_id'.'__'.$object];
921
                            if (isset($onwerEmailByAssignedUserId[$auid])) {
922
                                $dataObject['owner_email'] = $onwerEmailByAssignedUserId[$auid];
923
                            }
924
                        }
925
                        $mauticObjectReference = 'lead';
926
                        $entity                = $this->getMauticLead($dataObject, true, null, null, $object);
927
                        $detachClass           = Lead::class;
928
                        $company               = null;
929
                        $this->fetchDncToMautic($entity, $data);
930
                        if ($entity && isset($dataObject['account_id'.$newName]) && '' != trim($dataObject['account_id'.$newName])) {
931
                            $integrationCompanyEntity = $integrationEntityRepo->findOneBy(
932
                                [
933
                                    'integration'         => 'Sugarcrm',
934
                                    'integrationEntity'   => 'Accounts',
935
                                    'internalEntity'      => 'company',
936
                                    'integrationEntityId' => $dataObject['account_id'.$newName],
937
                                ]
938
                            );
939
                            if (isset($integrationCompanyEntity)) {
940
                                $companyId = $integrationCompanyEntity->getInternalEntityId();
941
                                $company   = $companyRepo->find($companyId);
942
943
                                $this->companyModel->addLeadToCompany($company, $entity);
944
                                $this->em->clear(Company::class);
945
                                $this->em->detach($entity);
946
                            }
947
                        }
948
                    } elseif ('Accounts' === $object) {
949
                        $entity                = $this->getMauticCompany($dataObject, $object);
950
                        $detachClass           = Company::class;
951
                        $mauticObjectReference = 'company';
952
                    } else {
953
                        $this->logIntegrationError(
954
                            new \Exception(
955
                                sprintf('Received an unexpected object without an internalObjectReference "%s"', $object)
956
                            )
957
                        );
958
959
                        continue;
960
                    }
961
962
                    if ($entity) {
963
                        $integrationId = $integrationEntityRepo->getIntegrationsEntityId(
964
                            'Sugarcrm',
965
                            $object,
966
                            $mauticObjectReference,
967
                            $entity->getId()
968
                        );
969
970
                        if (null == $integrationId) {
971
                            $integrationEntity = new IntegrationEntity();
972
                            $integrationEntity->setDateAdded(new \DateTime());
973
                            $integrationEntity->setLastSyncDate(new \DateTime());
974
                            $integrationEntity->setIntegration('Sugarcrm');
975
                            $integrationEntity->setIntegrationEntity($object);
976
                            $integrationEntity->setIntegrationEntityId($record['id']);
977
                            $integrationEntity->setInternalEntity($mauticObjectReference);
978
                            $integrationEntity->setInternalEntityId($entity->getId());
979
                            $integrationEntities[] = $integrationEntity;
980
                        } else {
981
                            $integrationEntity = $integrationEntityRepo->getEntity($integrationId[0]['id']);
982
                            $integrationEntity->setLastSyncDate(new \DateTime());
983
                            $integrationEntities[] = $integrationEntity;
984
                        }
985
                        $this->em->detach($entity);
0 ignored issues
show
Deprecated Code introduced by
The function Doctrine\ORM\EntityManager::detach() has been deprecated: 2.7 This method is being removed from the ORM and won't have any replacement ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

985
                        /** @scrutinizer ignore-deprecated */ $this->em->detach($entity);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
986
                        $this->em->clear($detachClass);
987
                        unset($entity);
988
                    } else {
989
                        continue;
990
                    }
991
                    ++$count;
992
                }
993
994
                $this->em->getRepository('MauticPluginBundle:IntegrationEntity')->saveEntities($integrationEntities);
995
                $this->em->clear('Mautic\PluginBundle\Entity\IntegrationEntity');
996
            }
997
            unset($data);
998
            unset($integrationEntities);
999
            unset($dataObject);
1000
        }
1001
1002
        return $count;
1003
    }
1004
1005
    /**
1006
     * @param \Mautic\PluginBundle\Integration\Form|FormBuilder $builder
1007
     * @param array                                             $data
1008
     * @param string                                            $formArea
1009
     */
1010
    public function appendToForm(&$builder, $data, $formArea)
1011
    {
1012
        if ('keys' == $formArea) {
1013
            $builder->add('version', ButtonGroupType::class, [
1014
                'choices' => [
1015
                    '6.x/community' => '6',
1016
                    '7.x'           => '7',
1017
                ],
1018
                'label'             => 'mautic.sugarcrm.form.version',
1019
                'constraints'       => [
1020
                    new NotBlank([
1021
                        'message' => 'mautic.core.value.required',
1022
                    ]),
1023
                ],
1024
                'required' => true,
1025
            ]);
1026
        }
1027
        if ('features' == $formArea) {
1028
            $builder->add(
1029
                'updateOwner',
1030
                ChoiceType::class,
1031
                [
1032
                    'choices' => [
1033
                        'mautic.sugarcrm.updateOwner' => 'updateOwner',
1034
                    ],
1035
                    'expanded'          => true,
1036
                    'multiple'          => true,
1037
                    'label'             => 'mautic.sugarcrm.form.updateOwner',
1038
                    'label_attr'        => ['class' => 'control-label'],
1039
                    'placeholder'       => false,
1040
                    'required'          => false,
1041
                    'attr'              => [
1042
                        'onclick' => 'Mautic.postForm(mQuery(\'form[name="integration_details"]\'),\'\');',
1043
                    ],
1044
                ]
1045
            );
1046
1047
            $builder->add(
1048
                'updateDnc',
1049
                ChoiceType::class,
1050
                [
1051
                    'choices' => [
1052
                        'mautic.sugarcrm.updateDnc' => 'updateDnc',
1053
                    ],
1054
                    'expanded'          => true,
1055
                    'multiple'          => true,
1056
                    'label'             => 'mautic.sugarcrm.form.updateDnc',
1057
                    'label_attr'        => ['class' => 'control-label'],
1058
                    'placeholder'       => false,
1059
                    'required'          => false,
1060
                    'attr'              => [
1061
                        'onclick' => 'Mautic.postForm(mQuery(\'form[name="integration_details"]\'),\'\');',
1062
                    ],
1063
                ]
1064
            );
1065
1066
            $builder->add(
1067
                'updateBlanks',
1068
                ChoiceType::class,
1069
                [
1070
                    'choices' => [
1071
                        'mautic.integrations.blanks' => 'updateBlanks',
1072
                    ],
1073
                    'expanded'          => true,
1074
                    'multiple'          => true,
1075
                    'label'             => 'mautic.integrations.form.blanks',
1076
                    'label_attr'        => ['class' => 'control-label'],
1077
                    'placeholder'       => false,
1078
                    'required'          => false,
1079
                ]
1080
            );
1081
1082
            $builder->add(
1083
                'objects',
1084
                ChoiceType::class,
1085
                [
1086
                    'choices' => [
1087
                        'mautic.sugarcrm.object.lead'    => 'Leads',
1088
                        'mautic.sugarcrm.object.contact' => 'Contacts',
1089
                        'mautic.sugarcrm.object.company' => 'company',
1090
                    ],
1091
                    'expanded'          => true,
1092
                    'multiple'          => true,
1093
                    'label'             => 'mautic.sugarcrm.form.objects_to_pull_from',
1094
                    'label_attr'        => ['class' => ''],
1095
                    'placeholder'       => false,
1096
                    'required'          => false,
1097
                ]
1098
            );
1099
1100
            $builder->add(
1101
                'activityEvents',
1102
                ChoiceType::class,
1103
                [
1104
                    'choices'           => array_flip($this->leadModel->getEngagementTypes()), // Choice type expects labels as keys
1105
                    'label'             => 'mautic.salesforce.form.activity_included_events',
1106
                    'label_attr'        => [
1107
                        'class'       => 'control-label',
1108
                        'data-toggle' => 'tooltip',
1109
                        'title'       => $this->translator->trans('mautic.salesforce.form.activity.events.tooltip'),
1110
                    ],
1111
                    'multiple'   => true,
1112
                    'empty_data' => ['point.gained', 'form.submitted', 'email.read'], // BC with pre 2.11.0
1113
                    'required'   => false,
1114
                ]
1115
            );
1116
        }
1117
    }
1118
1119
    /**
1120
     * @param \Mautic\LeadBundle\Entity\Lead $lead
1121
     * @param array                          $config
1122
     *
1123
     * @return array|bool
1124
     */
1125
    public function pushLead($lead, $config = [])
1126
    {
1127
        $config = $this->mergeConfigToFeatureSettings($config);
1128
1129
        if (empty($config['leadFields'])) {
1130
            return [];
1131
        }
1132
1133
        $object = 'Leads'; //Sugar objects, default is Leads
1134
1135
        //Check if lead has alredy been synched
1136
        /** @var IntegrationEntityRepository $integrationEntityRepo */
1137
        $integrationEntityRepo = $this->em->getRepository('MauticPluginBundle:IntegrationEntity');
1138
        //Check if it is a sugar CRM alredy synched lead
1139
        $integrationId = $integrationEntityRepo->getIntegrationsEntityId('Sugarcrm', $object, 'lead', $lead->getId());
1140
        if (empty($integrationId)) {
1141
            //Check if it is a sugar CRM alredy synched lead
1142
            $integrationId = $integrationEntityRepo->getIntegrationsEntityId('Sugarcrm', 'Contacts', 'lead', $lead->getId());
1143
            if (!empty($integrationId)) {
1144
                $object = 'Contacts';
1145
            }
1146
        }
1147
        if (!empty($integrationId)) {
1148
            $integrationEntity = $integrationEntityRepo->getEntity($integrationId[0]['id']);
1149
            $lastSyncDate      = $integrationEntity->getLastSyncDate();
1150
            $addedSyncDate     = $integrationEntity->getDateAdded();
1151
            if ($addedSyncDate > $lastSyncDate) {
1152
                $lastSyncDate = $addedSyncDate;
1153
            }
1154
1155
            $leadDateModified = $lead->getDateModified();
1156
            $leadDateAdded    = $lead->getDateAdded();
1157
            $leadLastDate     = $leadDateModified;
1158
            if ($leadDateAdded > $leadDateModified) {
1159
                $leadLastDate = $leadDateAdded;
1160
            }
1161
1162
            if ($lastSyncDate >= $leadLastDate) {
1163
                return false;
1164
            } //Do not push lead if it was already synched
1165
        }
1166
1167
        $fieldsToUpdateInSugar      = isset($config['update_mautic']) ? array_keys($config['update_mautic'], 0) : [];
1168
        $leadSugarFieldsToCreate    = $this->cleanSugarData($config, array_keys($config['leadFields']), $object);
1169
        $fieldsToUpdateInLeadsSugar = $this->cleanSugarData($config, $fieldsToUpdateInSugar, $object);
1170
        $leadFields                 = array_intersect_key($leadSugarFieldsToCreate, $fieldsToUpdateInLeadsSugar);
1171
1172
        $mappedData[$object] = $this->populateLeadData($lead, ['leadFields' => $leadFields, 'object' => $object]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
$mappedData was never initialized. Although not strictly required by PHP, it is generally a good practice to add $mappedData = array(); before regardless.
Loading history...
1173
1174
        $this->amendLeadDataBeforePush($mappedData[$object]);
1175
1176
        if (empty($mappedData[$object])) {
1177
            return false;
1178
        }
1179
1180
        if (!empty($integrationId)) {
1181
            $integrationEntity = $integrationEntityRepo->findOneBy(
1182
                [
1183
                    'integration'       => 'Sugarcrm',
1184
                    'integrationEntity' => $object,
1185
                    'internalEntity'    => 'lead',
1186
                    'internalEntityId'  => $lead->getId(),
1187
                ]
1188
            );
1189
1190
            $mappedData[$object]['id'] = $integrationEntity->getIntegrationEntityId();
1191
        }
1192
        try {
1193
            if ($this->isAuthorized()) {
1194
                if (!is_null($lead->getOwner())) {
1195
                    $sugarOwnerId = $this->getApiHelper()->getIdBySugarEmail(['emails' => [$lead->getOwner()->getEmail()]]);
1196
                    if (!empty($sugarOwnerId)) {
1197
                        $mappedData[$object]['assigned_user_id'] = array_values($sugarOwnerId)[0];
1198
                    }
1199
                }
1200
                $createdLeadData = $this->getApiHelper()->createLead($mappedData[$object], $lead);
0 ignored issues
show
The call to MauticPlugin\MauticCrmBu...pi\CrmApi::createLead() has too many arguments starting with $mappedData[$object]. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1200
                $createdLeadData = $this->getApiHelper()->/** @scrutinizer ignore-call */ createLead($mappedData[$object], $lead);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
1201
                if (isset($createdLeadData['id'])) {
1202
                    if (empty($integrationId)) {
1203
                        $integrationEntity = new IntegrationEntity();
1204
                        $integrationEntity->setDateAdded(new \DateTime());
1205
                        $integrationEntity->setLastSyncDate(new \DateTime());
1206
                        $integrationEntity->setIntegration('Sugarcrm');
1207
                        $integrationEntity->setIntegrationEntity($object);
1208
                        $integrationEntity->setIntegrationEntityId($createdLeadData['id']);
1209
                        $integrationEntity->setInternalEntity('lead');
1210
                        $integrationEntity->setInternalEntityId($lead->getId());
1211
                    } else {
1212
                        $integrationEntity = $integrationEntityRepo->getEntity($integrationId[0]['id']);
1213
                    }
1214
                    $integrationEntity->setLastSyncDate(new \DateTime());
1215
                    $this->em->persist($integrationEntity);
1216
                    $this->em->flush($integrationEntity);
1217
                }
1218
1219
                return true;
1220
            }
1221
        } catch (\Exception $e) {
1222
            $this->logIntegrationError($e);
1223
        }
1224
1225
        return false;
1226
    }
1227
1228
    /**
1229
     * Return key recognized by integration.
1230
     *
1231
     * @param $key
1232
     * @param $field
1233
     *
1234
     * @return mixed
1235
     */
1236
    public function convertLeadFieldKey($key, $field)
1237
    {
1238
        $search = [];
1239
        foreach ($this->objects as $object) {
1240
            $search[] = '__'.$object;
1241
        }
1242
1243
        return str_replace($search, '', $key);
1244
    }
1245
1246
    /**
1247
     * @param array  $fields
1248
     * @param array  $keys
1249
     * @param string $object
1250
     *
1251
     * @return array
1252
     */
1253
    public function cleanSugarData($fields, $keys, $object)
1254
    {
1255
        $leadFields = [];
1256
1257
        foreach ($keys as $key) {
1258
            if (strstr($key, '__'.$object)) {
1259
                $newKey = str_replace('__'.$object, '', $key);
1260
                //$leadFields[$object][$newKey] = $fields['leadFields'][$key];
1261
                $leadFields[$newKey] = $fields['leadFields'][$key];
1262
            }
1263
        }
1264
1265
        return $leadFields;
1266
    }
1267
1268
    /**
1269
     * @param array $params
1270
     *
1271
     * @return mixed
1272
     */
1273
    public function pushLeads($params = [])
1274
    {
1275
        list($fromDate, $toDate) = $this->getSyncTimeframeDates($params);
1276
        $limit                   = $params['limit'];
1277
        $config                  = $this->mergeConfigToFeatureSettings();
1278
        $integrationEntityRepo   = $this->em->getRepository('MauticPluginBundle:IntegrationEntity');
1279
        $mauticData              = $leadsToUpdate              = $fields              = [];
1280
        $fieldsToUpdateInSugar   = isset($config['update_mautic']) ? array_keys($config['update_mautic'], 0) : [];
1281
        $leadFields              = $config['leadFields'];
1282
        if (!empty($leadFields)) {
1283
            if ($keys = array_keys($leadFields, 'mauticContactTimelineLink')) {
1284
                foreach ($keys as $key) {
1285
                    unset($leadFields[$key]);
1286
                }
1287
            }
1288
1289
            if ($keys = array_keys($leadFields, 'mauticContactIsContactableByEmail')) {
1290
                foreach ($keys as $key) {
1291
                    unset($leadFields[$key]);
1292
                }
1293
            }
1294
1295
            $fields = implode(', l.', $leadFields);
1296
            $fields = 'l.owner_id,l.'.$fields;
1297
            $result = 0;
1298
1299
            //Leads fields
1300
            $leadSugarFieldsToCreate    = $this->cleanSugarData($config, array_keys($config['leadFields']), 'Leads');
1301
            $fieldsToUpdateInLeadsSugar = $this->cleanSugarData($config, $fieldsToUpdateInSugar, 'Leads');
1302
            $leadSugarFields            = array_intersect_key($leadSugarFieldsToCreate, $fieldsToUpdateInLeadsSugar);
1303
1304
            //Contacts fields
1305
            $contactSugarFields            = $this->cleanSugarData($config, array_keys($config['leadFields']), 'Contacts');
1306
            $fieldsToUpdateInContactsSugar = $this->cleanSugarData($config, $fieldsToUpdateInSugar, 'Contacts');
1307
            $contactSugarFields            = array_intersect_key($contactSugarFields, $fieldsToUpdateInContactsSugar);
1308
1309
            $availableFields = $this->getAvailableLeadFields(['feature_settings' => ['objects' => ['Leads', 'Contacts']]]);
1310
1311
            //update lead/contact records
1312
            $leadsToUpdate = $integrationEntityRepo->findLeadsToUpdate($this->getName(), 'lead', $fields, $limit, $fromDate, $toDate, ['Contacts', 'Leads']);
1313
        }
1314
        $checkEmailsInSugar = [];
1315
        $deletedSugarLeads  = [];
1316
        foreach ($leadsToUpdate as $object => $records) {
1317
            foreach ($records as $lead) {
1318
                if (isset($lead['email']) && !empty($lead['email'])) {
1319
                    $lead                                                       = $this->getCompoundMauticFields($lead);
1320
                    $checkEmailsInSugar[$object][mb_strtolower($lead['email'])] = $lead;
1321
                }
1322
            }
1323
        }
1324
        // Only get the max limit
1325
        if ($limit) {
1326
            $limit -= count($leadsToUpdate);
1327
        }
1328
1329
        //create lead records
1330
        if (null === $limit || $limit && !empty($fields)) {
1331
            $leadsToCreate = $integrationEntityRepo->findLeadsToCreate('Sugarcrm', $fields, $limit, $fromDate, $toDate);
1332
            foreach ($leadsToCreate as $lead) {
1333
                if (isset($lead['email'])) {
1334
                    $lead                                                       = $this->getCompoundMauticFields($lead);
1335
                    $checkEmailsInSugar['Leads'][mb_strtolower($lead['email'])] = $lead;
1336
                }
1337
            }
1338
        }
1339
1340
        foreach ($checkEmailsInSugar as $object => $checkObjectEmailsInSugar) {
1341
            list($checkEmailsUpdatedInSugar, $deletedRedords) = $this->getObjectDataToUpdate($checkObjectEmailsInSugar, $mauticData, $availableFields, $contactSugarFields, $leadSugarFields, $object);
1342
            //recheck synced records that might have been deleted in Sugar (deleted records don't come back in the query)
1343
            foreach ($checkEmailsUpdatedInSugar as $key => $deletedSugarRedords) {
1344
                if (isset($deletedSugarRedords['integration_entity_id']) && !empty($deletedSugarRedords['integration_entity_id'])) {
1345
                    $deletedSugarLeads[$key] = $deletedSugarRedords['integration_entity_id'];
1346
                }
1347
                unset($checkEmailsUpdatedInSugar[$key]);
1348
            }
1349
        }
1350
1351
        if (!empty($checkEmailsUpdatedInSugar)) {
1352
            $checkEmailsInSugar = array_merge($checkEmailsUpdatedInSugar, $checkEmailsInSugar);
1353
        }
1354
        // If there are any deleted, mark it as so to prevent them from being queried over and over or recreated
1355
        if ($deletedSugarLeads) {
1356
            $integrationEntityRepo->markAsDeleted($deletedSugarLeads, $this->getName(), 'lead');
1357
        }
1358
1359
        // Create any left over
1360
        if ($checkEmailsInSugar && isset($checkEmailsInSugar['Leads'])) {
1361
            list($checkEmailsInSugar, $deletedSugarLeads) = $this->getObjectDataToUpdate($checkEmailsInSugar['Leads'], $mauticData, $availableFields, $contactSugarFields, $leadSugarFields, 'Leads');
1362
            $ownerAssignedUserIdByEmail                   = null;
1363
            foreach ($checkEmailsInSugar as $lead) {
1364
                if (isset($lead['email'])) {
1365
                    $lead['owner_email'] = $this->getOwnerEmail($lead);
1366
                    if ($lead['owner_email']) {
1367
                        $ownerAssignedUserIdByEmail = $this->getApiHelper()->getIdBySugarEmail(['emails' => [$lead['owner_email']]]);
1368
                    }
1369
                    $this->buildCompositeBody(
1370
                        $mauticData,
1371
                        $availableFields,
1372
                        $leadSugarFieldsToCreate, //use all matched fields when creating new records in Sugar
1373
                        'Leads',
1374
                        $lead,
1375
                        $ownerAssignedUserIdByEmail
1376
                    );
1377
                }
1378
            }
1379
        }
1380
        /** @var SugarcrmApi $apiHelper */
1381
        $apiHelper = $this->getApiHelper();
1382
        if (!empty($mauticData)) {
1383
            $result = $apiHelper->syncLeadsToSugar($mauticData);
1384
        }
1385
1386
        return $this->processCompositeResponse($result);
1387
    }
1388
1389
    /**
1390
     * Update body to sync.
1391
     */
1392
    private function pushDncToSugar(array $lead, array &$body)
1393
    {
1394
        $features = $this->settings->getFeatureSettings();
1395
        // update DNC sync disabled
1396
        if (empty($features['updateDnc'])) {
1397
            return;
1398
        }
1399
        $leadEntity = $this->leadModel->getEntity($lead['internal_entity_id']);
1400
        /** @var \Mautic\LeadBundle\Entity\DoNotContact[] $dncEntries */
1401
        $dncEntries   = $this->doNotContactModel->getDncRepo()->getEntriesByLeadAndChannel($leadEntity, 'email');
1402
        $sugarDncKeys = array_combine(array_values($this->sugarDncKeys), $this->sugarDncKeys);
1403
        foreach ($dncEntries as $dncEntry) {
1404
            if (empty($sugarDncKeys)) {
1405
                continue;
1406
            }
1407
            // If DNC exists set to 1
1408
            switch ($dncEntry->getReason()) {
1409
                case 1:
1410
                case 3:
1411
                    $body[] = ['name' => 'email_opt_out', 'value' => 1];
1412
                    unset($sugarDncKeys['email_opt_out']);
1413
                    break;
1414
                case 2:
1415
                    $body[] = ['name' => 'invalid_email', 'value' => 1];
1416
                    unset($sugarDncKeys['invalid_email']);
1417
                    break;
1418
            }
1419
        }
1420
1421
        // uncheck
1422
        // If DNC doesn't exist set to 1
1423
        if (!empty($sugarDncKeys)) {
1424
            foreach ($sugarDncKeys as $sugarDncKey) {
1425
                $body[] = ['name' => $sugarDncKey, 'value' => 0];
1426
            }
1427
        }
1428
    }
1429
1430
    private function fetchDncToMautic(Lead $lead = null, array $data)
1431
    {
1432
        if (is_null($lead)) {
1433
            return;
1434
        }
1435
1436
        $features = $this->settings->getFeatureSettings();
1437
        if (empty($features['updateDnc'])) {
1438
            return;
1439
        }
1440
1441
        // try find opt_out value for lead
1442
        $isContactable = true;
1443
        foreach ($data['relationship_list'] as $relationshipList) {
1444
            foreach ($relationshipList['link_list'] as $links) {
1445
                if ('email_addresses' == $links['name']) {
1446
                    foreach ($links['records'] as $records) {
1447
                        if (!empty($records['link_value']['email_address']['value']) && $records['link_value']['email_address']['value'] == $lead->getEmail() && !empty($records['link_value']['opt_out']['value'])) {
1448
                            $isContactable = false;
1449
                            break 3;
1450
                        }
1451
                    }
1452
                }
1453
            }
1454
        }
1455
1456
        $reason = \Mautic\LeadBundle\Entity\DoNotContact::UNSUBSCRIBED;
1457
        if (!$isContactable) {
1458
            $this->doNotContactModel->addDncForContact($lead->getId(), 'email', $reason, $this->getName());
1459
        } else {
1460
            $this->doNotContactModel->removeDncForContact($lead->getId(), 'email', true, $reason);
1461
        }
1462
    }
1463
1464
    /**
1465
     * @param $checkEmailsInSugar
1466
     * @param $mauticData
1467
     * @param $availableFields
1468
     * @param $contactSugarFields
1469
     * @param $leadSugarFields
1470
     * @param string $object
1471
     *
1472
     * @return array The first element is made up of records that exist in Mautic, but which no longer have a match in CRM.
1473
     *               We therefore assume that they've been deleted in CRM and will mark them as deleted in the pushLeads function (~line 1320).
1474
     *               The second element contains Ids of records that were explicitly marked as deleted in CRM. ATM, nothing is done with this data.
1475
     */
1476
    public function getObjectDataToUpdate($checkEmailsInSugar, &$mauticData, $availableFields, $contactSugarFields, $leadSugarFields, $object = 'Leads')
1477
    {
1478
        $config     = $this->mergeConfigToFeatureSettings([]);
1479
        $queryParam = ('Leads' == $object) ? 'checkemail' : 'checkemail_contacts';
1480
1481
        $sugarLead         = $this->getApiHelper()->getLeads([$queryParam => array_keys($checkEmailsInSugar), 'offset' => 0, 'max_results' => 1000], $object);
1482
        $deletedSugarLeads = $sugarLeadRecords = [];
1483
1484
        if (isset($sugarLead['entry_list'])) {
1485
            //Sugar 6.X
1486
            $sugarLeadRecords = [];
1487
            foreach ($sugarLead['entry_list'] as $k => $record) {
1488
                $sugarLeadRecord                = [];
1489
                $sugarLeadRecord['id']          = $record['id'];
1490
                $sugarLeadRecord['module_name'] = $record['module_name'];
1491
                foreach ($record['name_value_list'] as $item) {
1492
                    $sugarLeadRecord[$item['name']] = $item['value'];
1493
                }
1494
                if (!isset($sugarLeadRecord['email1'])) {
1495
                    foreach ($sugarLead['relationship_list'][$k]['link_list'] as $links) {
1496
                        if ('email_addresses' == $links['name']) {
1497
                            foreach ($links['records'] as $records) {
1498
                                foreach ($records as $contactEmails) {
1499
                                    foreach ($contactEmails as $anyAddress) {
1500
                                        if ('email_address' == $anyAddress['name'] && !empty($anyAddress['value'])) {
1501
                                            $sugarLeadRecord['email1'] = $anyAddress['value'];
1502
                                            break;
1503
                                        }
1504
                                    }
1505
                                }
1506
                            }
1507
                        }
1508
                    }
1509
                }
1510
                $sugarLeadRecords[] = $sugarLeadRecord;
1511
            }
1512
        } elseif (isset($sugarLead['records'])) { //Sugar 7
1513
            $sugarLeadRecords = $sugarLead['records'];
1514
        }
1515
1516
        foreach ($sugarLeadRecords as $sugarLeadRecord) {
1517
            if ((isset($sugarLeadRecord) && $sugarLeadRecord)) {
1518
                $email           = $sugarLeadRecord['email1'];
1519
                $key             = mb_strtolower($email);
1520
                $leadOwnerEmails = [];
1521
                if (!empty($checkEmailsInSugar)) {
1522
                    foreach ($checkEmailsInSugar as $emailKey => $mauticRecord) {
1523
                        if ($key == $emailKey) {
1524
                            $isConverted = (isset($sugarLeadRecord['contact_id'])
1525
                                && null != $sugarLeadRecord['contact_id']
1526
                                && '' != $sugarLeadRecord['contact_id']);
1527
1528
                            $sugarIdMapping[$checkEmailsInSugar[$key]['internal_entity_id']] = ($isConverted) ? $sugarLeadRecord['contact_id'] : $sugarLeadRecord['id'];
1529
                            $lead['owner_email']                                             = $this->getOwnerEmail($mauticRecord);
1530
                            if ($lead['owner_email']) {
1531
                                $leadOwnerEmails[] = $lead['owner_email'];
1532
                            }
1533
                            $ownerAssignedUserIdByEmail = $this->getApiHelper()->getIdBySugarEmail(['emails' => array_unique($leadOwnerEmails)]);
1534
                            if (empty($sugarLeadRecord['deleted']) || 0 == $sugarLeadRecord['deleted']) {
1535
                                $sugarFieldMappings = $this->prepareFieldsForPush($availableFields);
1536
1537
                                if (isset($sugarFieldMappings['Contacts']) && !empty($sugarFieldMappings['Contacts'])) {
1538
                                    $contactSugarFields = $this->getBlankFieldsToUpdate($contactSugarFields, $sugarLeadRecord, $sugarFieldMappings['Contacts'], $config);
1539
                                }
1540
                                if (isset($sugarFieldMappings['Leads']) && !empty($sugarFieldMappings['Leads'])) {
1541
                                    $leadSugarFields = $this->getBlankFieldsToUpdate($leadSugarFields, $sugarLeadRecord, $sugarFieldMappings['Leads'], $config);
1542
                                }
1543
                                $this->buildCompositeBody(
1544
                                    $mauticData,
1545
                                    $availableFields,
1546
                                    $isConverted || ('Contacts' == $object) ? $contactSugarFields : $leadSugarFields,
1547
                                    $isConverted || ('Contacts' == $object) ? 'Contacts' : 'Leads',
1548
                                    $checkEmailsInSugar[$key],
1549
                                    $ownerAssignedUserIdByEmail,
1550
                                    $isConverted ? $sugarLeadRecord['contact_id'] : $sugarLeadRecord['id']
1551
                                );
1552
                            } else {
1553
                                // @todo - Should return also deleted contacts from Sugar
1554
                                $deletedSugarLeads[] = $sugarLeadRecord['id'];
1555
                                if (!empty($sugarLeadRecord['contact_id']) || '' != $sugarLeadRecord['contact_id']) {
1556
                                    $deletedSugarLeads[] = $sugarLeadRecord['contact_id'];
1557
                                }
1558
                            }
1559
                            unset($checkEmailsInSugar[$key]);
1560
                        }
1561
                    }
1562
                }
1563
            }
1564
        }
1565
1566
        return [$checkEmailsInSugar, $deletedSugarLeads];
1567
    }
1568
1569
    /**
1570
     * @param $lead
1571
     *
1572
     * @return array
1573
     */
1574
    public function getSugarLeadId($lead)
1575
    {
1576
        /** @var IntegrationEntityRepository $integrationEntityRepo */
1577
        $integrationEntityRepo = $this->em->getRepository('MauticPluginBundle:IntegrationEntity');
1578
        //try searching for lead as this has been changed before in updated done to the plugin
1579
        $result = $integrationEntityRepo->getIntegrationsEntityId('Sugarcrm', null, 'lead', $lead->getId());
1580
1581
        return $result;
1582
    }
1583
1584
    /**
1585
     * @param array $lead
1586
     */
1587
    protected function getOwnerEmail($lead)
1588
    {
1589
        if (isset($lead['owner_id']) && !empty($lead['owner_id'])) {
1590
            /** @var \Mautic\UserBundle\Entity\User $user */
1591
            $user = $this->userModel->getEntity($lead['owner_id']);
1592
1593
            return $user->getEmail();
1594
        }
1595
1596
        return null;
1597
    }
1598
1599
    /**
1600
     * @param      $mauticData
1601
     * @param      $availableFields
1602
     * @param      $object
1603
     * @param      $lead
1604
     * @param null $objectId
1605
     */
1606
    protected function buildCompositeBody(&$mauticData, $availableFields, $fieldsToUpdateInSugarUpdate, $object, $lead, $onwerAssignedUserIdByEmail = null, $objectId = null)
1607
    {
1608
        $body = [];
1609
        if (isset($lead['email']) && !empty($lead['email'])) {
1610
            //update and create (one query) every 200 records
1611
1612
            foreach ($fieldsToUpdateInSugarUpdate as $sugarField => $mauticField) {
1613
                $required = !empty($availableFields[$object][$sugarField.'__'.$object]['required']);
1614
                if (isset($lead[$mauticField])) {
1615
                    if (false !== strpos($lead[$mauticField], '|')) {
1616
                        // Transform Mautic Multi Select into SugarCRM/SuiteCRM Multi Select format
1617
                        $value = $this->convertMauticToSuiteCrmMultiSelect($lead[$mauticField]);
1618
                    } else {
1619
                        $value = $lead[$mauticField];
1620
                    }
1621
                    $body[] = ['name' => $sugarField, 'value' =>  $value];
1622
                } elseif ($required) {
1623
                    $value  = $this->translator->trans('mautic.integration.form.lead.unknown');
1624
                    $body[] = ['name' => $sugarField, 'value' => $value];
1625
                }
1626
            }
1627
1628
            if (!empty($body)) {
1629
                $id = $lead['internal_entity_id'].'-'.$object.(!empty($lead['id']) ? '-'.$lead['id'] : '');
1630
1631
                $body[] = ['name' => 'reference_id', 'value' => $id];
1632
1633
                if ($objectId) {
0 ignored issues
show
$objectId is of type null, thus it always evaluated to false.
Loading history...
1634
                    $body[] = ['name' => 'id', 'value' => $objectId];
1635
                }
1636
                if (isset($onwerAssignedUserIdByEmail) && isset($lead['owner_email']) && isset($onwerAssignedUserIdByEmail[$lead['owner_email']])) {
1637
                    $body[] = ['name' => 'assigned_user_id', 'value' => $onwerAssignedUserIdByEmail[$lead['owner_email']]];
1638
                }
1639
1640
                // pushd DNC to Sugar CRM
1641
                $this->pushDncToSugar($lead, $body);
1642
1643
                $mauticData[$object][] = $body;
1644
            }
1645
        }
1646
    }
1647
1648
    /**
1649
     * @param array $response
1650
     *
1651
     * @return array
1652
     */
1653
    protected function processCompositeResponse($response)
1654
    {
1655
        $created         = 0;
1656
        $errored         = 0;
1657
        $updated         = 0;
1658
        $object          = 'Lead';
1659
        $persistEntities = [];
1660
        if (is_array($response)) {
0 ignored issues
show
The condition is_array($response) is always true.
Loading history...
1661
            foreach ($response as $item) {
1662
                $contactId = $integrationEntityId = null;
1663
                if (!empty($item['reference_id'])) {
1664
                    $reference = explode('-', $item['reference_id']);
1665
                    if (3 === count($reference)) {
1666
                        list($contactId, $object, $integrationEntityId) = $reference;
1667
                    } else {
1668
                        list($contactId, $object) = $reference;
1669
                    }
1670
                }
1671
1672
                if (isset($item['ko']) && $item['ko']) {
1673
                    $this->logIntegrationError(new \Exception($item['error']));
1674
1675
                    if ($integrationEntityId) {
1676
                        $integrationEntity = $this->em->getReference('MauticPluginBundle:IntegrationEntity', $integrationEntityId);
1677
                        $integrationEntity->setLastSyncDate(new \DateTime());
1678
1679
                        $persistEntities[] = $integrationEntity;
1680
                    } elseif ($contactId) {
1681
                        $integrationEntity = new IntegrationEntity();
1682
                        $integrationEntity->setDateAdded(new \DateTime());
1683
                        $integrationEntity->setLastSyncDate(new \DateTime());
1684
                        $integrationEntity->setIntegration($this->getName());
1685
                        $integrationEntity->setIntegrationEntity($object);
1686
                        $integrationEntity->setInternalEntity('lead-error');
1687
                        $integrationEntity->setInternal(['error' => $item['error']]);
1688
                        $integrationEntity->setInternalEntityId($contactId);
1689
1690
                        $persistEntities[] = $integrationEntity;
1691
                        ++$errored;
1692
                    }
1693
                } elseif (!$item['ko']) {
1694
                    if ($item['new']) {
1695
                        // New object created
1696
                        $integrationEntity = new IntegrationEntity();
1697
                        $integrationEntity->setDateAdded(new \DateTime());
1698
                        $integrationEntity->setLastSyncDate(new \DateTime());
1699
                        $integrationEntity->setIntegration($this->getName());
1700
                        $integrationEntity->setIntegrationEntity($object);
1701
                        $integrationEntity->setIntegrationEntityId($item['id']);
1702
                        $integrationEntity->setInternalEntity('lead');
1703
                        $integrationEntity->setInternalEntityId($contactId);
1704
1705
                        $persistEntities[] = $integrationEntity;
1706
                        ++$created;
1707
                    } else {
1708
                        // Record was updated
1709
                        if ($integrationEntityId) {
1710
                            $integrationEntity = $this->em->getReference('MauticPluginBundle:IntegrationEntity', $integrationEntityId);
1711
                            $integrationEntity->setLastSyncDate(new \DateTime());
1712
                        } else {
1713
                            // Found in Sugarcrm so create a new record for it
1714
                            $integrationEntity = new IntegrationEntity();
1715
                            $integrationEntity->setDateAdded(new \DateTime());
1716
                            $integrationEntity->setLastSyncDate(new \DateTime());
1717
                            $integrationEntity->setIntegration($this->getName());
1718
                            $integrationEntity->setIntegrationEntity($object);
1719
                            $integrationEntity->setIntegrationEntityId($item['id']);
1720
                            $integrationEntity->setInternalEntity('lead');
1721
                            $integrationEntity->setInternalEntityId($contactId);
1722
                        }
1723
1724
                        $persistEntities[] = $integrationEntity;
1725
                        ++$updated;
1726
                    }
1727
                } else {
1728
                    $error = 'Unknown status code '.$item['httpStatusCode'];
1729
                    $this->logIntegrationError(new \Exception($error.' ('.$item['reference_id'].')'));
1730
                }
1731
            }
1732
1733
            if ($persistEntities) {
1734
                $this->em->getRepository('MauticPluginBundle:IntegrationEntity')->saveEntities($persistEntities);
1735
                unset($persistEntities);
1736
                $this->em->clear(IntegrationEntity::class);
1737
            }
1738
        }
1739
1740
        return [$updated, $created, $errored];
1741
    }
1742
1743
    /**
1744
     * @param       $fieldsToUpdate
1745
     * @param array $objects
1746
     *
1747
     * @return array
1748
     */
1749
    protected function cleanPriorityFields($fieldsToUpdate, $objects = null)
1750
    {
1751
        if (null === $objects) {
1752
            $objects = ['Leads', 'Contacts'];
1753
        }
1754
1755
        if (isset($fieldsToUpdate['leadFields'])) {
1756
            // Pass in the whole config
1757
            $fields = $fieldsToUpdate;
1758
        } else {
1759
            $fields = array_flip($fieldsToUpdate);
1760
        }
1761
1762
        return $this->prepareFieldsForSync($fields, $fieldsToUpdate, $objects);
1763
    }
1764
1765
    /**
1766
     * @param array $fields
1767
     * @param array $keys
1768
     * @param mixed $object
1769
     *
1770
     * @return array
1771
     */
1772
    public function prepareFieldsForSync($fields, $keys, $object = null)
1773
    {
1774
        $leadFields = [];
1775
        if (null === $object) {
1776
            $object = 'Lead';
1777
        }
1778
1779
        $objects = (!is_array($object)) ? [$object] : $object;
1780
        if (is_string($object) && 'Accounts' === $object) {
1781
            return isset($fields['companyFields']) ? $fields['companyFields'] : $fields;
1782
        }
1783
1784
        if (isset($fields['leadFields'])) {
1785
            $fields = $fields['leadFields'];
1786
            $keys   = array_keys($fields);
1787
        }
1788
1789
        foreach ($objects as $obj) {
1790
            if (!isset($leadFields[$obj])) {
1791
                $leadFields[$obj] = [];
1792
            }
1793
1794
            foreach ($keys as $key) {
1795
                if (strpos($key, '__'.$obj)) {
1796
                    $newKey = str_replace('__'.$obj, '', $key);
1797
                    if ('Id' === $newKey) {
1798
                        // Don't map Id for push
1799
                        continue;
1800
                    }
1801
1802
                    $leadFields[$obj][$newKey] = $fields[$key];
1803
                }
1804
            }
1805
        }
1806
1807
        return (is_array($object)) ? $leadFields : $leadFields[$object];
1808
    }
1809
1810
    /**
1811
     * @param        $config
1812
     * @param null   $object
1813
     * @param string $priorityObject
1814
     *
1815
     * @return mixed
1816
     */
1817
    protected function getPriorityFieldsForMautic($config, $object = null, $priorityObject = 'mautic')
1818
    {
1819
        $fields = parent::getPriorityFieldsForMautic($config, $object, $priorityObject);
1820
1821
        return ($object && isset($fields[$object])) ? $fields[$object] : $fields;
1822
    }
1823
1824
    /**
1825
     * @param $fields
1826
     *
1827
     * @return array
1828
     */
1829
    protected function prepareFieldsForPush($fields)
1830
    {
1831
        $fieldMappings = [];
1832
        $required      = [];
1833
        $config        = $this->mergeConfigToFeatureSettings();
1834
1835
        $contactFields = $this->cleanSugarData($config, array_keys($config['leadFields']), 'Contacts');
1836
        $leadFields    = $this->cleanSugarData($config, array_keys($config['leadFields']), 'Leads');
1837
        if (!empty($contactFields)) {
1838
            foreach ($fields['Contacts'] as $key => $field) {
1839
                if ($field['required']) {
1840
                    $required[$key] = $field;
1841
                }
1842
            }
1843
            $fieldMappings['Contacts']['required'] = [
1844
                'fields' => $required,
1845
            ];
1846
            $fieldMappings['Contacts']['create'] = $contactFields;
1847
        }
1848
        if (!empty($leadFields)) {
1849
            foreach ($fields['Leads'] as $key => $field) {
1850
                if ($field['required']) {
1851
                    $required[$key] = $field;
1852
                }
1853
            }
1854
            $fieldMappings['Leads']['required'] = [
1855
                'fields' => $required,
1856
            ];
1857
            $fieldMappings['Leads']['create'] = $leadFields;
1858
        }
1859
1860
        return $fieldMappings;
1861
    }
1862
1863
    /**
1864
     * Converts Mautic Multi-Select String into the format used to store Multi-Select values used by SuiteCRM / SugarCRM 6.x.
1865
     *
1866
     * @param  string
1867
     *
1868
     * @return string
1869
     */
1870
    public function convertMauticToSuiteCrmMultiSelect($mauticMultiSelectStringToConvert)
1871
    {
1872
        //$mauticMultiSelectStringToConvert = 'test|enhancedapi|dataservices';
1873
        $multiSelectArrayValues             = explode('|', $mauticMultiSelectStringToConvert);
1874
        $convertedSugarCrmMultiSelectString = '';
1875
        foreach ($multiSelectArrayValues as $item) {
1876
            $convertedSugarCrmMultiSelectString = $convertedSugarCrmMultiSelectString.'^'.$item.'^'.',';
1877
        }
1878
1879
        return substr($convertedSugarCrmMultiSelectString, 0, -1);
1880
    }
1881
1882
    /**
1883
     * Checks if a string contains SuiteCRM / SugarCRM 6.x Multi-Select values.
1884
     *
1885
     * @param  string
1886
     *
1887
     * @return bool
1888
     */
1889
    public function checkIfSugarCrmMultiSelectString($stringToCheck)
1890
    {
1891
        // Regular Express to check SugarCRM/SuiteCRM Multi-Select format below
1892
        // example format: '^choice1^,^choice2^,^choice_3^'
1893
        $regex = '/(\^)(?:([A-Za-z0-9\-\_]+))(\^)/';
1894
        if (preg_match($regex, $stringToCheck)) {
1895
            return true;
1896
        } else {
1897
            return false;
1898
        }
1899
    }
1900
1901
    /**
1902
     * Converts a SuiteCRM / SugarCRM 6.x Multi-Select String into the format used to store Multi-Select values used by Mautic.
1903
     *
1904
     * @param  string
1905
     *
1906
     * @return string
1907
     */
1908
    public function convertSuiteCrmToMauticMultiSelect($suiteCrmMultiSelectStringToConvert)
1909
    {
1910
        // Mautic Multi Select format - 'choice1|choice2|choice_3'
1911
        $regexString            = '/(\^)(?:([A-Za-z0-9\-\_]+))(\^)/';
1912
        preg_match_all($regexString, $suiteCrmMultiSelectStringToConvert, $matches, PREG_SET_ORDER, 0);
1913
        $convertedString        = '';
1914
        foreach ($matches as $innerArray) {
1915
            $convertedString     = $convertedString.$innerArray[2].'|';
1916
        }
1917
1918
        return substr($convertedString, 0, -1);
1919
    }
1920
}
1921