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 Mautic\FormBundle\Entity\Form; |
||||
15 | use Mautic\LeadBundle\Entity\Company; |
||||
16 | use Mautic\LeadBundle\Entity\Lead; |
||||
17 | use Mautic\LeadBundle\Helper\IdentifyCompanyHelper; |
||||
18 | use Mautic\PluginBundle\Entity\IntegrationEntity; |
||||
19 | use Mautic\PluginBundle\Entity\IntegrationEntityRepository; |
||||
20 | use Mautic\PluginBundle\Exception\ApiErrorException; |
||||
21 | use MauticPlugin\MauticCrmBundle\Api\Zoho\Mapper; |
||||
22 | use MauticPlugin\MauticCrmBundle\Api\ZohoApi; |
||||
23 | use Symfony\Component\Console\Helper\ProgressBar; |
||||
24 | use Symfony\Component\Console\Output\ConsoleOutput; |
||||
25 | use Symfony\Component\Console\Output\OutputInterface; |
||||
26 | use Symfony\Component\Form\Extension\Core\Type\ChoiceType; |
||||
27 | use Symfony\Component\Form\FormBuilder; |
||||
28 | |||||
29 | /** |
||||
30 | * @method ZohoApi getApiHelper |
||||
31 | */ |
||||
32 | class ZohoIntegration extends CrmAbstractIntegration |
||||
33 | { |
||||
34 | /** |
||||
35 | * Returns the name of the social integration that must match the name of the file. |
||||
36 | * |
||||
37 | * @return string |
||||
38 | */ |
||||
39 | public function getName() |
||||
40 | { |
||||
41 | return 'Zoho'; |
||||
42 | } |
||||
43 | |||||
44 | /** |
||||
45 | * {@inheritdoc} |
||||
46 | * |
||||
47 | * @return string |
||||
48 | */ |
||||
49 | public function getAuthenticationType() |
||||
50 | { |
||||
51 | return 'oauth2'; |
||||
52 | } |
||||
53 | |||||
54 | /** |
||||
55 | * {@inheritdoc} |
||||
56 | * |
||||
57 | * @return array |
||||
58 | */ |
||||
59 | public function getRequiredKeyFields() |
||||
60 | { |
||||
61 | return [ |
||||
62 | $this->getClientIdKey() => 'mautic.zoho.form.client_id', |
||||
63 | $this->getClientSecretKey() => 'mautic.zoho.form.client_secret', |
||||
64 | ]; |
||||
65 | } |
||||
66 | |||||
67 | /** |
||||
68 | * @return string |
||||
69 | */ |
||||
70 | public function getClientIdKey() |
||||
71 | { |
||||
72 | return 'client_id'; |
||||
73 | } |
||||
74 | |||||
75 | /** |
||||
76 | * @return string |
||||
77 | */ |
||||
78 | public function getClientSecretKey() |
||||
79 | { |
||||
80 | return 'client_secret'; |
||||
81 | } |
||||
82 | |||||
83 | /** |
||||
84 | * @return string |
||||
85 | */ |
||||
86 | public function getAuthTokenKey() |
||||
87 | { |
||||
88 | return 'access_token'; |
||||
89 | } |
||||
90 | |||||
91 | /** |
||||
92 | * @return string |
||||
93 | */ |
||||
94 | public function getAuthScope() |
||||
95 | { |
||||
96 | return 'ZohoCRM.modules.ALL,ZohoCRM.settings.ALL,ZohoCRM.bulk.all,ZohoCRM.users.all,ZohoCRM.org.all'; |
||||
97 | } |
||||
98 | |||||
99 | /** |
||||
100 | * @return string |
||||
101 | */ |
||||
102 | public function getDatacenter() |
||||
103 | { |
||||
104 | $featureSettings = $this->getKeys(); |
||||
105 | |||||
106 | return !empty($featureSettings['datacenter']) ? $featureSettings['datacenter'] : 'zoho.com'; |
||||
107 | } |
||||
108 | |||||
109 | /** |
||||
110 | * @return string |
||||
111 | */ |
||||
112 | public function getApiUrl() |
||||
113 | { |
||||
114 | return sprintf('https://accounts.%s', $this->getDatacenter()); |
||||
115 | } |
||||
116 | |||||
117 | /** |
||||
118 | * {@inheritdoc} |
||||
119 | * |
||||
120 | * @return string |
||||
121 | */ |
||||
122 | public function getAccessTokenUrl() |
||||
123 | { |
||||
124 | return $this->getApiUrl().'/oauth/v2/token'; |
||||
125 | } |
||||
126 | |||||
127 | /** |
||||
128 | * {@inheritdoc} |
||||
129 | * |
||||
130 | * @return string |
||||
131 | */ |
||||
132 | public function getAuthenticationUrl() |
||||
133 | { |
||||
134 | return $this->getApiUrl().'/oauth/v2/auth'; |
||||
135 | } |
||||
136 | |||||
137 | /** |
||||
138 | * @return array |
||||
139 | */ |
||||
140 | public function getSupportedFeatures() |
||||
141 | { |
||||
142 | return ['push_lead', 'get_leads', 'push_leads']; |
||||
143 | } |
||||
144 | |||||
145 | /** |
||||
146 | * Refresh tokens. |
||||
147 | */ |
||||
148 | public function getRefreshTokenKeys() |
||||
149 | { |
||||
150 | return [ |
||||
151 | 'refresh_token', |
||||
152 | 'expires', |
||||
153 | ]; |
||||
154 | } |
||||
155 | |||||
156 | /** |
||||
157 | * {@inheritdoc} |
||||
158 | * |
||||
159 | * @param $data |
||||
160 | */ |
||||
161 | public function prepareResponseForExtraction($data) |
||||
162 | { |
||||
163 | // Extract expiry and set expires for zoho |
||||
164 | if (is_array($data) && isset($data['expires_in'])) { |
||||
165 | $data['expires'] = $data['expires_in'] + time(); |
||||
166 | } |
||||
167 | |||||
168 | return $data; |
||||
169 | } |
||||
170 | |||||
171 | /** |
||||
172 | * Amend mapped lead data before creating to Mautic. |
||||
173 | * |
||||
174 | * @param array $data |
||||
175 | * @param string $object |
||||
176 | * |
||||
177 | * @return array |
||||
178 | */ |
||||
179 | public function amendLeadDataBeforeMauticPopulate($data, $object = null) |
||||
180 | { |
||||
181 | if ('company' === $object) { |
||||
182 | $object = 'Accounts'; |
||||
183 | } elseif ('Lead' === $object || 'Contact' === $object) { |
||||
184 | $object .= 's'; // pluralize object name for Zoho |
||||
185 | } |
||||
186 | |||||
187 | $config = $this->mergeConfigToFeatureSettings([]); |
||||
188 | |||||
189 | $result = []; |
||||
190 | if (isset($data['data'])) { |
||||
191 | $entity = null; |
||||
192 | /** @var IntegrationEntityRepository $integrationEntityRepo */ |
||||
193 | $integrationEntityRepo = $this->em->getRepository('MauticPluginBundle:IntegrationEntity'); |
||||
194 | $objects = $data['data']; |
||||
195 | /** @var array $rows */ |
||||
196 | // foreach ($rows as $row) { |
||||
197 | $integrationEntities = []; |
||||
198 | /** @var array $objects */ |
||||
199 | foreach ($objects as $recordId => $entityData) { |
||||
200 | $isModified = false; |
||||
201 | if ('Accounts' === $object) { |
||||
202 | $recordId = $entityData['id']; |
||||
203 | // first try to find integration entity |
||||
204 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||||
205 | 'Zoho', |
||||
206 | $object, |
||||
207 | 'company', |
||||
208 | null, |
||||
209 | null, |
||||
210 | null, |
||||
211 | false, |
||||
212 | 0, |
||||
213 | 0, |
||||
214 | [$recordId] |
||||
215 | ); |
||||
216 | if (count($integrationId)) { // company exists, then update local fields |
||||
217 | /** @var Company $entity */ |
||||
218 | $entity = $this->companyModel->getEntity($integrationId[0]['internal_entity_id']); |
||||
219 | $matchedFields = $this->populateMauticLeadData($entityData, $config, 'company'); |
||||
220 | |||||
221 | // Match that data with mapped lead fields |
||||
222 | $fieldsToUpdateInMautic = $this->getPriorityFieldsForMautic($config, $object, 'mautic_company'); |
||||
223 | if (!empty($fieldsToUpdateInMautic)) { |
||||
224 | $fieldsToUpdateInMautic = array_intersect_key($config['companyFields'], $fieldsToUpdateInMautic); |
||||
225 | $newMatchedFields = array_intersect_key($matchedFields, array_flip($fieldsToUpdateInMautic)); |
||||
226 | } else { |
||||
227 | $newMatchedFields = $matchedFields; |
||||
228 | } |
||||
229 | if (!isset($newMatchedFields['companyname'])) { |
||||
230 | if (isset($newMatchedFields['companywebsite'])) { |
||||
231 | $newMatchedFields['companyname'] = $newMatchedFields['companywebsite']; |
||||
232 | } |
||||
233 | } |
||||
234 | |||||
235 | // update values if already empty |
||||
236 | foreach ($matchedFields as $field => $value) { |
||||
237 | if (empty($entity->getFieldValue($field))) { |
||||
238 | $newMatchedFields[$field] = $value; |
||||
239 | } |
||||
240 | } |
||||
241 | |||||
242 | // remove unchanged fields |
||||
243 | foreach ($newMatchedFields as $k => $v) { |
||||
244 | if ($entity->getFieldValue($k) === $v) { |
||||
245 | unset($newMatchedFields[$k]); |
||||
246 | } |
||||
247 | } |
||||
248 | |||||
249 | if (count($newMatchedFields)) { |
||||
250 | $this->companyModel->setFieldValues($entity, $newMatchedFields, false); |
||||
251 | $this->companyModel->saveEntity($entity, false); |
||||
252 | $isModified = true; |
||||
253 | } |
||||
254 | } else { |
||||
255 | $entity = $this->getMauticCompany($entityData, 'Accounts'); |
||||
256 | } |
||||
257 | if ($entity) { |
||||
258 | $result[] = $entity->getName(); |
||||
259 | } |
||||
260 | $mauticObjectReference = 'company'; |
||||
261 | } elseif ('Leads' === $object) { |
||||
262 | $recordId = $entityData['id']; |
||||
263 | // first try to find integration entity |
||||
264 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||||
265 | 'Zoho', |
||||
266 | $object, |
||||
267 | 'lead', |
||||
268 | null, |
||||
269 | null, |
||||
270 | null, |
||||
271 | false, |
||||
272 | 0, |
||||
273 | 0, |
||||
274 | [$recordId] |
||||
275 | ); |
||||
276 | |||||
277 | if (count($integrationId)) { // lead exists, then update |
||||
278 | /** @var Lead $entity */ |
||||
279 | $entity = $this->leadModel->getEntity($integrationId[0]['internal_entity_id']); |
||||
280 | $matchedFields = $this->populateMauticLeadData($entityData, $config, $object); |
||||
281 | |||||
282 | // Match that data with mapped lead fields |
||||
283 | $fieldsToUpdateInMautic = $this->getPriorityFieldsForMautic($config, $object, 'mautic'); |
||||
284 | |||||
285 | if (!empty($fieldsToUpdateInMautic)) { |
||||
286 | $fieldsToUpdateInMautic = array_intersect_key($config['leadFields'], $fieldsToUpdateInMautic); |
||||
287 | $newMatchedFields = array_intersect_key($matchedFields, array_flip($fieldsToUpdateInMautic)); |
||||
288 | } else { |
||||
289 | $newMatchedFields = $matchedFields; |
||||
290 | } |
||||
291 | |||||
292 | // update values if already empty |
||||
293 | foreach ($matchedFields as $field => $value) { |
||||
294 | if (empty($entity->getFieldValue($field))) { |
||||
295 | $newMatchedFields[$field] = $value; |
||||
296 | } |
||||
297 | } |
||||
298 | // remove unchanged fields |
||||
299 | foreach ($newMatchedFields as $k => $v) { |
||||
300 | if ($entity->getFieldValue($k) === $v) { |
||||
301 | unset($newMatchedFields[$k]); |
||||
302 | } |
||||
303 | } |
||||
304 | if (count($newMatchedFields)) { |
||||
305 | $this->leadModel->setFieldValues($entity, $newMatchedFields, false, false); |
||||
306 | $this->leadModel->saveEntity($entity, false); |
||||
307 | $isModified = true; |
||||
308 | } |
||||
309 | } else { |
||||
310 | /** @var Lead $entity */ |
||||
311 | $entity = $this->getMauticLead($entityData, true, null, null, $object); |
||||
312 | } |
||||
313 | |||||
314 | if ($entity) { |
||||
315 | $result[] = $entity->getEmail(); |
||||
316 | } |
||||
317 | |||||
318 | // Associate lead company |
||||
319 | if (!empty($entityData['Company']) |
||||
320 | && $entityData['Company'] !== $this->translator->trans( |
||||
321 | 'mautic.integration.form.lead.unknown' |
||||
322 | ) |
||||
323 | ) { |
||||
324 | $company = IdentifyCompanyHelper::identifyLeadsCompany( |
||||
325 | ['company' => $entityData['Company']], |
||||
326 | null, |
||||
327 | $this->companyModel |
||||
328 | ); |
||||
329 | |||||
330 | if (!empty($company[2])) { |
||||
331 | $syncLead = $this->companyModel->addLeadToCompany($company[2], $entity); |
||||
332 | $this->em->detach($company[2]); |
||||
0 ignored issues
–
show
|
|||||
333 | } |
||||
334 | } |
||||
335 | |||||
336 | $mauticObjectReference = 'lead'; |
||||
337 | } elseif ('Contacts' === $object) { |
||||
338 | $recordId = $entityData['id']; |
||||
339 | |||||
340 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||||
341 | 'Zoho', |
||||
342 | $object, |
||||
343 | 'lead', |
||||
344 | null, |
||||
345 | null, |
||||
346 | null, |
||||
347 | false, |
||||
348 | 0, |
||||
349 | 0, |
||||
350 | [$recordId] |
||||
351 | ); |
||||
352 | if (count($integrationId)) { // contact exists, then update |
||||
353 | /** @var Lead $entity */ |
||||
354 | $entity = $this->leadModel->getEntity($integrationId[0]['internal_entity_id']); |
||||
355 | $matchedFields = $this->populateMauticLeadData($entityData, $config, $object); |
||||
356 | |||||
357 | // Match that data with mapped lead fields |
||||
358 | $fieldsToUpdateInMautic = $this->getPriorityFieldsForMautic($config, $object, 'mautic'); |
||||
359 | if (!empty($fieldsToUpdateInMautic)) { |
||||
360 | $fieldsToUpdateInMautic = array_intersect_key($config['leadFields'], $fieldsToUpdateInMautic); |
||||
361 | $newMatchedFields = array_intersect_key($matchedFields, array_flip($fieldsToUpdateInMautic)); |
||||
362 | } else { |
||||
363 | $newMatchedFields = $matchedFields; |
||||
364 | } |
||||
365 | |||||
366 | // update values if already empty |
||||
367 | foreach ($matchedFields as $field => $value) { |
||||
368 | if (empty($entity->getFieldValue($field))) { |
||||
369 | $newMatchedFields[$field] = $value; |
||||
370 | } |
||||
371 | } |
||||
372 | |||||
373 | // remove unchanged fields |
||||
374 | foreach ($newMatchedFields as $k => $v) { |
||||
375 | if ($entity->getFieldValue($k) === $v) { |
||||
376 | unset($newMatchedFields[$k]); |
||||
377 | } |
||||
378 | } |
||||
379 | |||||
380 | if (count($newMatchedFields)) { |
||||
381 | $this->leadModel->setFieldValues($entity, $newMatchedFields, false, false); |
||||
382 | $this->leadModel->saveEntity($entity, false); |
||||
383 | $isModified = true; |
||||
384 | } |
||||
385 | } else { |
||||
386 | /** @var Lead $entity */ |
||||
387 | $entity = $this->getMauticLead($entityData, true, null, null, $object); |
||||
388 | } |
||||
389 | |||||
390 | if ($entity) { |
||||
391 | $result[] = $entity->getEmail(); |
||||
392 | |||||
393 | // Associate lead company |
||||
394 | if (!empty($entityData['AccountName']) |
||||
395 | && $entityData['AccountName'] !== $this->translator->trans( |
||||
396 | 'mautic.integration.form.lead.unknown' |
||||
397 | ) |
||||
398 | ) { |
||||
399 | $company = IdentifyCompanyHelper::identifyLeadsCompany( |
||||
400 | ['company' => $entityData['AccountName']], |
||||
401 | null, |
||||
402 | $this->companyModel |
||||
403 | ); |
||||
404 | |||||
405 | if (!empty($company[2])) { |
||||
406 | $syncLead = $this->companyModel->addLeadToCompany($company[2], $entity); |
||||
407 | $this->em->detach($company[2]); |
||||
0 ignored issues
–
show
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
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...
|
|||||
408 | } |
||||
409 | } |
||||
410 | } |
||||
411 | |||||
412 | $mauticObjectReference = 'lead'; |
||||
413 | } else { |
||||
414 | $this->logIntegrationError( |
||||
415 | new \Exception( |
||||
416 | sprintf('Received an unexpected object "%s"', $object) |
||||
417 | ) |
||||
418 | ); |
||||
419 | continue; |
||||
420 | } |
||||
421 | |||||
422 | if ($entity) { |
||||
423 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||||
424 | 'Zoho', |
||||
425 | $object, |
||||
426 | $mauticObjectReference, |
||||
427 | $entity->getId() |
||||
428 | ); |
||||
429 | |||||
430 | if (0 === count($integrationId)) { |
||||
431 | $integrationEntity = new IntegrationEntity(); |
||||
432 | $integrationEntity->setDateAdded(new \DateTime()); |
||||
433 | $integrationEntity->setIntegration('Zoho'); |
||||
434 | $integrationEntity->setIntegrationEntity($object); |
||||
435 | $integrationEntity->setIntegrationEntityId($recordId); |
||||
436 | $integrationEntity->setInternalEntity($mauticObjectReference); |
||||
437 | $integrationEntity->setInternalEntityId($entity->getId()); |
||||
438 | $integrationEntities[] = $integrationEntity; |
||||
439 | } else { |
||||
440 | $integrationEntity = $integrationEntityRepo->getEntity($integrationId[0]['id']); |
||||
441 | if ($isModified) { |
||||
442 | $integrationEntity->setLastSyncDate(new \DateTime()); |
||||
443 | $integrationEntities[] = $integrationEntity; |
||||
444 | } |
||||
445 | } |
||||
446 | $this->em->detach($entity); |
||||
447 | unset($entity); |
||||
448 | } else { |
||||
449 | continue; |
||||
450 | } |
||||
451 | } |
||||
452 | |||||
453 | $this->em->getRepository('MauticPluginBundle:IntegrationEntity')->saveEntities($integrationEntities); |
||||
454 | $this->em->clear('Mautic\PluginBundle\Entity\IntegrationEntity'); |
||||
455 | //} |
||||
456 | unset($integrationEntities); |
||||
457 | } |
||||
458 | |||||
459 | return $result; |
||||
460 | } |
||||
461 | |||||
462 | /** |
||||
463 | * @param array $params |
||||
464 | * @param string $query |
||||
465 | * @param $executed |
||||
466 | * @param array $result |
||||
467 | * @param string $object |
||||
468 | * |
||||
469 | * @return int |
||||
470 | */ |
||||
471 | public function getLeads($params, $query, &$executed, $result = [], $object = 'Lead') |
||||
472 | { |
||||
473 | if ('Lead' === $object || 'Contact' === $object) { |
||||
474 | $object .= 's'; // pluralize object name for Zoho |
||||
475 | } |
||||
476 | |||||
477 | $executed = 0; |
||||
478 | |||||
479 | try { |
||||
480 | if ($this->isAuthorized()) { |
||||
481 | $config = $this->mergeConfigToFeatureSettings(); |
||||
482 | $fields = $config['leadFields']; |
||||
483 | $config['object'] = $object; |
||||
484 | $aFields = $this->getAvailableLeadFields($config); |
||||
485 | $mappedData = []; |
||||
486 | |||||
487 | foreach (array_keys($fields) as $k) { |
||||
488 | if (isset($aFields[$object][$k])) { |
||||
489 | $mappedData[] = $aFields[$object][$k]['api_name']; |
||||
490 | } |
||||
491 | } |
||||
492 | |||||
493 | $maxRecords = 200; |
||||
494 | $fields = implode(',', $mappedData); |
||||
495 | $oparams['fields'] = $fields; |
||||
496 | $oparams['per_page'] = $maxRecords; // maximum number of records |
||||
497 | if (isset($params['fetchAll'], $params['start']) && !$params['fetchAll']) { |
||||
498 | $oparams['lastModifiedTime'] = date('c', strtotime($params['start'])); |
||||
499 | } |
||||
500 | |||||
501 | if (!array_key_exists('page', $oparams)) { |
||||
502 | $oparams['page'] = 1; |
||||
503 | } |
||||
504 | |||||
505 | if (isset($params['output']) && $params['output']->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) { |
||||
506 | $progress = new ProgressBar($params['output']); |
||||
507 | $progress->start(); |
||||
508 | } |
||||
509 | |||||
510 | while (true) { |
||||
511 | $data = $this->getApiHelper()->getLeads($oparams, $object); |
||||
512 | |||||
513 | if (!isset($data['data'])) { |
||||
514 | break; // no more data, exit loop |
||||
515 | } |
||||
516 | $result = $this->amendLeadDataBeforeMauticPopulate($data, $object); |
||||
517 | $executed += count($result); |
||||
518 | if (isset($params['output'])) { |
||||
519 | if ($params['output']->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { |
||||
520 | $params['output']->writeln($result); |
||||
521 | } else { |
||||
522 | $progress->advance(); |
||||
523 | } |
||||
524 | } |
||||
525 | |||||
526 | // prepare next loop |
||||
527 | ++$oparams['page']; |
||||
528 | } |
||||
529 | |||||
530 | if (isset($params['output']) && $params['output']->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) { |
||||
531 | $progress->finish(); |
||||
532 | } |
||||
533 | } |
||||
534 | } catch (\Exception $e) { |
||||
535 | $this->logIntegrationError($e); |
||||
536 | } |
||||
537 | |||||
538 | return $executed; |
||||
539 | } |
||||
540 | |||||
541 | /** |
||||
542 | * @param array $params |
||||
543 | * @param null $query |
||||
544 | * @param null $executed |
||||
545 | * @param array $result |
||||
546 | * |
||||
547 | * @return int|null |
||||
548 | */ |
||||
549 | public function getCompanies($params = [], $query = null, &$executed = null, &$result = []) |
||||
550 | { |
||||
551 | $executed = 0; |
||||
552 | $object = 'company'; |
||||
553 | |||||
554 | try { |
||||
555 | if ($this->isAuthorized()) { |
||||
556 | $config = $this->mergeConfigToFeatureSettings(); |
||||
557 | $fields = $config['companyFields']; |
||||
558 | $config['object'] = $object; |
||||
559 | $aFields = $this->getAvailableLeadFields($config); |
||||
560 | $mappedData = []; |
||||
561 | |||||
562 | foreach (array_keys($fields) as $k) { |
||||
563 | if (isset($aFields[$object][$k])) { |
||||
564 | $mappedData[] = $aFields[$object][$k]['api_name']; |
||||
565 | } |
||||
566 | } |
||||
567 | |||||
568 | $maxRecords = 200; |
||||
569 | $fields = implode(',', $mappedData); |
||||
570 | $oparams['fields'] = $fields; |
||||
571 | $oparams['per_page'] = $maxRecords; // maximum number of records |
||||
572 | if (isset($params['fetchAll'], $params['start']) && !$params['fetchAll']) { |
||||
573 | $oparams['lastModifiedTime'] = date('c', strtotime($params['start'])); |
||||
574 | } |
||||
575 | |||||
576 | if (!array_key_exists('page', $oparams)) { |
||||
577 | $oparams['page'] = 1; |
||||
578 | } |
||||
579 | |||||
580 | if (isset($params['output']) && $params['output']->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) { |
||||
581 | $progress = new ProgressBar($params['output']); |
||||
582 | $progress->start(); |
||||
583 | } |
||||
584 | |||||
585 | while (true) { |
||||
586 | $data = $this->getApiHelper()->getCompanies($oparams); |
||||
587 | if (!isset($data['data'])) { |
||||
588 | break; // no more data, exit loop |
||||
589 | } |
||||
590 | $result = $this->amendLeadDataBeforeMauticPopulate($data, $object); |
||||
591 | $executed += count($result); |
||||
592 | if (isset($params['output'])) { |
||||
593 | if ($params['output']->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) { |
||||
594 | $params['output']->writeln($result); |
||||
595 | } else { |
||||
596 | $progress->advance(); |
||||
597 | } |
||||
598 | } |
||||
599 | |||||
600 | // prepare next loop |
||||
601 | ++$oparams['page']; |
||||
602 | } |
||||
603 | |||||
604 | if (isset($params['output']) && $params['output']->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) { |
||||
605 | $progress->finish(); |
||||
606 | } |
||||
607 | } |
||||
608 | } catch (\Exception $e) { |
||||
609 | $this->logIntegrationError($e); |
||||
610 | } |
||||
611 | |||||
612 | return $executed; |
||||
613 | } |
||||
614 | |||||
615 | /** |
||||
616 | * {@inheritdoc} |
||||
617 | * |
||||
618 | * @param array $data |
||||
619 | * @param array $config |
||||
620 | * @param null $object |
||||
621 | * |
||||
622 | * @return array |
||||
623 | */ |
||||
624 | public function populateMauticLeadData($data, $config = [], $object = 'Leads') |
||||
625 | { |
||||
626 | // Match that data with mapped lead fields |
||||
627 | $aFields = $this->getAvailableLeadFields($config); |
||||
628 | $matchedFields = []; |
||||
629 | |||||
630 | $fieldsName = ('company' === $object) ? 'companyFields' : 'leadFields'; |
||||
631 | |||||
632 | if (isset($aFields[$object])) { |
||||
633 | $aFields = $aFields[$object]; |
||||
634 | } |
||||
635 | foreach ($aFields as $k => $v) { |
||||
636 | foreach ($data as $dk => $dv) { |
||||
637 | if ($dk === $v['api_name']) { |
||||
638 | $matchedFields[$config[$fieldsName][$k]] = $dv; |
||||
639 | } |
||||
640 | } |
||||
641 | } |
||||
642 | |||||
643 | return $matchedFields; |
||||
644 | } |
||||
645 | |||||
646 | /** |
||||
647 | * Generate the auth login URL. Note that if oauth2, response_type=code is assumed. If this is not the case, |
||||
648 | * override this function. |
||||
649 | * |
||||
650 | * @return string |
||||
651 | */ |
||||
652 | public function getAuthLoginUrl() |
||||
653 | { |
||||
654 | $authType = $this->getAuthenticationType(); |
||||
655 | |||||
656 | if ('oauth2' == $authType) { |
||||
657 | $callback = $this->getAuthCallbackUrl(); |
||||
658 | $clientIdKey = $this->getClientIdKey(); |
||||
659 | $state = $this->getAuthLoginState(); |
||||
660 | $url = $this->getAuthenticationUrl() |
||||
661 | .'?client_id='.$this->keys[$clientIdKey] |
||||
662 | .'&response_type=code' |
||||
663 | .'&redirect_uri='.urlencode($callback) |
||||
664 | .'&state='.$state.'&prompt=consent&access_type=offline'; |
||||
665 | |||||
666 | if ($scope = $this->getAuthScope()) { |
||||
667 | $url .= '&scope='.urlencode($scope); |
||||
668 | } |
||||
669 | |||||
670 | if ($this->session) { |
||||
671 | $this->session->set($this->getName().'_csrf_token', $state); |
||||
672 | } |
||||
673 | |||||
674 | return $url; |
||||
675 | } else { |
||||
676 | return $this->router->generate( |
||||
677 | 'mautic_integration_auth_callback', |
||||
678 | ['integration' => $this->getName()] |
||||
679 | ); |
||||
680 | } |
||||
681 | } |
||||
682 | |||||
683 | /** |
||||
684 | * @param Form|FormBuilder $builder |
||||
685 | * @param array $data |
||||
686 | * @param string $formArea |
||||
687 | * |
||||
688 | * @throws \InvalidArgumentException |
||||
689 | */ |
||||
690 | public function appendToForm(&$builder, $data, $formArea) |
||||
691 | { |
||||
692 | if ('features' == $formArea) { |
||||
693 | $builder->add( |
||||
694 | 'updateBlanks', |
||||
695 | ChoiceType::class, |
||||
696 | [ |
||||
697 | 'choices' => [ |
||||
698 | 'mautic.integrations.blanks' => 'updateBlanks', |
||||
699 | ], |
||||
700 | 'expanded' => true, |
||||
701 | 'multiple' => true, |
||||
702 | 'label' => 'mautic.integrations.form.blanks', |
||||
703 | 'label_attr' => ['class' => 'control-label'], |
||||
704 | 'placeholder' => false, |
||||
705 | 'required' => false, |
||||
706 | ] |
||||
707 | ); |
||||
708 | } |
||||
709 | if ('keys' === $formArea) { |
||||
710 | $builder->add( |
||||
711 | 'datacenter', |
||||
712 | ChoiceType::class, |
||||
713 | [ |
||||
714 | 'choices' => [ |
||||
715 | 'mautic.plugin.zoho.zone_us' => 'zoho.com', |
||||
716 | 'mautic.plugin.zoho.zone_europe' => 'zoho.eu', |
||||
717 | 'mautic.plugin.zoho.zone_japan' => 'zoho.co.jp', |
||||
718 | 'mautic.plugin.zoho.zone_china' => 'zoho.com.cn', |
||||
719 | ], |
||||
720 | 'label' => 'mautic.plugin.zoho.zone_select', |
||||
721 | 'placeholder' => false, |
||||
722 | 'required' => true, |
||||
723 | 'attr' => [ |
||||
724 | 'tooltip' => 'mautic.plugin.zoho.zone.tooltip', |
||||
725 | ], |
||||
726 | ] |
||||
727 | ); |
||||
728 | } elseif ('features' === $formArea) { |
||||
729 | $builder->add( |
||||
730 | 'objects', |
||||
731 | ChoiceType::class, |
||||
732 | [ |
||||
733 | 'choices' => [ |
||||
734 | 'mautic.zoho.object.lead' => 'Leads', |
||||
735 | 'mautic.zoho.object.contact' => 'Contacts', |
||||
736 | 'mautic.zoho.object.account' => 'company', |
||||
737 | ], |
||||
738 | 'expanded' => true, |
||||
739 | 'multiple' => true, |
||||
740 | 'label' => $this->getTranslator()->trans('mautic.crm.form.objects_to_pull_from', ['%crm%' => 'Zoho']), |
||||
741 | 'label_attr' => ['class' => ''], |
||||
742 | 'placeholder' => false, |
||||
743 | 'required' => false, |
||||
744 | ] |
||||
745 | ); |
||||
746 | } |
||||
747 | } |
||||
748 | |||||
749 | /** |
||||
750 | * Get available company fields for choices in the config UI. |
||||
751 | * |
||||
752 | * @param array $settings |
||||
753 | * |
||||
754 | * @return array |
||||
755 | */ |
||||
756 | public function getFormCompanyFields($settings = []) |
||||
757 | { |
||||
758 | return $this->getFormFieldsByObject('Accounts', $settings); |
||||
759 | } |
||||
760 | |||||
761 | /** |
||||
762 | * @param array $settings |
||||
763 | * |
||||
764 | * @return array|mixed |
||||
765 | */ |
||||
766 | public function getFormLeadFields($settings = []) |
||||
767 | { |
||||
768 | $leadFields = $this->getFormFieldsByObject('Leads', $settings); |
||||
769 | $contactFields = $this->getFormFieldsByObject('Contacts', $settings); |
||||
770 | |||||
771 | return array_merge($leadFields, $contactFields); |
||||
772 | } |
||||
773 | |||||
774 | /** |
||||
775 | * @param array $settings |
||||
776 | * |
||||
777 | * @return array|bool |
||||
778 | * |
||||
779 | * @throws ApiErrorException |
||||
780 | */ |
||||
781 | public function getAvailableLeadFields($settings = []) |
||||
782 | { |
||||
783 | $zohoFields = []; |
||||
784 | $silenceExceptions = isset($settings['silence_exceptions']) ? $settings['silence_exceptions'] : true; |
||||
785 | |||||
786 | if (isset($settings['feature_settings']['objects'])) { |
||||
787 | $zohoObjects = $settings['feature_settings']['objects']; |
||||
788 | } else { |
||||
789 | $settings = $this->settings->getFeatureSettings(); |
||||
790 | $zohoObjects = isset($settings['objects']) ? $settings['objects'] : ['Leads']; |
||||
791 | } |
||||
792 | |||||
793 | try { |
||||
794 | if ($this->isAuthorized()) { |
||||
795 | if (!empty($zohoObjects) && is_array($zohoObjects)) { |
||||
796 | foreach ($zohoObjects as $zohoObject) { |
||||
797 | // Check the cache first |
||||
798 | $settings['cache_suffix'] = $cacheSuffix = '.'.$zohoObject; |
||||
799 | if ($fields = parent::getAvailableLeadFields($settings)) { |
||||
800 | $zohoFields[$zohoObject] = $fields; |
||||
801 | continue; |
||||
802 | } |
||||
803 | $leadObject = $this->getApiHelper()->getLeadFields($zohoObject); |
||||
804 | |||||
805 | if (null === $leadObject || (isset($leadObject['status']) && 'error' === $leadObject['status'])) { |
||||
806 | return []; |
||||
807 | } |
||||
808 | |||||
809 | /** @var array $opts */ |
||||
810 | $opts = $leadObject['fields']; |
||||
811 | foreach ($opts as $field) { |
||||
812 | if (true == $field['read_only']) { |
||||
813 | continue; |
||||
814 | } |
||||
815 | |||||
816 | $is_required = false; |
||||
817 | if (true == $field['system_mandatory']) { |
||||
818 | $is_required = true; |
||||
819 | } |
||||
820 | |||||
821 | $zohoFields[$zohoObject][$field['api_name']] = [ |
||||
822 | 'type' => 'string', |
||||
823 | 'label' => $field['display_label'], |
||||
824 | 'api_name' => $field['api_name'], |
||||
825 | 'required' => $is_required, |
||||
826 | ]; |
||||
827 | } |
||||
828 | if (empty($settings['ignore_field_cache'])) { |
||||
829 | $this->cache->set('leadFields'.$cacheSuffix, $zohoFields[$zohoObject]); |
||||
830 | } |
||||
831 | } |
||||
832 | } |
||||
833 | } |
||||
834 | } catch (ApiErrorException $exception) { |
||||
835 | $this->logIntegrationError($exception); |
||||
836 | |||||
837 | if (!$silenceExceptions) { |
||||
838 | if (false !== strpos($exception->getMessage(), 'Invalid Ticket Id')) { |
||||
839 | // Use a bit more friendly message |
||||
840 | $exception = new ApiErrorException('There was an issue with communicating with Zoho. Please try to reauthorize.'); |
||||
841 | } |
||||
842 | |||||
843 | throw $exception; |
||||
844 | } |
||||
845 | |||||
846 | return false; |
||||
847 | } |
||||
848 | |||||
849 | return $zohoFields; |
||||
850 | } |
||||
851 | |||||
852 | /** |
||||
853 | * @param array $params |
||||
854 | * |
||||
855 | * @return mixed |
||||
856 | */ |
||||
857 | public function pushLeads($params = []) |
||||
858 | { |
||||
859 | $maxRecords = (isset($params['limit']) && $params['limit'] < 100) ? $params['limit'] : 100; |
||||
860 | if (isset($params['fetchAll']) && $params['fetchAll']) { |
||||
861 | $params['start'] = null; |
||||
862 | $params['end'] = null; |
||||
863 | } |
||||
864 | $config = $this->mergeConfigToFeatureSettings(); |
||||
865 | $integrationEntityRepo = $this->em->getRepository('MauticPluginBundle:IntegrationEntity'); |
||||
866 | $fieldsToUpdateInZoho = isset($config['update_mautic']) ? array_keys($config['update_mautic'], 0) : []; |
||||
867 | $leadFields = array_unique(array_values($config['leadFields'])); |
||||
868 | $totalUpdated = $totalCreated = $totalErrors = 0; |
||||
869 | if ($key = array_search('mauticContactTimelineLink', $leadFields)) { |
||||
870 | unset($leadFields[$key]); |
||||
871 | } |
||||
872 | if ($key = array_search('mauticContactIsContactableByEmail', $leadFields)) { |
||||
873 | unset($leadFields[$key]); |
||||
874 | } |
||||
875 | if (empty($leadFields)) { |
||||
876 | return [0, 0, 0]; |
||||
877 | } |
||||
878 | |||||
879 | $fields = implode(', l.', $leadFields); |
||||
880 | $fields = 'l.'.$fields; |
||||
881 | |||||
882 | $availableFields = $this->getAvailableLeadFields(['feature_settings' => ['objects' => ['Leads', 'Contacts']]]); |
||||
883 | $fieldsToUpdate['Leads'] = array_values(array_intersect(array_keys($availableFields['Leads']), $fieldsToUpdateInZoho)); |
||||
884 | $fieldsToUpdate['Contacts'] = array_values(array_intersect(array_keys($availableFields['Contacts']), $fieldsToUpdateInZoho)); |
||||
885 | $fieldsToUpdate['Leads'] = array_intersect_key($config['leadFields'], array_flip($fieldsToUpdate['Leads'])); |
||||
886 | $fieldsToUpdate['Contacts'] = array_intersect_key($config['leadFields'], array_flip($fieldsToUpdate['Contacts'])); |
||||
887 | |||||
888 | $progress = false; |
||||
889 | $totalToUpdate = array_sum( |
||||
890 | $integrationEntityRepo->findLeadsToUpdate('Zoho', 'lead', $fields, false, $params['start'], $params['end'], ['Contacts', 'Leads']) |
||||
891 | ); |
||||
892 | $totalToCreate = $integrationEntityRepo->findLeadsToCreate('Zoho', $fields, false, $params['start'], $params['end']); |
||||
893 | $totalCount = $totalToCreate + $totalToUpdate; |
||||
894 | |||||
895 | if (defined('IN_MAUTIC_CONSOLE')) { |
||||
896 | // start with update |
||||
897 | if ($totalToUpdate + $totalToCreate) { |
||||
898 | $output = new ConsoleOutput(); |
||||
899 | $output->writeln("About $totalToUpdate to update and about $totalToCreate to create/update"); |
||||
900 | $progress = new ProgressBar($output, $totalCount); |
||||
901 | } |
||||
902 | } |
||||
903 | |||||
904 | // Start with contacts so we know who is a contact when we go to process converted leads |
||||
905 | $leadsToCreateInZ = []; |
||||
906 | $leadsToUpdateInZ = []; |
||||
907 | $isContact = []; |
||||
908 | $integrationEntities = []; |
||||
909 | |||||
910 | // Fetch them separately so we can determine which oneas are already there |
||||
911 | $toUpdate = $integrationEntityRepo->findLeadsToUpdate( |
||||
912 | 'Zoho', |
||||
913 | 'lead', |
||||
914 | $fields, |
||||
915 | $totalToUpdate, |
||||
916 | $params['start'], |
||||
917 | $params['end'], |
||||
918 | 'Contacts', |
||||
919 | [] |
||||
920 | )['Contacts']; |
||||
921 | |||||
922 | if (is_array($toUpdate)) { |
||||
923 | foreach ($toUpdate as $lead) { |
||||
924 | if (isset($lead['email']) && !empty($lead['email'])) { |
||||
925 | $key = mb_strtolower($this->cleanPushData($lead['email'])); |
||||
926 | $lead['integration_entity'] = 'Contacts'; |
||||
927 | $leadsToUpdateInZ[$key] = $lead; |
||||
928 | $isContact[$key] = $lead; |
||||
929 | } |
||||
930 | } |
||||
931 | } |
||||
932 | |||||
933 | // Switch to Lead |
||||
934 | $toUpdate = $integrationEntityRepo->findLeadsToUpdate( |
||||
935 | 'Zoho', |
||||
936 | 'lead', |
||||
937 | $fields, |
||||
938 | $totalToUpdate, |
||||
939 | $params['start'], |
||||
940 | $params['end'], |
||||
941 | 'Leads', |
||||
942 | [] |
||||
943 | )['Leads']; |
||||
944 | |||||
945 | if (is_array($toUpdate)) { |
||||
946 | foreach ($toUpdate as $lead) { |
||||
947 | if (isset($lead['email']) && !empty($lead['email'])) { |
||||
948 | $key = mb_strtolower($this->cleanPushData($lead['email'])); |
||||
949 | $lead = $this->getCompoundMauticFields($lead); |
||||
950 | if (isset($isContact[$key])) { |
||||
951 | $isContact[$key] = $lead; // lead-converted |
||||
952 | } else { |
||||
953 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||||
954 | 'Zoho', |
||||
955 | 'Leads', |
||||
956 | 'lead', |
||||
957 | $lead['internal_entity_id'] |
||||
958 | ); |
||||
959 | |||||
960 | $lead['integration_entity'] = 'Leads'; |
||||
961 | $leadsToUpdateInZ[$key] = $lead; |
||||
962 | $integrationEntity = $this->em->getReference('MauticPluginBundle:IntegrationEntity', $integrationId[0]['id']); |
||||
963 | $integrationEntities[] = $integrationEntity->setLastSyncDate(new \DateTime()); |
||||
964 | } |
||||
965 | } |
||||
966 | } |
||||
967 | } |
||||
968 | unset($toUpdate); |
||||
969 | |||||
970 | // convert ignored contacts |
||||
971 | foreach ($isContact as $email => $lead) { |
||||
972 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId( |
||||
973 | 'Zoho', |
||||
974 | 'Leads', |
||||
975 | 'lead', |
||||
976 | $lead['internal_entity_id'] |
||||
977 | ); |
||||
978 | if (count($integrationId)) { // lead exists, then update |
||||
979 | $integrationEntity = $this->em->getReference('MauticPluginBundle:IntegrationEntity', $integrationId[0]['id']); |
||||
980 | $integrationEntity->setLastSyncDate(new \DateTime()); |
||||
981 | $integrationEntity->setInternalEntity('lead-converted'); |
||||
982 | $integrationEntities[] = $integrationEntity; |
||||
983 | unset($leadsToUpdateInZ[$email]); |
||||
984 | } |
||||
985 | } |
||||
986 | |||||
987 | //create lead records, including deleted on Zoho side (last_sync = null) |
||||
988 | /** @var array $leadsToCreate */ |
||||
989 | $leadsToCreate = $integrationEntityRepo->findLeadsToCreate('Zoho', $fields, $totalToCreate, $params['start'], $params['end']); |
||||
990 | |||||
991 | if (is_array($leadsToCreate)) { |
||||
992 | foreach ($leadsToCreate as $lead) { |
||||
993 | if (isset($lead['email']) && !empty($lead['email'])) { |
||||
994 | $key = mb_strtolower($this->cleanPushData($lead['email'])); |
||||
995 | $lead = $this->getCompoundMauticFields($lead); |
||||
996 | $lead['integration_entity'] = 'Leads'; |
||||
997 | $leadsToCreateInZ[$key] = $lead; |
||||
998 | } |
||||
999 | } |
||||
1000 | } |
||||
1001 | unset($leadsToCreate); |
||||
1002 | |||||
1003 | if (count($integrationEntities)) { |
||||
1004 | // Persist updated entities if applicable |
||||
1005 | $integrationEntityRepo->saveEntities($integrationEntities); |
||||
1006 | $this->em->clear(IntegrationEntity::class); |
||||
1007 | } |
||||
1008 | |||||
1009 | // update leads and contacts |
||||
1010 | $mapper = new Mapper($availableFields); |
||||
1011 | foreach (['Leads', 'Contacts'] as $zObject) { |
||||
1012 | $counter = 1; |
||||
1013 | $mapper->setObject($zObject); |
||||
1014 | foreach ($leadsToUpdateInZ as $lead) { |
||||
1015 | if ($zObject !== $lead['integration_entity']) { |
||||
1016 | continue; |
||||
1017 | } |
||||
1018 | |||||
1019 | if ($progress) { |
||||
1020 | $progress->advance(); |
||||
1021 | } |
||||
1022 | |||||
1023 | $existingPerson = $this->getExistingRecord('Email', $lead['email'], $zObject); |
||||
1024 | $objectFields = $this->prepareFieldsForPush($availableFields[$zObject]); |
||||
1025 | $fieldsToUpdate[$zObject] = $this->getBlankFieldsToUpdate($fieldsToUpdate[$zObject], $existingPerson, $objectFields, $config); |
||||
1026 | |||||
1027 | $totalUpdated += $mapper |
||||
1028 | ->setMappedFields($fieldsToUpdate[$zObject]) |
||||
1029 | ->setContact($lead) |
||||
1030 | ->map($lead['internal_entity_id'], $lead['integration_entity_id']); |
||||
1031 | ++$counter; |
||||
1032 | |||||
1033 | // ONLY 100 RECORDS CAN BE SENT AT A TIME |
||||
1034 | if ($maxRecords === $counter) { |
||||
1035 | $this->updateContactInZoho($mapper, $zObject, $totalUpdated, $totalErrors); |
||||
1036 | $counter = 1; |
||||
1037 | } |
||||
1038 | } |
||||
1039 | |||||
1040 | if ($counter > 1) { |
||||
1041 | $this->updateContactInZoho($mapper, $zObject, $totalUpdated, $totalErrors); |
||||
1042 | } |
||||
1043 | } |
||||
1044 | |||||
1045 | // create leads and contacts |
||||
1046 | foreach (['Leads', 'Contacts'] as $zObject) { |
||||
1047 | $counter = 1; |
||||
1048 | $mapper->setObject($zObject); |
||||
1049 | foreach ($leadsToCreateInZ as $lead) { |
||||
1050 | if ($zObject !== $lead['integration_entity']) { |
||||
1051 | continue; |
||||
1052 | } |
||||
1053 | if ($progress) { |
||||
1054 | $progress->advance(); |
||||
1055 | } |
||||
1056 | |||||
1057 | $totalCreated += $mapper |
||||
1058 | ->setMappedFields($config['leadFields']) |
||||
1059 | ->setContact($lead) |
||||
1060 | ->map($lead['internal_entity_id']); |
||||
1061 | ++$counter; |
||||
1062 | |||||
1063 | // ONLY 100 RECORDS CAN BE SENT AT A TIME |
||||
1064 | if ($maxRecords === $counter) { |
||||
1065 | $this->createContactInZoho($mapper, $zObject, $totalCreated, $totalErrors); |
||||
1066 | $counter = 1; |
||||
1067 | } |
||||
1068 | } |
||||
1069 | |||||
1070 | if ($counter > 1) { |
||||
1071 | $this->createContactInZoho($mapper, $zObject, $totalCreated, $totalErrors); |
||||
1072 | } |
||||
1073 | } |
||||
1074 | |||||
1075 | if ($progress) { |
||||
1076 | $progress->finish(); |
||||
1077 | $output->writeln(''); |
||||
1078 | } |
||||
1079 | |||||
1080 | return [$totalUpdated, $totalCreated, $totalErrors, $totalCount - ($totalCreated + $totalUpdated + $totalErrors)]; |
||||
1081 | } |
||||
1082 | |||||
1083 | /** |
||||
1084 | * @param Lead|array $lead |
||||
1085 | * @param array $config |
||||
1086 | * |
||||
1087 | * @return array|bool |
||||
1088 | */ |
||||
1089 | public function pushLead($lead, $config = []) |
||||
1090 | { |
||||
1091 | $config = $this->mergeConfigToFeatureSettings($config); |
||||
1092 | $zObject = 'Leads'; |
||||
1093 | |||||
1094 | $fieldsToUpdateInZoho = isset($config['update_mautic']) ? array_keys($config['update_mautic'], 0) : []; |
||||
1095 | $availableFields = $this->getAvailableLeadFields(['feature_settings' => ['objects' => ['Leads', 'Contacts']]]); |
||||
1096 | $fieldsToUpdate['Leads'] = array_values(array_intersect(array_keys($availableFields['Leads']), $fieldsToUpdateInZoho)); |
||||
1097 | $fieldsToUpdate['Contacts'] = array_values(array_intersect(array_keys($availableFields['Contacts']), $fieldsToUpdateInZoho)); |
||||
1098 | $fieldsToUpdate['Leads'] = array_intersect_key($config['leadFields'], array_flip($fieldsToUpdate['Leads'])); |
||||
1099 | $fieldsToUpdate['Contacts'] = array_intersect_key($config['leadFields'], array_flip($fieldsToUpdate['Contacts'])); |
||||
1100 | $objectFields = $this->prepareFieldsForPush($availableFields[$zObject]); |
||||
1101 | $existingPerson = $this->getExistingRecord('Email', $lead->getEmail(), $zObject); |
||||
1102 | $fieldsToUpdate[$zObject] = $this->getBlankFieldsToUpdate($fieldsToUpdate[$zObject], $existingPerson, $objectFields, $config); |
||||
1103 | |||||
1104 | if (empty($config['leadFields'])) { |
||||
1105 | return []; |
||||
1106 | } |
||||
1107 | |||||
1108 | $mapper = new Mapper($availableFields); |
||||
1109 | $mapper->setObject($zObject); |
||||
1110 | |||||
1111 | $integrationEntityRepo = $this->em->getRepository('MauticPluginBundle:IntegrationEntity'); |
||||
1112 | $integrationId = $integrationEntityRepo->getIntegrationsEntityId('Zoho', $zObject, 'lead', $lead->getId()); |
||||
1113 | |||||
1114 | $counter = 0; |
||||
1115 | $errorCounter = 0; |
||||
1116 | |||||
1117 | try { |
||||
1118 | if ($this->isAuthorized()) { |
||||
1119 | if (!empty($existingPerson) && empty($integrationId)) { |
||||
1120 | $this->createIntegrationEntity($zObject, $existingPerson['id'], 'lead', $lead->getId()); |
||||
1121 | |||||
1122 | $mapper |
||||
1123 | ->setMappedFields($fieldsToUpdate[$zObject]) |
||||
1124 | ->setContact($lead->getProfileFields()) |
||||
1125 | ->map($lead->getId(), $existingPerson['id']); |
||||
1126 | $this->updateContactInZoho($mapper, $zObject, $counter, $errorCounter); |
||||
1127 | } elseif (!empty($existingPerson) && !empty($integrationId)) { // contact exists, then update |
||||
1128 | $mapper |
||||
1129 | ->setMappedFields($fieldsToUpdate[$zObject]) |
||||
1130 | ->setContact($lead->getProfileFields()) |
||||
1131 | ->map($lead->getId(), $existingPerson['id']); |
||||
1132 | $this->updateContactInZoho($mapper, $zObject, $counter, $errorCounter); |
||||
1133 | } else { |
||||
1134 | $mapper |
||||
1135 | ->setMappedFields($config['leadFields']) |
||||
1136 | ->setContact($lead->getProfileFields()) |
||||
1137 | ->map($lead->getId()); |
||||
1138 | $this->createContactInZoho($mapper, $zObject, $counter, $errorCounter); |
||||
1139 | } |
||||
1140 | |||||
1141 | return true; |
||||
1142 | } |
||||
1143 | } catch (\Exception $e) { |
||||
1144 | $this->logIntegrationError($e); |
||||
1145 | } |
||||
1146 | |||||
1147 | return false; |
||||
1148 | } |
||||
1149 | |||||
1150 | /** |
||||
1151 | * @param $fields |
||||
1152 | * @param $sfRecord |
||||
1153 | * @param $config |
||||
1154 | * @param $objectFields |
||||
1155 | */ |
||||
1156 | public function getBlankFieldsToUpdate($fields, $sfRecord, $objectFields, $config) |
||||
1157 | { |
||||
1158 | //check if update blank fields is selected |
||||
1159 | if (isset($config['updateBlanks']) && isset($config['updateBlanks'][0]) && 'updateBlanks' == $config['updateBlanks'][0]) { |
||||
1160 | foreach ($sfRecord as $fieldName => $sfField) { |
||||
1161 | if (array_key_exists($fieldName, $objectFields['required']['fields'])) { |
||||
1162 | continue; // this will be treated differently |
||||
1163 | } |
||||
1164 | if ('null' === $sfField && array_key_exists($fieldName, $objectFields['create']) && !array_key_exists($fieldName, $fields)) { |
||||
1165 | //map to mautic field |
||||
1166 | $fields[$fieldName] = $objectFields['create'][$fieldName]; |
||||
1167 | } |
||||
1168 | } |
||||
1169 | } |
||||
1170 | |||||
1171 | return $fields; |
||||
1172 | } |
||||
1173 | |||||
1174 | /** |
||||
1175 | * Get if data priority is enabled in the integration or not default is false. |
||||
1176 | * |
||||
1177 | * @return string |
||||
1178 | */ |
||||
1179 | public function getDataPriority() |
||||
1180 | { |
||||
1181 | return true; |
||||
1182 | } |
||||
1183 | |||||
1184 | /** |
||||
1185 | * @param array $response |
||||
1186 | * @param string $zObject |
||||
1187 | * @param bool $createIntegrationEntity |
||||
1188 | * |
||||
1189 | * @return int |
||||
1190 | * |
||||
1191 | * @throws \MauticPlugin\MauticCrmBundle\Api\Zoho\Exception\MatchingKeyNotFoundException |
||||
1192 | */ |
||||
1193 | private function consumeResponse($response, $zObject, $createIntegrationEntity = false, Mapper $mapper = null) |
||||
1194 | { |
||||
1195 | $rows = $response; |
||||
1196 | if (isset($rows['data'][0])) { |
||||
1197 | $rows = $rows['data']; |
||||
1198 | } |
||||
1199 | |||||
1200 | $failed = 0; |
||||
1201 | foreach ($rows as $key => $row) { |
||||
1202 | $mauticId = $mapper->getContactIdByKey($key); |
||||
1203 | |||||
1204 | if ('SUCCESS' === $row['code'] && $createIntegrationEntity) { |
||||
1205 | $zohoId = $row['details']['id']; |
||||
1206 | $this->logger->debug('CREATE INTEGRATION ENTITY: '.$zohoId); |
||||
1207 | $integrationId = $this->getIntegrationEntityRepository()->getIntegrationsEntityId( |
||||
1208 | 'Zoho', |
||||
1209 | $zObject, |
||||
1210 | 'lead', |
||||
1211 | null, |
||||
1212 | null, |
||||
1213 | null, |
||||
1214 | false, |
||||
1215 | 0, |
||||
1216 | 0, |
||||
1217 | $zohoId |
||||
1218 | ); |
||||
1219 | |||||
1220 | if (0 === count($integrationId)) { |
||||
1221 | $this->createIntegrationEntity($zObject, $zohoId, 'lead', $mauticId); |
||||
1222 | } |
||||
1223 | } elseif (isset($row['status']) && 'error' === $row['status']) { |
||||
1224 | ++$failed; |
||||
1225 | $exception = new ApiErrorException($row['message']); |
||||
1226 | $exception->setContactId($mauticId); |
||||
1227 | $this->logIntegrationError($exception); |
||||
1228 | } |
||||
1229 | } |
||||
1230 | |||||
1231 | return $failed; |
||||
1232 | } |
||||
1233 | |||||
1234 | /** |
||||
1235 | * @param string $seachColumn |
||||
1236 | * @param string $searchValue |
||||
1237 | * @param string $object |
||||
1238 | * |
||||
1239 | * @return array |
||||
1240 | */ |
||||
1241 | private function getExistingRecord($seachColumn, $searchValue, $object = 'Leads') |
||||
1242 | { |
||||
1243 | $availableFields = $this->getAvailableLeadFields(['feature_settings' => ['objects' => ['Leads', 'Contacts']]]); |
||||
1244 | $records = $this->getApiHelper()->getSearchRecords($seachColumn, $searchValue, $object); |
||||
1245 | $idField = [ |
||||
1246 | 'id' => [ |
||||
1247 | 'type' => 'string', |
||||
1248 | 'label' => 'ID', |
||||
1249 | 'api_name' => 'id', |
||||
1250 | 'required' => true, |
||||
1251 | ], |
||||
1252 | ]; |
||||
1253 | |||||
1254 | return $this->parseZohoRecord($records, array_merge($availableFields[$object], $idField)); |
||||
1255 | } |
||||
1256 | |||||
1257 | /** |
||||
1258 | * @param $data |
||||
1259 | * @param $fields |
||||
1260 | * |
||||
1261 | * @return array |
||||
1262 | */ |
||||
1263 | private function parseZohoRecord($data, $fields) |
||||
1264 | { |
||||
1265 | $parsedData = []; |
||||
1266 | if (empty($data['data'])) { |
||||
1267 | return $parsedData; |
||||
1268 | } |
||||
1269 | |||||
1270 | $records = $data['data'][0]; |
||||
1271 | foreach ($fields as $field) { |
||||
1272 | foreach ($records as $recordKey => $recordValue) { |
||||
1273 | if ($recordKey === $field['api_name']) { |
||||
1274 | $parsedData[$recordKey] = $recordValue; |
||||
1275 | continue; |
||||
1276 | } |
||||
1277 | } |
||||
1278 | } |
||||
1279 | |||||
1280 | return $parsedData; |
||||
1281 | } |
||||
1282 | |||||
1283 | /** |
||||
1284 | * @param string $object |
||||
1285 | * @param int $counter |
||||
1286 | * @param int $errorCounter |
||||
1287 | */ |
||||
1288 | private function updateContactInZoho(Mapper $mapper, $object, &$counter, &$errorCounter) |
||||
1289 | { |
||||
1290 | $response = $this->getApiHelper()->updateLead($mapper->getArray(), $object); |
||||
1291 | $failed = $this->consumeResponse($response, $object, false, $mapper); |
||||
1292 | $counter -= $failed; |
||||
1293 | $errorCounter += $failed; |
||||
1294 | } |
||||
1295 | |||||
1296 | /** |
||||
1297 | * @param string $object |
||||
1298 | * @param int $counter |
||||
1299 | * @param int $errorCounter |
||||
1300 | */ |
||||
1301 | private function createContactInZoho(Mapper $mapper, $object, &$counter, &$errorCounter) |
||||
1302 | { |
||||
1303 | $response = $this->getApiHelper()->createLead($mapper->getArray(), $object); |
||||
1304 | $failed = $this->consumeResponse($response, $object, true, $mapper); |
||||
1305 | $counter -= $failed; |
||||
1306 | $errorCounter += $failed; |
||||
1307 | } |
||||
1308 | |||||
1309 | /** |
||||
1310 | * @param $fieldsToUpdate |
||||
1311 | * @param array $objects |
||||
1312 | * |
||||
1313 | * @return array |
||||
1314 | */ |
||||
1315 | protected function cleanPriorityFields($fieldsToUpdate, $objects = null) |
||||
1316 | { |
||||
1317 | if (null === $objects) { |
||||
1318 | $objects = ['Leads', 'Contacts']; |
||||
1319 | } |
||||
1320 | |||||
1321 | if (isset($fieldsToUpdate['leadFields'])) { |
||||
1322 | // Pass in the whole config |
||||
1323 | $fields = $fieldsToUpdate; |
||||
1324 | } else { |
||||
1325 | $fields = array_flip($fieldsToUpdate); |
||||
1326 | } |
||||
1327 | |||||
1328 | return $this->prepareFieldsForSync($fields, $fieldsToUpdate, $objects); |
||||
1329 | } |
||||
1330 | |||||
1331 | /** |
||||
1332 | * @param array $fields |
||||
1333 | * @param array $keys |
||||
1334 | * @param mixed $object |
||||
1335 | * |
||||
1336 | * @return array |
||||
1337 | */ |
||||
1338 | public function prepareFieldsForSync($fields, $keys, $object = null) |
||||
1339 | { |
||||
1340 | $leadFields = []; |
||||
1341 | if (null === $object) { |
||||
1342 | $object = 'Leads'; |
||||
1343 | } |
||||
1344 | |||||
1345 | $objects = (!is_array($object)) ? [$object] : $object; |
||||
1346 | if (is_string($object) && 'Accounts' === $object) { |
||||
1347 | return isset($fields['companyFields']) ? $fields['companyFields'] : $fields; |
||||
1348 | } |
||||
1349 | |||||
1350 | if (isset($fields['leadFields'])) { |
||||
1351 | $fields = $fields['leadFields']; |
||||
1352 | $keys = array_keys($fields); |
||||
1353 | } |
||||
1354 | |||||
1355 | foreach ($objects as $obj) { |
||||
1356 | if (!isset($leadFields[$obj])) { |
||||
1357 | $leadFields[$obj] = []; |
||||
1358 | } |
||||
1359 | |||||
1360 | foreach ($keys as $key) { |
||||
1361 | $leadFields[$obj][$key] = $fields[$key]; |
||||
1362 | } |
||||
1363 | } |
||||
1364 | |||||
1365 | return (is_array($object)) ? $leadFields : $leadFields[$object]; |
||||
1366 | } |
||||
1367 | } |
||||
1368 |
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.