Spaces:
No application file
No application file
| namespace MauticPlugin\MauticCrmBundle\Integration; | |
| use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType; | |
| use Mautic\LeadBundle\Entity\Lead; | |
| use Mautic\PluginBundle\Entity\IntegrationEntity; | |
| use Mautic\PluginBundle\Entity\IntegrationEntityRepository; | |
| use Mautic\PluginBundle\Exception\ApiErrorException; | |
| use Mautic\PluginBundle\Integration\IntegrationObject; | |
| use MauticPlugin\MauticCrmBundle\Form\Type\IntegrationCampaignsTaskType; | |
| use Symfony\Component\Form\Extension\Core\Type\ChoiceType; | |
| use Symfony\Component\Form\FormBuilder; | |
| /** | |
| * @method \MauticPlugin\MauticCrmBundle\Api\ConnectwiseApi getApiHelper() | |
| */ | |
| class ConnectwiseIntegration extends CrmAbstractIntegration | |
| { | |
| public const PAGESIZE = 200; | |
| public function getName(): string | |
| { | |
| return 'Connectwise'; | |
| } | |
| public function getSupportedFeatures(): array | |
| { | |
| return ['push_lead', 'get_leads']; | |
| } | |
| /** | |
| * @return array<string, string> | |
| */ | |
| public function getRequiredKeyFields(): array | |
| { | |
| return [ | |
| 'username' => 'mautic.connectwise.form.integrator', | |
| 'password' => 'mautic.connectwise.form.privatekey', | |
| 'site' => 'mautic.connectwise.form.site', | |
| 'appcookie' => 'mautic.connectwise.form.cookie', | |
| ]; | |
| } | |
| /** | |
| * Get the array key for application cookie. | |
| */ | |
| public function getClientId(): string | |
| { | |
| return 'appcookie'; | |
| } | |
| /** | |
| * Get the array key for companyid. | |
| */ | |
| public function getCompanyIdKey(): string | |
| { | |
| return 'companyid'; | |
| } | |
| /** | |
| * Get the array key for client id. | |
| */ | |
| public function getIntegrator(): string | |
| { | |
| return 'username'; | |
| } | |
| /** | |
| * Get the array key for client id. | |
| */ | |
| public function getConnectwiseUrl(): string | |
| { | |
| return 'site'; | |
| } | |
| /** | |
| * Get the array key for client secret. | |
| */ | |
| public function getClientSecretKey(): string | |
| { | |
| return 'password'; | |
| } | |
| public function getSecretKeys(): array | |
| { | |
| return [ | |
| 'password', 'appcookie', | |
| ]; | |
| } | |
| public function getApiUrl(): string | |
| { | |
| return sprintf('%s/v4_6_release/apis/3.0', $this->keys['site']); | |
| } | |
| /** | |
| * @return string | |
| */ | |
| public function getAuthLoginUrl() | |
| { | |
| return $this->router->generate('mautic_integration_auth_callback', ['integration' => $this->getName()]); | |
| } | |
| /** | |
| * @return bool | |
| */ | |
| public function authCallback($settings = [], $parameters = []) | |
| { | |
| $url = $this->getApiUrl(); | |
| $error = false; | |
| try { | |
| $response = $this->makeRequest($url.'/system/members/', $parameters, 'GET', $settings); | |
| foreach ($response as $key => $r) { | |
| $key = preg_replace('/[\r\n]+/', '', $key); | |
| switch ($key) { | |
| case '<!DOCTYPE_html_PUBLIC_"-//W3C//DTD_XHTML_1_0_Strict//EN"_"http://www_w3_org/TR/xhtml1/DTD/xhtml1-strict_dtd"><html_xmlns': | |
| $error = '404 not found error'; | |
| break; | |
| case 'code': | |
| $error = $response['message'].' '.$r; | |
| break; | |
| } | |
| } | |
| if (!$error) { | |
| $data = ['username' => $this->keys['username'], 'password' => $this->keys['password']]; | |
| $this->extractAuthKeys($data, 'username'); | |
| } | |
| } catch (\Exception $e) { | |
| return $e->getMessage(); | |
| } | |
| return $error; | |
| } | |
| /** | |
| * Append ClientID into header to enable authentication. | |
| * | |
| * @param string $url | |
| * @param array<mixed> $parameters | |
| * @param string $method | |
| * @param array<mixed> $settings | |
| * @param string $authType | |
| * | |
| * @return array<mixed> | |
| */ | |
| public function prepareRequest($url, $parameters, $method, $settings, $authType): array | |
| { | |
| [$parameters,$headers] = parent::prepareRequest($url, $parameters, $method, $settings, $authType); | |
| $headers['clientId'] = $this->keys['appcookie']; // Even though it is called appcookie it is ClientID | |
| return [$parameters, $headers]; | |
| } | |
| public function getAuthenticationType(): string | |
| { | |
| return 'basic'; | |
| } | |
| public function getDataPriority(): bool | |
| { | |
| return true; | |
| } | |
| /** | |
| * Get available company fields for choices in the config UI. | |
| * | |
| * @param array $settings | |
| * | |
| * @return array | |
| */ | |
| public function getFormCompanyFields($settings = []) | |
| { | |
| return $this->getFormFieldsByObject('company', $settings); | |
| } | |
| /** | |
| * @param array $settings | |
| * | |
| * @return array|mixed | |
| */ | |
| public function getFormLeadFields($settings = []) | |
| { | |
| return $this->getFormFieldsByObject('Contact', $settings); | |
| } | |
| /** | |
| * @return mixed[] | |
| */ | |
| public function getAvailableLeadFields($settings = []): array | |
| { | |
| $cwFields = []; | |
| if (isset($settings['feature_settings']['objects'])) { | |
| $cwObjects = $settings['feature_settings']['objects']; | |
| } else { | |
| $cwObjects['Contact'] = 'Contact'; | |
| } | |
| if (!$this->isAuthorized()) { | |
| return []; | |
| } | |
| switch ($cwObjects) { | |
| case isset($cwObjects['Contact']): | |
| $contactFields = $this->getContactFields(); | |
| $cwFields['Contact'] = $this->setFields($contactFields); | |
| break; | |
| case isset($cwObjects['company']): | |
| $company = $this->getCompanyFields(); | |
| $cwFields['company'] = $this->setFields($company); | |
| break; | |
| } | |
| return $cwFields; | |
| } | |
| public function setFields($fields): array | |
| { | |
| $cwFields = []; | |
| foreach ($fields as $fieldName => $field) { | |
| if (in_array($field['type'], ['string', 'boolean', 'ref'])) { | |
| $cwFields[$fieldName] = [ | |
| 'type' => $field['type'], | |
| 'label' => ucfirst($fieldName), | |
| 'required' => $field['required'], | |
| ]; | |
| } | |
| } | |
| return $cwFields; | |
| } | |
| /** | |
| * @param \Mautic\PluginBundle\Integration\Form|FormBuilder $builder | |
| * @param array $data | |
| * @param string $formArea | |
| */ | |
| public function appendToForm(&$builder, $data, $formArea): void | |
| { | |
| if ('features' == $formArea) { | |
| $builder->add( | |
| 'updateBlanks', | |
| ChoiceType::class, | |
| [ | |
| 'choices' => [ | |
| 'mautic.integrations.blanks' => 'updateBlanks', | |
| ], | |
| 'expanded' => true, | |
| 'multiple' => true, | |
| 'label' => 'mautic.integrations.form.blanks', | |
| 'label_attr' => ['class' => 'control-label'], | |
| 'placeholder' => false, | |
| 'required' => false, | |
| ] | |
| ); | |
| $builder->add( | |
| 'objects', | |
| ChoiceType::class, | |
| [ | |
| 'choices' => [ | |
| 'mautic.connectwise.object.contact' => 'Contact', | |
| 'mautic.connectwise.object.company' => 'company', | |
| ], | |
| 'expanded' => true, | |
| 'multiple' => true, | |
| 'label' => 'mautic.connectwise.form.objects_to_pull_from', | |
| 'label_attr' => ['class' => ''], | |
| 'placeholder' => false, | |
| 'required' => false, | |
| ] | |
| ); | |
| } | |
| if ('integration' == $formArea) { | |
| if ($this->isAuthorized()) { | |
| $builder->add( | |
| 'push_activities', | |
| YesNoButtonGroupType::class, | |
| [ | |
| 'label' => 'mautic.plugin.config.push.activities', | |
| 'label_attr' => ['class' => 'control-label'], | |
| 'attr' => [ | |
| 'class' => 'form-control', | |
| ], | |
| 'data' => (!isset($data['push_activities'])) ? true : $data['push_activities'], | |
| 'required' => false, | |
| ] | |
| ); | |
| $builder->add( | |
| 'campaign_task', | |
| IntegrationCampaignsTaskType::class, | |
| [ | |
| 'label' => false, | |
| 'attr' => [ | |
| 'data-hide-on' => '{"campaignevent_properties_config_push_activities_0":"checked"}', | |
| ], | |
| 'data' => $data['campaign_task'] ?? [], | |
| ]); | |
| } | |
| } | |
| } | |
| /** | |
| * @return array of company fields for connectwise | |
| */ | |
| public function getCompanyFields(): array | |
| { | |
| return [ | |
| 'identifier' => ['type' => 'string', 'required' => true], | |
| 'name' => ['type' => 'string', 'required' => true], | |
| 'addressLine1' => ['type' => 'string', 'required' => false], | |
| 'addressLine2' => ['type' => 'string', 'required' => false], | |
| 'city' => ['type' => 'string', 'required' => false], | |
| 'state' => ['type' => 'string', 'required' => false], | |
| 'zip' => ['type' => 'string', 'required' => false], | |
| 'phoneNumber' => ['type' => 'string', 'required' => false], | |
| 'faxNumber' => ['type' => 'string', 'required' => false], | |
| 'website' => ['type' => 'string', 'required' => false], | |
| 'territoryId' => ['type' => 'string', 'required' => false], | |
| 'marketId' => ['type' => 'string', 'required' => false], | |
| 'accountNumber' => ['type' => 'string', 'required' => false], | |
| 'dateAcquired' => ['type' => 'string', 'required' => false], | |
| 'annualRevenue' => ['type' => 'string', 'required' => false], | |
| 'numberOfEmployees' => ['type' => 'string', 'required' => false], | |
| 'leadSource' => ['type' => 'string', 'required' => false], | |
| 'leadFlag' => ['type' => 'boolean', 'required' => false], | |
| 'unsubscribeFlag' => ['type' => 'boolean', 'required' => false], | |
| 'calendarId' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField1' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField2' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField3' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField4' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField5' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField6' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField7' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField8' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField9' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField10' => ['type' => 'string', 'required' => false], | |
| 'vendorIdentifier' => ['type' => 'string', 'required' => false], | |
| 'taxIdentifier' => ['type' => 'string', 'required' => false], | |
| 'invoiceToEmailAddress' => ['type' => 'string', 'required' => false], | |
| 'invoiceCCEmailAddress' => ['type' => 'string', 'required' => false], | |
| 'deletedFlag' => ['type' => 'boolean', 'required' => false], | |
| 'dateDeleted' => ['type' => 'string', 'required' => false], | |
| 'deletedBy' => ['type' => 'string', 'required' => false], | |
| // todo 'customFields' => 'array', | |
| ]; | |
| } | |
| /** | |
| * @return array of contact fields for connectwise | |
| */ | |
| public function getContactFields(): array | |
| { | |
| return [ | |
| 'firstName' => ['type' => 'string', 'required' => true], | |
| 'lastName' => ['type' => 'string', 'required' => false], | |
| 'type' => ['type' => 'string', 'required' => false], | |
| 'company' => ['type' => 'ref', 'required' => false, 'value' => 'name'], | |
| 'addressLine1' => ['type' => 'string', 'required' => false], | |
| 'addressLine2' => ['type' => 'string', 'required' => false], | |
| 'city' => ['type' => 'string', 'required' => false], | |
| 'state' => ['type' => 'string', 'required' => false], | |
| 'zip' => ['type' => 'string', 'required' => false], | |
| 'country' => ['type' => 'string', 'required' => false], | |
| 'inactiveFlag' => ['type' => 'string', 'required' => false], | |
| 'securityIdentifier' => ['type' => 'string', 'required' => false], | |
| 'managerContactId' => ['type' => 'string', 'required' => false], | |
| 'assistantContactId' => ['type' => 'string', 'required' => false], | |
| 'title' => ['type' => 'string', 'required' => false], | |
| 'school' => ['type' => 'string', 'required' => false], | |
| 'nickName' => ['type' => 'string', 'required' => false], | |
| 'marriedFlag' => ['type' => 'boolean', 'required' => false], | |
| 'childrenFlag' => ['type' => 'boolean', 'required' => false], | |
| 'significantOther' => ['type' => 'string', 'required' => false], | |
| 'portalPassword' => ['type' => 'string', 'required' => false], | |
| 'portalSecurityLevel' => ['type' => 'string', 'required' => false], | |
| 'disablePortalLoginFlag' => ['type' => 'boolean', 'required' => false], | |
| 'unsubscribeFlag' => ['type' => 'boolean', 'required' => false], | |
| 'userDefinedField1' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField2' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField3' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField4' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField5' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField6' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField7' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField8' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField9' => ['type' => 'string', 'required' => false], | |
| 'userDefinedField10' => ['type' => 'string', 'required' => false], | |
| 'gender' => ['type' => 'string', 'required' => false], | |
| 'birthDay' => ['type' => 'string', 'required' => false], | |
| 'anniversary' => ['type' => 'string', 'required' => false], | |
| 'presence' => ['type' => 'string', 'required' => false], | |
| 'mobileGuid' => ['type' => 'string', 'required' => false], | |
| 'facebookUrl' => ['type' => 'string', 'required' => false], | |
| 'twitterUrl' => ['type' => 'string', 'required' => false], | |
| 'linkedInUrl' => ['type' => 'string', 'required' => false], | |
| 'defaultBillingFlag' => ['type' => 'boolean', 'required' => false], | |
| 'communicationItems' => [ | |
| 'type' => 'array', | |
| 'required' => false, | |
| 'items' => [ | |
| 'name' => ['type' => 'name'], | |
| 'value' => 'value', | |
| 'keys' => ['Email', 'Direct', 'Fax', 'Cell'], | |
| ], | |
| ], | |
| 'Direct' => ['type' => 'string', 'required' => false, 'configOnly' => true], | |
| 'Cell' => ['type' => 'string', 'required' => false, 'configOnly' => true], | |
| 'Email' => ['type' => 'string', 'required' => true, 'configOnly' => true], | |
| 'Fax' => ['type' => 'string', 'required' => false, 'configOnly' => true], | |
| ]; | |
| } | |
| /** | |
| * Get Contacts from connectwise. | |
| * | |
| * @param array $params | |
| */ | |
| public function getLeads($params = [], $query = null, &$executed = null, $result = [], $object = 'Contact'): int | |
| { | |
| return $this->getRecords($params, $object); | |
| } | |
| /** | |
| * Get Companies from connectwise. | |
| */ | |
| public function getCompanies(array $params = []): int | |
| { | |
| return $this->getRecords($params, 'company'); | |
| } | |
| public function getRecords($params, $object): int | |
| { | |
| if (!$this->isAuthorized()) { | |
| return 0; | |
| } | |
| $page = 1; | |
| $executed = 0; | |
| $integrationEntities = []; | |
| try { | |
| while ($records = ('Contact' == $object) | |
| ? $this->getApiHelper()->getContacts($params, $page) | |
| : $this->getApiHelper()->getCompanies($params, $page)) { | |
| $mauticReferenceObject = ('Contact' == $object) ? 'lead' : 'company'; | |
| foreach ($records as $record) { | |
| if (is_array($record)) { | |
| $id = $record['id']; | |
| $formattedData = $this->amendLeadDataBeforeMauticPopulate($record, $object); | |
| $entity = ('Contact' == $object) | |
| ? $this->getMauticLead($formattedData) | |
| : $this->getMauticCompany( | |
| $formattedData, | |
| 'company' | |
| ); | |
| if ($entity) { | |
| $integrationEntities[] = $this->saveSyncedData($entity, $object, $mauticReferenceObject, $id); | |
| $this->em->detach($entity); | |
| unset($entity); | |
| ++$executed; | |
| } | |
| } | |
| } | |
| if ($integrationEntities) { | |
| $this->em->getRepository(IntegrationEntity::class)->saveEntities($integrationEntities); | |
| $this->integrationEntityModel->getRepository()->detachEntities($integrationEntities); | |
| } | |
| // No use checking the next page if there are less records than the requested page size | |
| if (count($records) < self::PAGESIZE) { | |
| break; | |
| } | |
| ++$page; | |
| } | |
| return $executed; | |
| } catch (\Exception $e) { | |
| if (404 !== $e->getCode()) { | |
| $this->logIntegrationError($e); | |
| } | |
| } | |
| return $executed; | |
| } | |
| /** | |
| * Ammend mapped lead data before creating to Mautic. | |
| * | |
| * @return mixed[] | |
| */ | |
| public function amendLeadDataBeforeMauticPopulate($data, $object): array | |
| { | |
| $fieldsValues = []; | |
| if (empty($data)) { | |
| return $fieldsValues; | |
| } | |
| if ('Contact' == $object) { | |
| $fields = $this->getContactFields(); | |
| } else { | |
| $fields = $this->getCompanyFields(); | |
| } | |
| foreach ($data as $key => $field) { | |
| if (isset($fields[$key])) { | |
| $name = $key; | |
| if ('array' == $fields[$key]['type']) { | |
| $items = $fields[$key]['items']; | |
| foreach ($field as $item) { | |
| if (is_array($item[key($items['name'])])) { | |
| foreach ($item[key($items['name'])] as $nameKey => $nameField) { | |
| if ($nameKey == $items['name'][key($items['name'])]) { | |
| $name = $nameField; | |
| } | |
| } | |
| } | |
| $fieldsValues[$name] = $item[$items['value']]; | |
| } | |
| } elseif ('ref' == $fields[$key]['type']) { | |
| $fieldsValues[$name] = $field[$fields[$key]['value']]; | |
| } else { | |
| $fieldsValues[$name] = $field; | |
| } | |
| } | |
| } | |
| if (isset($data['id'])) { | |
| $fieldsValues['id'] = $data['id']; | |
| } | |
| return $fieldsValues; | |
| } | |
| public function saveSyncedData($entity, $object, $mauticObjectReference, $integrationEntityId): IntegrationEntity | |
| { | |
| /** @var IntegrationEntityRepository $integrationEntityRepo */ | |
| $integrationEntityRepo = $this->em->getRepository(IntegrationEntity::class); | |
| $integrationEntities = $integrationEntityRepo->getIntegrationEntities( | |
| $this->getName(), | |
| $object, | |
| $mauticObjectReference, | |
| [$entity->getId()] | |
| ); | |
| if ($integrationEntities) { | |
| $integrationEntity = reset($integrationEntities); | |
| $integrationEntity->setLastSyncDate(new \DateTime()); | |
| } else { | |
| $integrationEntity = new IntegrationEntity(); | |
| $integrationEntity->setDateAdded(new \DateTime()); | |
| $integrationEntity->setIntegration($this->getName()); | |
| $integrationEntity->setIntegrationEntity($object); | |
| $integrationEntity->setIntegrationEntityId($integrationEntityId); | |
| $integrationEntity->setInternalEntity($mauticObjectReference); | |
| $integrationEntity->setInternalEntityId($entity->getId()); | |
| } | |
| return $integrationEntity; | |
| } | |
| /** | |
| * @param array|Lead $lead | |
| * @param array $config | |
| * | |
| * @throws ApiErrorException | |
| */ | |
| public function pushLead($lead, $config = []): bool | |
| { | |
| $config = $this->mergeConfigToFeatureSettings($config); | |
| $personFound = false; | |
| $leadPushed = false; | |
| $object = 'Contact'; | |
| if (empty($config['leadFields']) || !$lead->getEmail()) { | |
| return $leadPushed; | |
| } | |
| // findLead first | |
| $cwContactExists = $this->getApiHelper()->getContacts(['Email' => $lead->getEmail()]); | |
| if (!empty($cwContactExists)) { | |
| $personFound = true; | |
| } | |
| $personData = []; | |
| try { | |
| if ($personFound) { | |
| foreach ($cwContactExists as $cwContact) { // go through array of contacts found since Connectwise lets you duplicate records with same email address | |
| $mappedData = $this->getMappedFields($object, $lead, $personFound, $config, $cwContact); | |
| if (!empty($mappedData)) { | |
| $personData = $this->getApiHelper()->updateContact($mappedData, $cwContact['id']); | |
| } else { | |
| $personData['id'] = $cwContact['id']; | |
| } | |
| } | |
| } else { | |
| $mappedData = $this->getMappedFields($object, $lead, $personFound, $config); | |
| $personData = $this->getApiHelper()->createContact($mappedData); | |
| } | |
| if (!empty($personData['id'])) { | |
| $id = $personData['id']; | |
| $integrationEntities[] = $this->saveSyncedData($lead, $object, 'lead', $id); | |
| if (isset($config['push_activities']) and true == $config['push_activities']) { | |
| $savedEntity = $this->createActivity($config['campaign_task'], $id, $lead->getId()); | |
| if ($savedEntity instanceof IntegrationEntity) { | |
| $integrationEntities[] = $savedEntity; | |
| } | |
| } | |
| $this->em->getRepository(IntegrationEntity::class)->saveEntities($integrationEntities); | |
| $this->integrationEntityModel->getRepository()->detachEntities($integrationEntities); | |
| $leadPushed = true; | |
| } | |
| } catch (\Exception $e) { | |
| if ($e instanceof ApiErrorException) { | |
| $e->setContact($lead); | |
| } | |
| $this->logIntegrationError($e); | |
| } | |
| return $leadPushed; | |
| } | |
| public function getMappedFields($object, $lead, $personFound, $config, $cwContactData = []): array | |
| { | |
| $fieldsToUpdateInCW = isset($config['update_mautic']) && $personFound ? array_keys($config['update_mautic'], 1) : []; | |
| $objectFields = $this->prepareFieldsForPush($this->getContactFields()); | |
| $leadFields = $config['leadFields']; | |
| $cwContactExists = $this->amendLeadDataBeforeMauticPopulate($cwContactData, $object); | |
| $communicationItems = $cwContactData['communicationItems'] ?? []; | |
| $leadFields = array_diff_key($leadFields, array_flip($fieldsToUpdateInCW)); | |
| $leadFields = $this->getBlankFieldsToUpdate($leadFields, $cwContactExists, $objectFields, $config); | |
| return $this->populateLeadData( | |
| $lead, | |
| [ | |
| 'leadFields' => $leadFields, | |
| 'object' => 'Contact', | |
| 'feature_settings' => [ | |
| 'objects' => $config['objects'], | |
| ], | |
| 'update' => $personFound, | |
| 'communicationItems' => $communicationItems, | |
| ] | |
| ); | |
| } | |
| /** | |
| * Match lead data with integration fields. | |
| */ | |
| public function populateLeadData($lead, $config = []): array | |
| { | |
| if ($lead instanceof Lead) { | |
| $fields = $lead->getFields(true); | |
| } else { | |
| $fields = $lead; | |
| } | |
| $leadFields = $config['leadFields']; | |
| if (empty($leadFields)) { | |
| return []; | |
| } | |
| $availableFields = $this->getContactFields(); | |
| $unknown = $this->translator->trans('mautic.integration.form.lead.unknown'); | |
| $matched = []; | |
| foreach ($availableFields as $key => $field) { | |
| $integrationKey = $matchIntegrationKey = $key; | |
| if (isset($field['configOnly'])) { | |
| continue; | |
| } | |
| if ('communicationItems' == $integrationKey) { | |
| $communicationItems = []; | |
| foreach ($field['items']['keys'] as $keyItem => $item) { | |
| $defaultValue = []; | |
| $keyExists = false; | |
| if (isset($leadFields[$item])) { | |
| if ('Email' == $item) { | |
| $defaultValue = ['defaultFlag' => true]; | |
| } | |
| $mauticKey = $leadFields[$item]; | |
| if (isset($fields[$mauticKey]) && !empty($fields[$mauticKey]['value'])) { | |
| foreach ($config['communicationItems'] as $key => $ci) { | |
| if ($ci['type']['id'] == $keyItem + 1) { | |
| $config['communicationItems'][$key]['value'] = $fields[$mauticKey]['value']; | |
| $keyExists = true; | |
| } | |
| } | |
| if (!$keyExists) { | |
| $type = [ | |
| 'type' => ['id' => $keyItem + 1, 'name' => $item], ]; | |
| $values = array_merge(['value' => $this->cleanPushData($fields[$mauticKey]['value'])], $defaultValue); | |
| $communicationItems[] = array_merge($type, $values); | |
| } | |
| } | |
| } | |
| } | |
| if ($config['update']) { | |
| $communicationItems = array_merge($config['communicationItems'], $communicationItems); | |
| } | |
| if (!empty($communicationItems)) { | |
| $matched[$integrationKey] = $communicationItems; | |
| } | |
| } | |
| if ('company' === $integrationKey && !empty($fields['company']['value'])) { | |
| try { | |
| $foundCompanies = $this->getApiHelper()->getCompanies([ | |
| 'conditions' => [ | |
| sprintf('Name = "%s"', $fields['company']['value']), | |
| ], | |
| ]); | |
| $matched['company'] = ['identifier' => $foundCompanies[0]['identifier']]; | |
| } catch (ApiErrorException) { | |
| // No matching companies were found | |
| } | |
| continue; | |
| } | |
| if (isset($leadFields[$integrationKey])) { | |
| $mauticKey = $leadFields[$integrationKey]; | |
| if (isset($fields[$mauticKey]) && !empty($fields[$mauticKey]['value'])) { | |
| $matched[$matchIntegrationKey] = $this->cleanPushData($fields[$mauticKey]['value']); | |
| } | |
| } | |
| if (!empty($field['required']) && empty($matched[$matchIntegrationKey]) && !$config['update']) { | |
| $matched[$matchIntegrationKey] = $unknown; | |
| } | |
| } | |
| if ($config['update']) { | |
| $updateFields = []; | |
| foreach ($matched as $key => $field) { | |
| $updateFields[] = [ | |
| 'op' => 'replace', | |
| 'path' => $key, | |
| 'value' => $field, | |
| ]; | |
| } | |
| $matched = $updateFields; | |
| } | |
| return $matched; | |
| } | |
| /** | |
| * @param array $objects | |
| * | |
| * @return array | |
| */ | |
| protected function cleanPriorityFields($fieldsToUpdate, $objects = null) | |
| { | |
| if (null === $objects) { | |
| $objects = ['Leads', 'Contacts']; | |
| } | |
| if (isset($fieldsToUpdate['leadFields']) && is_array($objects)) { | |
| // Pass in the whole config | |
| $fields = $fieldsToUpdate['leadFields']; | |
| } else { | |
| $fields = array_flip($fieldsToUpdate); | |
| } | |
| return $this->prepareFieldsForSync($fields, $fieldsToUpdate, $objects); | |
| } | |
| /** | |
| * @param string $priorityObject | |
| * | |
| * @return mixed | |
| */ | |
| protected function getPriorityFieldsForMautic($config, $object = null, $priorityObject = 'mautic') | |
| { | |
| if ('company' == $object) { | |
| $priority = parent::getPriorityFieldsForMautic($config, $object, 'mautic_company'); | |
| $fields = array_intersect_key($config['companyFields'], $priority); | |
| } else { | |
| $fields = parent::getPriorityFieldsForMautic($config, $object, $priorityObject); | |
| } | |
| return ($object && isset($fields[$object])) ? $fields[$object] : $fields; | |
| } | |
| /** | |
| * @return array | |
| * | |
| * @throws \Exception | |
| */ | |
| public function getCampaigns() | |
| { | |
| $campaigns = []; | |
| try { | |
| $campaigns = $this->getApiHelper()->getCampaigns(); | |
| } catch (\Exception $e) { | |
| $this->logIntegrationError($e); | |
| } | |
| return $campaigns; | |
| } | |
| /** | |
| * @throws \Exception | |
| */ | |
| public function getCampaignChoices(): array | |
| { | |
| $choices = []; | |
| $campaigns = $this->getCampaigns(); | |
| foreach ($campaigns as $campaign) { | |
| if (isset($campaign['id'])) { | |
| $choices[] = [ | |
| 'value' => $campaign['id'], | |
| 'label' => $campaign['name'], | |
| ]; | |
| } | |
| } | |
| return $choices; | |
| } | |
| public function getCampaignMembers($campaignId): bool | |
| { | |
| if (!$this->isAuthorized()) { | |
| return false; | |
| } | |
| try { | |
| $page = 1; | |
| while ($campaignsMembersResults = $this->getApiHelper()->getCampaignMembers($campaignId, $page)) { | |
| $campaignMemberObject = new IntegrationObject('CampaignMember', 'lead'); | |
| $recordList = $this->getRecordList($campaignsMembersResults, 'id'); | |
| $contacts = (array) $this->integrationEntityModel->getSyncedRecords(new IntegrationObject('Contact', 'lead'), $this->getName(), $recordList); | |
| $existingContactsIds = array_column(array_filter( | |
| $contacts, | |
| fn ($contact): bool => 'lead' === $contact['internal_entity'] | |
| ), 'integration_entity_id'); | |
| $contactsToFetch = array_diff_key($recordList, array_flip($existingContactsIds)); | |
| if (!empty($contactsToFetch)) { | |
| $listOfContactsToFetch = implode(',', array_keys($contactsToFetch)); | |
| $params['Ids'] = $listOfContactsToFetch; | |
| $this->getLeads($params); | |
| } | |
| $saveCampaignMembers = array_merge($existingContactsIds, array_keys($contactsToFetch)); | |
| $this->saveCampaignMembers($saveCampaignMembers, $campaignMemberObject, $campaignId); | |
| if (count($campaignsMembersResults) < self::PAGESIZE) { | |
| // No use continuing as we have less results than page size | |
| break; | |
| } | |
| ++$page; | |
| } | |
| return true; | |
| } catch (\Exception $e) { | |
| if (404 !== $e->getCode()) { | |
| $this->logIntegrationError($e); | |
| } | |
| } | |
| return false; | |
| } | |
| public function saveCampaignMembers($allCampaignMembers, $campaignMemberObject, $campaignId): void | |
| { | |
| if (empty($allCampaignMembers)) { | |
| return; | |
| } | |
| $persistEntities = []; | |
| $recordList = $this->getRecordList($allCampaignMembers); | |
| $mauticObject = new IntegrationObject('Contact', 'lead'); | |
| $contacts = $this->integrationEntityModel->getSyncedRecords($mauticObject, $this->getName(), $recordList); | |
| // first find existing campaign members. | |
| foreach ($contacts as $contact) { | |
| $existingCampaignMember = $this->integrationEntityModel->getSyncedRecords($campaignMemberObject, $this->getName(), $campaignId, $contact['internal_entity_id']); | |
| if (empty($existingCampaignMember)) { | |
| $persistEntities[] = $this->createIntegrationEntity( | |
| $campaignMemberObject->getType(), | |
| $campaignId, | |
| $campaignMemberObject->getInternalType(), | |
| $contact['internal_entity_id'], | |
| [], | |
| false | |
| ); | |
| } | |
| } | |
| if ($persistEntities) { | |
| $this->em->getRepository(IntegrationEntity::class)->saveEntities($persistEntities); | |
| $this->integrationEntityModel->getRepository()->detachEntities($persistEntities); | |
| unset($persistEntities); | |
| } | |
| } | |
| public function getRecordList($records, $index = null): array | |
| { | |
| $recordList = []; | |
| foreach ($records as $record) { | |
| if ($index && isset($record[$index])) { | |
| $record = $record[$index]; | |
| } | |
| $recordList[$record] = [ | |
| 'id' => $record, | |
| ]; | |
| } | |
| return $recordList; | |
| } | |
| /** | |
| * @throws ApiErrorException | |
| */ | |
| public function getActivityTypes(): array | |
| { | |
| $activities = []; | |
| $cwActivities = $this->getApiHelper()->getActivityTypes(); | |
| foreach ($cwActivities as $cwActivity) { | |
| if (isset($cwActivity['id'])) { | |
| $activities[$cwActivity['id']] = $cwActivity['name']; | |
| } | |
| } | |
| return $activities; | |
| } | |
| /** | |
| * @throws ApiErrorException | |
| */ | |
| public function getMembers(): array | |
| { | |
| $members = []; | |
| $cwMembers = $this->getApiHelper()->getMembers(); | |
| foreach ($cwMembers as $cwMember) { | |
| if (isset($cwMember['id'])) { | |
| $members[$cwMember['id']] = $cwMember['identifier']; | |
| } | |
| } | |
| return $members; | |
| } | |
| /** | |
| * @throws ApiErrorException | |
| */ | |
| public function createActivity($config, $cwContactId, $leadId): ?IntegrationEntity | |
| { | |
| if ($cwContactId and !empty($config['activity_name'])) { | |
| $activity = [ | |
| 'name' => $config['activity_name'], | |
| 'type' => ['id' => $config['campaign_activity_type']], | |
| 'assignTo' => ['id' => $config['campaign_members']], | |
| 'contact' => ['id' => $cwContactId], | |
| ]; | |
| $activities = $this->getApiHelper()->postActivity($activity); | |
| if (isset($activities['id'])) { | |
| return $this->createIntegrationEntity('Activities', $activities['id'], 'lead', $leadId, null, false); | |
| } | |
| } | |
| return null; | |
| } | |
| } | |