Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# Workflow name:
|
||||
name: Close Pull Requests
|
||||
|
||||
# Workflow triggers:
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
# Workflow jobs:
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: superbrothers/close-pull-request@v3
|
||||
with:
|
||||
comment: |
|
||||
Thank you for submitting a pull request. :raised_hands:
|
||||
|
||||
We greatly appreciate your willingness to submit a contribution. However, we are not accepting pull requests against this repository, as all development happens on the [main project repository](https://github.com/mautic/mautic).
|
||||
|
||||
We kindly request that you submit this pull request against the [respective directory](https://github.com/mautic/mautic/blob/head/plugins/MauticClearbitBundle) of the main repository where we'll review and provide feedback. If this is your first Mautic contribution, be sure to read the [contributing guide](https://github.com/mautic/mautic/blob/4.x/.github/CONTRIBUTING.md) which provides guidelines and instructions for submitting contributions.
|
||||
|
||||
Thank you again, and we look forward to receiving your contribution! :smiley:
|
||||
|
||||
Best,
|
||||
The Mautic team
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.1 KiB |
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'name' => 'Clearbit',
|
||||
'description' => 'Enables integration with Clearbit for contact and company lookup',
|
||||
'version' => '1.0',
|
||||
'author' => 'Werner Garcia',
|
||||
|
||||
'routes' => [
|
||||
'public' => [
|
||||
'mautic_plugin_clearbit_index' => [
|
||||
'path' => '/clearbit/callback',
|
||||
'controller' => 'MauticPlugin\MauticClearbitBundle\Controller\PublicController::callbackAction',
|
||||
],
|
||||
],
|
||||
'main' => [
|
||||
'mautic_plugin_clearbit_action' => [
|
||||
'path' => '/clearbit/{objectAction}/{objectId}',
|
||||
'controller' => 'MauticPlugin\MauticClearbitBundle\Controller\ClearbitController::executeAction',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'services' => [
|
||||
'others' => [
|
||||
'mautic.plugin.clearbit.lookup_helper' => [
|
||||
'class' => MauticPlugin\MauticClearbitBundle\Helper\LookupHelper::class,
|
||||
'arguments' => [
|
||||
'mautic.helper.integration',
|
||||
'mautic.helper.user',
|
||||
'monolog.logger.mautic',
|
||||
'mautic.lead.model.lead',
|
||||
'mautic.lead.model.company',
|
||||
],
|
||||
],
|
||||
],
|
||||
'integrations' => [
|
||||
'mautic.integration.clearbit' => [
|
||||
'class' => MauticPlugin\MauticClearbitBundle\Integration\ClearbitIntegration::class,
|
||||
'arguments' => [
|
||||
'event_dispatcher',
|
||||
'mautic.helper.cache_storage',
|
||||
'doctrine.orm.entity_manager',
|
||||
'request_stack',
|
||||
'router',
|
||||
'translator',
|
||||
'monolog.logger.mautic',
|
||||
'mautic.helper.encryption',
|
||||
'mautic.lead.model.lead',
|
||||
'mautic.lead.model.company',
|
||||
'mautic.helper.paths',
|
||||
'mautic.core.model.notification',
|
||||
'mautic.lead.model.field',
|
||||
'mautic.plugin.model.integration_entity',
|
||||
'mautic.lead.model.dnc',
|
||||
'mautic.lead.field.fields_with_unique_identifier',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Mautic\CoreBundle\DependencyInjection\MauticCoreExtension;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return function (ContainerConfigurator $configurator): void {
|
||||
$services = $configurator->services()
|
||||
->defaults()
|
||||
->autowire()
|
||||
->autoconfigure()
|
||||
->public();
|
||||
|
||||
$excludes = [
|
||||
'Services',
|
||||
];
|
||||
|
||||
$services->load('MauticPlugin\\MauticClearbitBundle\\', '../')
|
||||
->exclude('../{'.implode(',', array_merge(MauticCoreExtension::DEFAULT_EXCLUDES, $excludes)).'}');
|
||||
};
|
||||
@@ -0,0 +1,520 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\Controller;
|
||||
|
||||
use Mautic\FormBundle\Controller\FormController;
|
||||
use Mautic\LeadBundle\Entity\Company;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use MauticPlugin\MauticClearbitBundle\Form\Type\BatchLookupType;
|
||||
use MauticPlugin\MauticClearbitBundle\Form\Type\LookupType;
|
||||
use MauticPlugin\MauticClearbitBundle\Helper\LookupHelper;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class ClearbitController extends FormController
|
||||
{
|
||||
/**
|
||||
* @param string $objectId
|
||||
*
|
||||
* @return JsonResponse
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function lookupPersonAction(Request $request, LookupHelper $lookupHelper, $objectId = '')
|
||||
{
|
||||
if ('POST' === $request->getMethod()) {
|
||||
$data = $request->request->all()['clearbit_lookup'] ?? [];
|
||||
$objectId = $data['objectId'];
|
||||
}
|
||||
/** @var \Mautic\LeadBundle\Model\LeadModel $model */
|
||||
$model = $this->getModel('lead');
|
||||
$lead = $model->getEntity($objectId);
|
||||
|
||||
if (!$this->security->hasEntityAccess(
|
||||
'lead:leads:editown',
|
||||
'lead:leads:editother',
|
||||
$lead->getPermissionUser()
|
||||
)
|
||||
) {
|
||||
$this->addFlashMessage(
|
||||
$this->translator->trans('mautic.plugin.clearbit.forbidden'),
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'closeModal' => true,
|
||||
'flashes' => $this->getFlashContent(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
if ('GET' === $request->getMethod()) {
|
||||
$route = $this->generateUrl(
|
||||
'mautic_plugin_clearbit_action',
|
||||
[
|
||||
'objectAction' => 'lookupPerson',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'form' => $this->createForm(
|
||||
LookupType::class,
|
||||
[
|
||||
'objectId' => $objectId,
|
||||
],
|
||||
[
|
||||
'action' => $route,
|
||||
]
|
||||
)->createView(),
|
||||
'lookupItem' => $lead->getEmail(),
|
||||
],
|
||||
'contentTemplate' => '@MauticClearbit/Clearbit/lookup.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_contact_index',
|
||||
'mauticContent' => 'lead',
|
||||
'route' => $route,
|
||||
],
|
||||
]
|
||||
);
|
||||
} else {
|
||||
if ('POST' === $request->getMethod()) {
|
||||
try {
|
||||
$lookupHelper->lookupContact($lead, array_key_exists('notify', $data));
|
||||
$this->addFlashMessage(
|
||||
'mautic.lead.batch_leads_affected',
|
||||
[
|
||||
'%count%' => 1,
|
||||
]
|
||||
);
|
||||
} catch (\Exception $ex) {
|
||||
$this->addFlashMessage(
|
||||
$ex->getMessage(),
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
}
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'closeModal' => true,
|
||||
'flashes' => $this->getFlashContent(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response('Bad Request', 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JsonResponse
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function batchLookupPersonAction(Request $request, LookupHelper $lookupHelper)
|
||||
{
|
||||
/** @var \Mautic\LeadBundle\Model\LeadModel $model */
|
||||
$model = $this->getModel('lead');
|
||||
if ('GET' === $request->getMethod()) {
|
||||
$data = $request->query->all()['clearbit_batch_lookup'] ?? [];
|
||||
} else {
|
||||
$data = $request->request->all()['clearbit_batch_lookup'] ?? [];
|
||||
}
|
||||
|
||||
$entities = [];
|
||||
if (array_key_exists('ids', $data)) {
|
||||
$ids = $data['ids'];
|
||||
|
||||
if (!is_array($ids)) {
|
||||
$ids = json_decode($ids, true);
|
||||
}
|
||||
|
||||
if (is_array($ids) && count($ids)) {
|
||||
$entities = $model->getEntities(
|
||||
[
|
||||
'filter' => [
|
||||
'force' => [
|
||||
[
|
||||
'column' => 'l.id',
|
||||
'expr' => 'in',
|
||||
'value' => $ids,
|
||||
],
|
||||
],
|
||||
],
|
||||
'ignore_paginator' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$lookupEmails = [];
|
||||
if ($count = count($entities)) {
|
||||
/** @var Lead $lead */
|
||||
foreach ($entities as $lead) {
|
||||
if ($this->security->hasEntityAccess(
|
||||
'lead:leads:editown',
|
||||
'lead:leads:editother',
|
||||
$lead->getPermissionUser()
|
||||
)
|
||||
&& $lead->getEmail()
|
||||
) {
|
||||
$lookupEmails[$lead->getId()] = $lead->getEmail();
|
||||
}
|
||||
}
|
||||
|
||||
$count = count($lookupEmails);
|
||||
}
|
||||
|
||||
if (0 === $count) {
|
||||
$this->addFlashMessage(
|
||||
$this->translator->trans('mautic.plugin.clearbit.empty'),
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'closeModal' => true,
|
||||
'flashes' => $this->getFlashContent(),
|
||||
]
|
||||
);
|
||||
} else {
|
||||
if ($count > 20) {
|
||||
$this->addFlashMessage(
|
||||
$this->translator->trans('mautic.plugin.clearbit.toomany'),
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'closeModal' => true,
|
||||
'flashes' => $this->getFlashContent(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
if ('GET' === $request->getMethod()) {
|
||||
$route = $this->generateUrl(
|
||||
'mautic_plugin_clearbit_action',
|
||||
[
|
||||
'objectAction' => 'batchLookupPerson',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'form' => $this->createForm(
|
||||
BatchLookupType::class,
|
||||
[],
|
||||
[
|
||||
'action' => $route,
|
||||
]
|
||||
)->createView(),
|
||||
'lookupItems' => array_values($lookupEmails),
|
||||
],
|
||||
'contentTemplate' => '@MauticClearbit/Clearbit/batchLookup.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_contact_index',
|
||||
'mauticContent' => 'leadBatch',
|
||||
'route' => $route,
|
||||
],
|
||||
]
|
||||
);
|
||||
} else {
|
||||
if ('POST' === $request->getMethod()) {
|
||||
$notify = array_key_exists('notify', $data);
|
||||
foreach ($lookupEmails as $id => $lookupEmail) {
|
||||
if ($lead = $model->getEntity($id)) {
|
||||
try {
|
||||
$lookupHelper->lookupContact($lead, $notify);
|
||||
} catch (\Exception $ex) {
|
||||
$this->addFlashMessage(
|
||||
$ex->getMessage(),
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
--$count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($count) {
|
||||
$this->addFlashMessage(
|
||||
'mautic.lead.batch_leads_affected',
|
||||
[
|
||||
'%count%' => $count,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'closeModal' => true,
|
||||
'flashes' => $this->getFlashContent(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response('Bad Request', 400);
|
||||
}
|
||||
|
||||
/***************** COMPANY ***********************/
|
||||
|
||||
/**
|
||||
* @param string $objectId
|
||||
*
|
||||
* @return JsonResponse
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function lookupCompanyAction(Request $request, LookupHelper $lookupHelper, $objectId = '')
|
||||
{
|
||||
if ('POST' === $request->getMethod()) {
|
||||
$data = $request->request->all()['clearbit_lookup'] ?? [];
|
||||
$objectId = $data['objectId'];
|
||||
}
|
||||
/** @var \Mautic\LeadBundle\Model\CompanyModel $model */
|
||||
$model = $this->getModel('lead.company');
|
||||
/** @var Company $company */
|
||||
$company = $model->getEntity($objectId);
|
||||
|
||||
if ('GET' === $request->getMethod()) {
|
||||
$route = $this->generateUrl(
|
||||
'mautic_plugin_clearbit_action',
|
||||
[
|
||||
'objectAction' => 'lookupCompany',
|
||||
]
|
||||
);
|
||||
|
||||
$website = $company->getFieldValue('companywebsite');
|
||||
|
||||
if (!$website) {
|
||||
$this->addFlashMessage(
|
||||
$this->translator->trans('mautic.plugin.clearbit.compempty'),
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'closeModal' => true,
|
||||
'flashes' => $this->getFlashContent(),
|
||||
]
|
||||
);
|
||||
}
|
||||
$parse = parse_url($website);
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'form' => $this->createForm(
|
||||
LookupType::class,
|
||||
[
|
||||
'objectId' => $objectId,
|
||||
],
|
||||
[
|
||||
'action' => $route,
|
||||
]
|
||||
)->createView(),
|
||||
'lookupItem' => $parse['host'],
|
||||
],
|
||||
'contentTemplate' => '@MauticClearbit/Clearbit/lookup.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_company_index',
|
||||
'mauticContent' => 'company',
|
||||
'route' => $route,
|
||||
],
|
||||
]
|
||||
);
|
||||
} else {
|
||||
if ('POST' === $request->getMethod()) {
|
||||
try {
|
||||
$lookupHelper->lookupCompany($company, array_key_exists('notify', $data));
|
||||
$this->addFlashMessage(
|
||||
'mautic.company.batch_companies_affected',
|
||||
[
|
||||
'%count%' => 1,
|
||||
]
|
||||
);
|
||||
} catch (\Exception $ex) {
|
||||
$this->addFlashMessage(
|
||||
$ex->getMessage(),
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
}
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'closeModal' => true,
|
||||
'flashes' => $this->getFlashContent(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response('Bad Request', 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return JsonResponse
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function batchLookupCompanyAction(Request $request, LookupHelper $lookupHelper)
|
||||
{
|
||||
/** @var \Mautic\LeadBundle\Model\CompanyModel $model */
|
||||
$model = $this->getModel('lead.company');
|
||||
if ('GET' === $request->getMethod()) {
|
||||
$data = $request->query->all()['clearbit_batch_lookup'] ?? [];
|
||||
} else {
|
||||
$data = $request->request->all()['clearbit_batch_lookup'] ?? [];
|
||||
}
|
||||
|
||||
$entities = [];
|
||||
if (array_key_exists('ids', $data)) {
|
||||
$ids = $data['ids'];
|
||||
|
||||
if (!is_array($ids)) {
|
||||
$ids = json_decode($ids, true);
|
||||
}
|
||||
|
||||
if (is_array($ids) && count($ids)) {
|
||||
$entities = $model->getEntities(
|
||||
[
|
||||
'filter' => [
|
||||
'force' => [
|
||||
[
|
||||
'column' => 'comp.id',
|
||||
'expr' => 'in',
|
||||
'value' => $ids,
|
||||
],
|
||||
],
|
||||
],
|
||||
'ignore_paginator' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$lookupWebsites = [];
|
||||
if ($count = count($entities)) {
|
||||
/** @var Company $company */
|
||||
foreach ($entities as $company) {
|
||||
if ($company->getFieldValue('companywebsite')) {
|
||||
$website = $company->getFieldValue('companywebsite');
|
||||
$parse = parse_url($website);
|
||||
if (!isset($parse['host'])) {
|
||||
continue;
|
||||
}
|
||||
$lookupWebsites[$company->getId()] = $parse['host'];
|
||||
}
|
||||
}
|
||||
|
||||
$count = count($lookupWebsites);
|
||||
}
|
||||
|
||||
if (0 === $count) {
|
||||
$this->addFlashMessage(
|
||||
$this->translator->trans('mautic.plugin.clearbit.compempty'),
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'closeModal' => true,
|
||||
'flashes' => $this->getFlashContent(),
|
||||
]
|
||||
);
|
||||
} else {
|
||||
if ($count > 20) {
|
||||
$this->addFlashMessage(
|
||||
$this->translator->trans('mautic.plugin.clearbit.comptoomany'),
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'closeModal' => true,
|
||||
'flashes' => $this->getFlashContent(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
if ('GET' === $request->getMethod()) {
|
||||
$route = $this->generateUrl(
|
||||
'mautic_plugin_clearbit_action',
|
||||
[
|
||||
'objectAction' => 'batchLookupCompany',
|
||||
]
|
||||
);
|
||||
|
||||
return $this->delegateView(
|
||||
[
|
||||
'viewParameters' => [
|
||||
'form' => $this->createForm(
|
||||
BatchLookupType::class,
|
||||
[],
|
||||
[
|
||||
'action' => $route,
|
||||
]
|
||||
)->createView(),
|
||||
'lookupItems' => array_values($lookupWebsites),
|
||||
],
|
||||
'contentTemplate' => '@MauticClearbit/Clearbit/batchLookup.html.twig',
|
||||
'passthroughVars' => [
|
||||
'activeLink' => '#mautic_company_index',
|
||||
'mauticContent' => 'companyBatch',
|
||||
'route' => $route,
|
||||
],
|
||||
]
|
||||
);
|
||||
} else {
|
||||
if ('POST' === $request->getMethod()) {
|
||||
$notify = array_key_exists('notify', $data);
|
||||
foreach ($lookupWebsites as $id => $lookupWebsite) {
|
||||
if ($company = $model->getEntity($id)) {
|
||||
try {
|
||||
$lookupHelper->lookupCompany($company, $notify);
|
||||
} catch (\Exception $ex) {
|
||||
$this->addFlashMessage(
|
||||
$ex->getMessage(),
|
||||
[],
|
||||
'error'
|
||||
);
|
||||
--$count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($count) {
|
||||
$this->addFlashMessage(
|
||||
'mautic.company.batch_companies_affected',
|
||||
[
|
||||
'%count%' => $count,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
return new JsonResponse(
|
||||
[
|
||||
'closeModal' => true,
|
||||
'flashes' => $this->getFlashContent(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response('Bad Request', 400);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\Controller;
|
||||
|
||||
use Mautic\FormBundle\Controller\FormController;
|
||||
use Mautic\LeadBundle\Entity\Company;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\UserBundle\Entity\User;
|
||||
use Mautic\UserBundle\Model\UserModel;
|
||||
use MauticPlugin\MauticClearbitBundle\Helper\LookupHelper;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class PublicController extends FormController
|
||||
{
|
||||
/**
|
||||
* Write a notification.
|
||||
*
|
||||
* @param string $message Message of the notification
|
||||
* @param string $header Header for message
|
||||
* @param string $iconClass CSS class for the icon (e.g. ri-eye-line)
|
||||
* @param User|null $user User object; defaults to current user
|
||||
*/
|
||||
public function addNewNotification($message, $header, $iconClass, User $user): void
|
||||
{
|
||||
/** @var \Mautic\CoreBundle\Model\NotificationModel $notificationModel */
|
||||
$notificationModel = $this->getModel('core.notification');
|
||||
$notificationModel->addNotification($message, 'FullContact', false, $header, $iconClass, null, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function callbackAction(Request $request, LoggerInterface $mauticLogger, LookupHelper $lookupHelper): Response
|
||||
{
|
||||
if (!$request->request->has('body') || !$request->request->has('id')
|
||||
|| !$request->request->has('type')
|
||||
|| !$request->request->has('status')
|
||||
|| 200 !== $request->request->get('status')
|
||||
) {
|
||||
$mauticLogger->log('error', 'ERROR on Clearbit callback: Malformed request variables: '.json_encode($request->request->all(), JSON_PRETTY_PRINT));
|
||||
|
||||
return new Response('ERROR');
|
||||
}
|
||||
|
||||
/** @var array $result */
|
||||
$result = $request->request->all()['body'] ?? [];
|
||||
$oid = $request->request->get('id');
|
||||
$validatedRequest = $lookupHelper->validateRequest($oid, $request->request->get('type'));
|
||||
|
||||
if (!$validatedRequest || !is_array($result)) {
|
||||
$mauticLogger->log('error', 'ERROR on Clearbit callback: Wrong body or id in request: id='.$oid.' body='.json_encode($result, JSON_PRETTY_PRINT));
|
||||
|
||||
return new Response('ERROR');
|
||||
}
|
||||
|
||||
$notify = $validatedRequest['notify'];
|
||||
|
||||
try {
|
||||
if ('person' === $request->request->get('type')) {
|
||||
/** @var \Mautic\LeadBundle\Model\LeadModel $model */
|
||||
$model = $this->getModel('lead');
|
||||
/** @var Lead $lead */
|
||||
$lead = $validatedRequest['entity'];
|
||||
$currFields = $lead->getFields(true);
|
||||
$mauticLogger->log('debug', 'CURRFIELDS: '.var_export($currFields, true));
|
||||
|
||||
$loc = [];
|
||||
if (array_key_exists('geo', $result)) {
|
||||
$loc = $result['geo'];
|
||||
}
|
||||
|
||||
$data = [];
|
||||
|
||||
foreach ([
|
||||
'facebook' => 'http://www.facebook.com/',
|
||||
'linkedin' => 'http://www.linkedin.com/',
|
||||
'twitter' => 'http://www.twitter.com/',
|
||||
] as $p => $u) {
|
||||
foreach ($result as $type => $socialProfile) {
|
||||
if ($type === $p && empty($currFields[$p]['value'])) {
|
||||
$data[$p] = (array_key_exists('handle', $socialProfile) && $socialProfile['handle']) ? $u.$socialProfile['handle'] : '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (array_key_exists('name', $result)
|
||||
&& array_key_exists(
|
||||
'familyName',
|
||||
$result['name']
|
||||
)
|
||||
&& empty($currFields['lastname']['value'])
|
||||
) {
|
||||
$data['lastname'] = $result['name']['familyName'];
|
||||
}
|
||||
|
||||
if (array_key_exists('name', $result)
|
||||
&& array_key_exists(
|
||||
'givenName',
|
||||
$result['name']
|
||||
)
|
||||
&& empty($currFields['firstname']['value'])
|
||||
) {
|
||||
$data['firstname'] = $result['name']['givenName'];
|
||||
}
|
||||
|
||||
if (array_key_exists('site', $result) && empty($currFields['website']['value'])) {
|
||||
$data['website'] = $result['site'];
|
||||
}
|
||||
|
||||
if (array_key_exists('employment', $result)
|
||||
&& array_key_exists(
|
||||
'name',
|
||||
$result['employment']
|
||||
)
|
||||
&& empty($currFields['company']['value'])
|
||||
) {
|
||||
$data['company'] = $result['employment']['name'];
|
||||
}
|
||||
|
||||
if (array_key_exists('employment', $result)
|
||||
&& array_key_exists(
|
||||
'title',
|
||||
$result['employment']
|
||||
)
|
||||
&& empty($currFields['position']['value'])
|
||||
) {
|
||||
$data['position'] = $result['employment']['title'];
|
||||
}
|
||||
|
||||
if (array_key_exists('city', $loc) && empty($currFields['city']['value'])) {
|
||||
$data['city'] = $loc['city'];
|
||||
}
|
||||
if (array_key_exists('state', $loc) && empty($currFields['state']['value'])) {
|
||||
$data['state'] = $loc['state'];
|
||||
}
|
||||
|
||||
if (array_key_exists('country', $loc) && empty($currFields['country']['value'])) {
|
||||
$data['country'] = $loc['country'];
|
||||
}
|
||||
|
||||
$mauticLogger->log('debug', 'SETTING FIELDS: '.print_r($data, true));
|
||||
|
||||
// Unset the nonce so that it's not used again
|
||||
$socialCache = $lead->getSocialCache();
|
||||
unset($socialCache['clearbit']['nonce']);
|
||||
$lead->setSocialCache($socialCache);
|
||||
|
||||
$model->setFieldValues($lead, $data);
|
||||
$model->saveEntity($lead);
|
||||
|
||||
if ($notify && (!isset($lead->imported) || !$lead->imported)) {
|
||||
/** @var UserModel $userModel */
|
||||
$userModel = $this->getModel('user');
|
||||
if ($user = $userModel->getEntity($notify)) {
|
||||
$this->addNewNotification(
|
||||
sprintf($this->translator->trans('mautic.plugin.clearbit.contact_retrieved'), $lead->getEmail()),
|
||||
'Clearbit Plugin',
|
||||
'ri-search-line',
|
||||
$user
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/****************** COMPANY STUFF *********************/
|
||||
|
||||
if ('company' === $request->request->get('type')) {
|
||||
/** @var \Mautic\LeadBundle\Model\CompanyModel $model */
|
||||
$model = $this->getModel('lead.company');
|
||||
/** @var Company $company */
|
||||
$company = $validatedRequest['entity'];
|
||||
$currFields = $company->getFields(true);
|
||||
|
||||
$loc = [];
|
||||
if (array_key_exists('geo', $result)) {
|
||||
$loc = $result['geo'];
|
||||
}
|
||||
|
||||
$data = [];
|
||||
|
||||
if (array_key_exists('streetNumber', $loc)
|
||||
&& array_key_exists(
|
||||
'streetName',
|
||||
$loc
|
||||
)
|
||||
&& empty($currFields['companyaddress1']['value'])
|
||||
) {
|
||||
$data['companyaddress1'] = $loc['streetNumber'].' '.$loc['streetName'];
|
||||
}
|
||||
|
||||
if (array_key_exists('city', $loc) && empty($currFields['companycity']['value'])) {
|
||||
$data['companycity'] = $loc['city'];
|
||||
}
|
||||
|
||||
if (array_key_exists('metrics', $result)
|
||||
&& array_key_exists(
|
||||
'employees',
|
||||
$result['metrics']
|
||||
)
|
||||
&& empty($currFields['companynumber_of_employees']['value'])
|
||||
) {
|
||||
$data['companynumber_of_employees'] = $result['metrics']['employees'];
|
||||
}
|
||||
|
||||
if (array_key_exists('description', $result) && empty($currFields['companydescription']['value'])) {
|
||||
$data['companydescription'] = $result['description'];
|
||||
}
|
||||
|
||||
if (array_key_exists('phone', $result) && empty($currFields['companyphone']['value'])) {
|
||||
$data['companyphone'] = $result['phone'];
|
||||
}
|
||||
|
||||
if (array_key_exists('site', $result)
|
||||
&& array_key_exists(
|
||||
'emailAddresses',
|
||||
$result['site']
|
||||
)
|
||||
&& count($result['site']['emailAddresses'])
|
||||
&& empty($currFields['companyemail']['value'])
|
||||
) {
|
||||
$data['companyemail'] = $result['site']['emailAddresses'][0];
|
||||
}
|
||||
|
||||
if (array_key_exists('country', $loc) && empty($currFields['companycountry']['value'])) {
|
||||
$data['companycountry'] = $loc['country'];
|
||||
}
|
||||
|
||||
if (array_key_exists('state', $loc) && empty($currFields['companystate']['value'])) {
|
||||
$data['companystate'] = $loc['state'];
|
||||
}
|
||||
|
||||
$mauticLogger->log('debug', 'SETTING FIELDS: '.print_r($data, true));
|
||||
|
||||
// Unset the nonce so that it's not used again
|
||||
$socialCache = $company->getSocialCache();
|
||||
unset($socialCache['clearbit']['nonce']);
|
||||
$company->setSocialCache($socialCache);
|
||||
|
||||
$model->setFieldValues($company, $data);
|
||||
$model->saveEntity($company);
|
||||
|
||||
if ($notify) {
|
||||
/** @var UserModel $userModel */
|
||||
$userModel = $this->getModel('user');
|
||||
if ($user = $userModel->getEntity($notify)) {
|
||||
$this->addNewNotification(
|
||||
sprintf($this->translator->trans('mautic.plugin.clearbit.company_retrieved'), $company->getName()),
|
||||
'Clearbit Plugin',
|
||||
'ri-search-line',
|
||||
$user
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$mauticLogger->log('error', 'ERROR on Clearbit callback: '.$ex->getMessage());
|
||||
try {
|
||||
if ($notify) {
|
||||
/** @var UserModel $userModel */
|
||||
$userModel = $this->getModel('user');
|
||||
if ($user = $userModel->getEntity($notify)) {
|
||||
$this->addNewNotification(
|
||||
sprintf(
|
||||
$this->translator->trans('mautic.plugin.clearbit.unable'),
|
||||
$ex->getMessage()
|
||||
),
|
||||
'Clearbit Plugin',
|
||||
'ri-error-warning-line',
|
||||
$user
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $ex2) {
|
||||
$mauticLogger->log('error', 'Clearbit: '.$ex2->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return new Response('OK');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Config\FileLocator;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Extension\Extension;
|
||||
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
|
||||
|
||||
class MauticClearbitExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* @param mixed[] $configs
|
||||
*/
|
||||
public function load(array $configs, ContainerBuilder $container): void
|
||||
{
|
||||
$loader = new PhpFileLoader($container, new FileLocator(__DIR__.'/../Config'));
|
||||
$loader->load('services.php');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\EventListener;
|
||||
|
||||
use Mautic\CoreBundle\CoreEvents;
|
||||
use Mautic\CoreBundle\Event\CustomButtonEvent;
|
||||
use Mautic\CoreBundle\Twig\Helper\ButtonHelper;
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
use MauticPlugin\MauticClearbitBundle\Integration\ClearbitIntegration;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Routing\RouterInterface;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
class ButtonSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private IntegrationHelper $helper,
|
||||
private TranslatorInterface $translator,
|
||||
private RouterInterface $router,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
CoreEvents::VIEW_INJECT_CUSTOM_BUTTONS => ['injectViewButtons', 0],
|
||||
];
|
||||
}
|
||||
|
||||
public function injectViewButtons(CustomButtonEvent $event): void
|
||||
{
|
||||
/** @var ClearbitIntegration $myIntegration */
|
||||
$myIntegration = $this->helper->getIntegrationObject('Clearbit');
|
||||
|
||||
if (false === $myIntegration || !$myIntegration->getIntegrationSettings()->getIsPublished()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (str_starts_with($event->getRoute(), 'mautic_contact_')) {
|
||||
$event->addButton(
|
||||
[
|
||||
'attr' => [
|
||||
'class' => 'btn btn-ghost btn-sm btn-nospin',
|
||||
'data-toggle' => 'ajaxmodal',
|
||||
'data-target' => '#MauticSharedModal',
|
||||
'onclick' => 'this.href=\''.
|
||||
$this->router->generate(
|
||||
'mautic_plugin_clearbit_action',
|
||||
['objectAction' => 'batchLookupPerson']
|
||||
).
|
||||
'?\' + mQuery.param({\'clearbit_batch_lookup\':{\'ids\':JSON.parse(Mautic.getCheckedListIds(false, true))}});return true;',
|
||||
'data-header' => $this->translator->trans('mautic.plugin.clearbit.button.caption'),
|
||||
],
|
||||
'btnText' => $this->translator->trans('mautic.plugin.clearbit.button.caption'),
|
||||
'iconClass' => 'ri-search-line',
|
||||
],
|
||||
ButtonHelper::LOCATION_BULK_ACTIONS
|
||||
);
|
||||
|
||||
if ($event->getItem()) {
|
||||
$lookupContactButton = [
|
||||
'attr' => [
|
||||
'data-toggle' => 'ajaxmodal',
|
||||
'data-target' => '#MauticSharedModal',
|
||||
'data-header' => $this->translator->trans(
|
||||
'mautic.plugin.clearbit.lookup.header',
|
||||
['%item%' => $event->getItem()->getEmail()]
|
||||
),
|
||||
'href' => $this->router->generate(
|
||||
'mautic_plugin_clearbit_action',
|
||||
['objectId' => $event->getItem()->getId(), 'objectAction' => 'lookupPerson']
|
||||
),
|
||||
],
|
||||
'btnText' => $this->translator->trans('mautic.plugin.clearbit.button.caption'),
|
||||
'iconClass' => 'ri-search-line',
|
||||
];
|
||||
|
||||
$event->addButton(
|
||||
$lookupContactButton,
|
||||
ButtonHelper::LOCATION_PAGE_ACTIONS,
|
||||
['mautic_contact_action', ['objectAction' => 'view']]
|
||||
);
|
||||
|
||||
$event->addButton(
|
||||
$lookupContactButton,
|
||||
ButtonHelper::LOCATION_LIST_ACTIONS,
|
||||
'mautic_contact_index'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (str_starts_with($event->getRoute(), 'mautic_company_')) {
|
||||
$event->addButton(
|
||||
[
|
||||
'attr' => [
|
||||
'class' => 'btn btn-ghost btn-sm btn-nospin',
|
||||
'data-toggle' => 'ajaxmodal',
|
||||
'data-target' => '#MauticSharedModal',
|
||||
'onclick' => 'this.href=\''.
|
||||
$this->router->generate(
|
||||
'mautic_plugin_clearbit_action',
|
||||
['objectAction' => 'batchLookupCompany']
|
||||
).
|
||||
'?\' + mQuery.param({\'clearbit_batch_lookup\':{\'ids\':JSON.parse(Mautic.getCheckedListIds(false, true))}});return true;',
|
||||
'data-header' => $this->translator->trans(
|
||||
'mautic.plugin.clearbit.button.caption'
|
||||
),
|
||||
],
|
||||
'btnText' => $this->translator->trans('mautic.plugin.clearbit.button.caption'),
|
||||
'iconClass' => 'ri-search-line',
|
||||
],
|
||||
ButtonHelper::LOCATION_BULK_ACTIONS
|
||||
);
|
||||
|
||||
if ($event->getItem()) {
|
||||
$lookupCompanyButton = [
|
||||
'attr' => [
|
||||
'data-toggle' => 'ajaxmodal',
|
||||
'data-target' => '#MauticSharedModal',
|
||||
'data-header' => $this->translator->trans(
|
||||
'mautic.plugin.clearbit.lookup.header',
|
||||
['%item%' => $event->getItem()->getName()]
|
||||
),
|
||||
'href' => $this->router->generate(
|
||||
'mautic_plugin_clearbit_action',
|
||||
['objectId' => $event->getItem()->getId(), 'objectAction' => 'lookupCompany']
|
||||
),
|
||||
],
|
||||
'btnText' => $this->translator->trans('mautic.plugin.clearbit.button.caption'),
|
||||
'iconClass' => 'ri-search-line',
|
||||
];
|
||||
|
||||
$event->addButton(
|
||||
$lookupCompanyButton,
|
||||
ButtonHelper::LOCATION_LIST_ACTIONS,
|
||||
'mautic_company_index'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\EventListener;
|
||||
|
||||
use Mautic\LeadBundle\Event\CompanyEvent;
|
||||
use Mautic\LeadBundle\Event\LeadEvent;
|
||||
use Mautic\LeadBundle\LeadEvents;
|
||||
use MauticPlugin\MauticClearbitBundle\Helper\LookupHelper;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class LeadSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private LookupHelper $lookupHelper,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
LeadEvents::LEAD_POST_SAVE => ['leadPostSave', 0],
|
||||
LeadEvents::COMPANY_POST_SAVE => ['companyPostSave', 0],
|
||||
];
|
||||
}
|
||||
|
||||
public function leadPostSave(LeadEvent $event): void
|
||||
{
|
||||
$this->lookupHelper->lookupContact($event->getLead(), true, true);
|
||||
}
|
||||
|
||||
public function companyPostSave(CompanyEvent $event): void
|
||||
{
|
||||
$this->lookupHelper->lookupCompany($event->getCompany(), true, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<array<mixed>>
|
||||
*/
|
||||
class BatchLookupType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add('ids', HiddenType::class);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.submit',
|
||||
'cancel_onclick' => 'javascript:void(0);',
|
||||
'cancel_attr' => [
|
||||
'data-dismiss' => 'modal',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'notify',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.plugin.clearbit.notify',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'data' => true,
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'clearbit_batch_lookup';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\Form\Type;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\FormButtonsType;
|
||||
use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
/**
|
||||
* @extends AbstractType<array<mixed>>
|
||||
*/
|
||||
class LookupType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder->add(
|
||||
'objectId',
|
||||
HiddenType::class,
|
||||
[
|
||||
'attr' => [
|
||||
'value' => $options['data']['objectId'],
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'buttons',
|
||||
FormButtonsType::class,
|
||||
[
|
||||
'apply_text' => false,
|
||||
'save_text' => 'mautic.core.form.submit',
|
||||
'cancel_onclick' => 'javascript:void(0);',
|
||||
'cancel_attr' => [
|
||||
'data-dismiss' => 'modal',
|
||||
],
|
||||
]
|
||||
);
|
||||
|
||||
$builder->add(
|
||||
'notify',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.plugin.clearbit.notify',
|
||||
'label_attr' => ['class' => 'control-label'],
|
||||
'attr' => [
|
||||
'class' => 'form-control',
|
||||
],
|
||||
'data' => true,
|
||||
'required' => false,
|
||||
]
|
||||
);
|
||||
|
||||
if (!empty($options['action'])) {
|
||||
$builder->setAction($options['action']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlockPrefix(): string
|
||||
{
|
||||
return 'clearbit_lookup';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\Helper;
|
||||
|
||||
use Mautic\CoreBundle\Helper\EncryptionHelper;
|
||||
use Mautic\CoreBundle\Helper\UserHelper;
|
||||
use Mautic\LeadBundle\Entity\Company;
|
||||
use Mautic\LeadBundle\Entity\Lead;
|
||||
use Mautic\LeadBundle\Model\CompanyModel;
|
||||
use Mautic\LeadBundle\Model\LeadModel;
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
use MauticPlugin\MauticClearbitBundle\Integration\ClearbitIntegration;
|
||||
use MauticPlugin\MauticClearbitBundle\Services\Clearbit_Company;
|
||||
use MauticPlugin\MauticClearbitBundle\Services\Clearbit_Person;
|
||||
use Monolog\Logger;
|
||||
|
||||
class LookupHelper
|
||||
{
|
||||
/**
|
||||
* @var bool|ClearbitIntegration
|
||||
*/
|
||||
protected $integration;
|
||||
|
||||
public function __construct(
|
||||
IntegrationHelper $integrationHelper,
|
||||
protected UserHelper $userHelper,
|
||||
protected Logger $logger,
|
||||
protected LeadModel $leadModel,
|
||||
protected CompanyModel $companyModel,
|
||||
) {
|
||||
$this->integration = $integrationHelper->getIntegrationObject('Clearbit');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $notify
|
||||
* @param bool $checkAuto
|
||||
*/
|
||||
public function lookupContact(Lead $lead, $notify = false, $checkAuto = false): void
|
||||
{
|
||||
if (!$lead->getEmail()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* @var Clearbit_Person $clearbit */
|
||||
if ($clearbit = $this->getClearbit()) {
|
||||
if (!$checkAuto || ($checkAuto && $this->integration->shouldAutoUpdate())) {
|
||||
try {
|
||||
[$cacheId, $webhookId, $cache] = $this->getCache($lead, $notify);
|
||||
|
||||
if (!array_key_exists($cacheId, $cache['clearbit'])) {
|
||||
$clearbit->setWebhookId($webhookId);
|
||||
$res = $clearbit->lookupByEmail($lead->getEmail());
|
||||
// Prevent from filling up the cache
|
||||
$cache['clearbit'] = [
|
||||
$cacheId => serialize($res),
|
||||
'nonce' => $cache['clearbit']['nonce'],
|
||||
];
|
||||
$lead->setSocialCache($cache);
|
||||
|
||||
if ($checkAuto) {
|
||||
$this->leadModel->getRepository()->saveEntity($lead);
|
||||
} else {
|
||||
$this->leadModel->saveEntity($lead);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->logger->log('error', 'Error while using Clearbit to lookup '.$lead->getEmail().': '.$ex->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $notify
|
||||
* @param bool $checkAuto
|
||||
*/
|
||||
public function lookupCompany(Company $company, $notify = false, $checkAuto = false): void
|
||||
{
|
||||
if (!$website = $company->getFieldValue('companywebsite')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* @var Clearbit_Company $clearbit */
|
||||
if ($clearbit = $this->getClearbit(false)) {
|
||||
if (!$checkAuto || ($checkAuto && $this->integration->shouldAutoUpdate())) {
|
||||
try {
|
||||
$parse = parse_url($company->getFieldValue('companywebsite'));
|
||||
[$cacheId, $webhookId, $cache] = $this->getCache($company, $notify);
|
||||
|
||||
if (isset($parse['host']) && !array_key_exists($cacheId, $cache['clearbit'])) {
|
||||
/* @var Router $router */
|
||||
$clearbit->setWebhookId($webhookId);
|
||||
$res = $clearbit->lookupByDomain($parse['host']);
|
||||
// Prevent from filling up the cache
|
||||
$cache['clearbit'] = [
|
||||
$cacheId => serialize($res),
|
||||
'nonce' => $cache['clearbit']['nonce'],
|
||||
];
|
||||
$company->setSocialCache($cache);
|
||||
if ($checkAuto) {
|
||||
$this->companyModel->getRepository()->saveEntity($company);
|
||||
} else {
|
||||
$this->companyModel->saveEntity($company);
|
||||
}
|
||||
}
|
||||
} catch (\Exception $ex) {
|
||||
$this->logger->log('error', 'Error while using Clearbit to lookup '.$parse['host'].': '.$ex->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function validateRequest($oid, $type)
|
||||
{
|
||||
// prefix#entityId#hour#userId#nonce
|
||||
[$w, $id, $hour, $uid, $nonce] = explode('#', $oid, 5);
|
||||
$notify = (str_contains($w, '_notify') && $uid) ? $uid : false;
|
||||
|
||||
switch ($type) {
|
||||
case 'person':
|
||||
$entity = $this->leadModel->getEntity($id);
|
||||
break;
|
||||
case 'company':
|
||||
$entity = $this->companyModel->getEntity($id);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($entity) {
|
||||
$socialCache = $entity->getSocialCache();
|
||||
$cacheId = $w.'#'.$id.'#'.$hour;
|
||||
|
||||
if (isset($socialCache['clearbit'][$cacheId]) && !empty($socialCache['clearbit']['nonce']) && !empty($nonce)
|
||||
&& $socialCache['clearbit']['nonce'] === $nonce
|
||||
) {
|
||||
return [
|
||||
'notify' => $notify,
|
||||
'entity' => $entity,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $person
|
||||
*
|
||||
* @return bool|Clearbit_Company|Clearbit_Person
|
||||
*/
|
||||
protected function getClearbit($person = true)
|
||||
{
|
||||
if (!$this->integration || !$this->integration->getIntegrationSettings()->getIsPublished()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// get api_key from plugin settings
|
||||
$keys = $this->integration->getDecryptedApiKeys();
|
||||
|
||||
return ($person) ? new Clearbit_Person($keys['apikey']) : new Clearbit_Company($keys['apikey']);
|
||||
}
|
||||
|
||||
protected function getCache($entity, $notify): array
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $this->userHelper->getUser();
|
||||
$nonce = substr(EncryptionHelper::generateKey(), 0, 16);
|
||||
$cacheId = sprintf('clearbit%s#', $notify ? '_notify' : '').$entity->getId().'#'.gmdate('YmdH');
|
||||
$webhookId = $cacheId.'#'.$user->getId().'#'.$nonce;
|
||||
|
||||
$cache = $entity->getSocialCache();
|
||||
if (!isset($cache['clearbit'])) {
|
||||
$cache['clearbit'] = [];
|
||||
}
|
||||
|
||||
$cache['clearbit']['nonce'] = $nonce;
|
||||
|
||||
return [$cacheId, $webhookId, $cache];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\Integration;
|
||||
|
||||
use Mautic\CoreBundle\Form\Type\YesNoButtonGroupType;
|
||||
use Mautic\PluginBundle\Integration\AbstractIntegration;
|
||||
use Symfony\Component\Form\Form;
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
|
||||
class ClearbitIntegration extends AbstractIntegration
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Clearbit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return's authentication method such as oauth2, oauth1a, key, etc.
|
||||
*/
|
||||
public function getAuthenticationType(): string
|
||||
{
|
||||
return 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array of key => label elements that will be converted to inputs to
|
||||
* obtain from the user.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getRequiredKeyFields(): array
|
||||
{
|
||||
// Do not rename field. clearbit.js depends on it
|
||||
return [
|
||||
'apikey' => 'mautic.integration.clearbit.apikey',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FormBuilder|Form $builder
|
||||
* @param array $data
|
||||
* @param string $formArea
|
||||
*/
|
||||
public function appendToForm(&$builder, $data, $formArea): void
|
||||
{
|
||||
if ('keys' === $formArea) {
|
||||
$builder->add(
|
||||
'auto_update',
|
||||
YesNoButtonGroupType::class,
|
||||
[
|
||||
'label' => 'mautic.plugin.clearbit.auto_update',
|
||||
'data' => isset($data['auto_update']) && (bool) $data['auto_update'],
|
||||
'attr' => [
|
||||
'tooltip' => 'mautic.plugin.clearbit.auto_update.tooltip',
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function shouldAutoUpdate(): bool
|
||||
{
|
||||
$featureSettings = $this->getKeys();
|
||||
|
||||
return isset($featureSettings['auto_update']) && (bool) $featureSettings['auto_update'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|array
|
||||
*/
|
||||
public function getFormNotes($section)
|
||||
{
|
||||
if ('custom' === $section) {
|
||||
return [
|
||||
'template' => '@MauticClearbit/Integration/form.html.twig',
|
||||
'parameters' => [
|
||||
'mauticUrl' => $this->router->generate(
|
||||
'mautic_plugin_clearbit_index', [], UrlGeneratorInterface::ABSOLUTE_URL
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
return parent::getFormNotes($section);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle;
|
||||
|
||||
use Mautic\PluginBundle\Bundle\PluginBundleBase;
|
||||
|
||||
class MauticClearbitBundle extends PluginBundleBase
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# Mautic bundle for Clearbit plugin
|
||||
|
||||
## This plugin is managed centrally in https://github.com/mautic/mautic/blob/head/plugins/MauticClearbitBundle and this is a read-only mirror repository.
|
||||
|
||||
**📣 Please make PRs and issues against Mautic Core, not here!**
|
||||
@@ -0,0 +1,18 @@
|
||||
<div class="alert alert-info">{{ 'mautic.plugin.clearbit.submit_items'|trans }}</div>
|
||||
<div style="margin-top: 10px">
|
||||
<ul class="list-group" style="max-height: 400px;overflow-y: auto">
|
||||
{% for item in lookupItems %}
|
||||
<li class="list-group-item">{{ item }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
var ids = Mautic.getCheckedListIds(false, true);
|
||||
if (mQuery('#clearbit_batch_lookup_ids').length) {
|
||||
mQuery('#clearbit_batch_lookup_ids').val(ids);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{{ form(form) }}
|
||||
@@ -0,0 +1,7 @@
|
||||
<div class="alert alert-info">{{ 'mautic.plugin.clearbit.submit'|trans }}</div>
|
||||
<div style="margin-top: 10px">
|
||||
<ul class="list-group" style="max-height: 400px;overflow-y: auto">
|
||||
<li class="list-group-item">{{ lookupItem }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
{{ form(form) }}
|
||||
@@ -0,0 +1,9 @@
|
||||
<div class="well well-sm" style="margin-bottom:0 !important;">
|
||||
<p>
|
||||
{{ 'mautic.plugin.clearbit.webhook_info'|trans|purify }}
|
||||
</p>
|
||||
<div class="alert alert-warning">
|
||||
{{ 'mautic.plugin.clearbit.public_info'|trans|purify }}
|
||||
</div>
|
||||
<input type="text" readonly="" onclick="this.setSelectionRange(0, this.value.length);" value="{{ mauticUrl }}" class="form-control">
|
||||
</div>
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\Services;
|
||||
|
||||
/**
|
||||
* This class handles the actually HTTP request to the Clearbit endpoint.
|
||||
*/
|
||||
class Clearbit_Base
|
||||
{
|
||||
public const REQUEST_LATENCY = 0.2;
|
||||
|
||||
public const USER_AGENT = 'mautic/clearbit-php-0.1.0';
|
||||
|
||||
private \DateTime $_next_req_time;
|
||||
|
||||
protected $_baseUri = '';
|
||||
|
||||
protected $_resourceUri = '';
|
||||
|
||||
protected $_version = 'v2';
|
||||
|
||||
protected $_webhookId;
|
||||
|
||||
public $response_obj;
|
||||
|
||||
public $response_code;
|
||||
|
||||
public $response_json;
|
||||
|
||||
/**
|
||||
* Slow down calls to the Clearbit API if needed.
|
||||
*/
|
||||
private function _wait_for_rate_limit(): void
|
||||
{
|
||||
$now = new \DateTime();
|
||||
if ($this->_next_req_time->getTimestamp() > $now->getTimestamp()) {
|
||||
$t = $this->_next_req_time->getTimestamp() - $now->getTimestamp();
|
||||
sleep($t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $hdr
|
||||
*/
|
||||
private function _update_rate_limit($hdr): void
|
||||
{
|
||||
$remaining = (float) $hdr['X-RateLimit-Remaining'];
|
||||
$reset = (float) $hdr['X-RateLimit-Reset'];
|
||||
$spacing = $reset / (1.0 + $remaining);
|
||||
$delay = $spacing - self::REQUEST_LATENCY;
|
||||
$this->_next_req_time = new \DateTime('now + '.$delay.' seconds');
|
||||
}
|
||||
|
||||
/**
|
||||
* The base constructor Sets the API key available from here:
|
||||
* https://dashboard.clearbit.com/keys.
|
||||
*
|
||||
* @param string $api_key
|
||||
*/
|
||||
public function __construct(
|
||||
protected $api_key,
|
||||
) {
|
||||
$this->_next_req_time = new \DateTime('@0');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $id
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function setWebhookId($id = null)
|
||||
{
|
||||
$this->_webhookId = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
protected function _execute($params = [])
|
||||
{
|
||||
$this->_wait_for_rate_limit();
|
||||
|
||||
if ($this->_webhookId) {
|
||||
$params['webhook_id'] = $this->_webhookId;
|
||||
}
|
||||
|
||||
$fullUrl = $this->_baseUri.$this->_version.$this->_resourceUri.
|
||||
'?'.http_build_query($params);
|
||||
|
||||
// open connection
|
||||
$connection = curl_init($fullUrl);
|
||||
curl_setopt($connection, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($connection, CURLOPT_USERAGENT, self::USER_AGENT);
|
||||
curl_setopt($connection, CURLOPT_HEADER, true); // return HTTP headers with response
|
||||
curl_setopt($connection, CURLOPT_HTTPHEADER, ['Authorization: Bearer '.$this->api_key]);
|
||||
|
||||
// execute request
|
||||
$resp = curl_exec($connection);
|
||||
|
||||
[$response_headers, $this->response_json] = explode("\r\n\r\n", $resp, 2);
|
||||
// $response_headers now has a string of the HTTP headers
|
||||
// $response_json is the body of the HTTP response
|
||||
|
||||
$headers = [];
|
||||
|
||||
foreach (explode("\r\n", $response_headers) as $i => $line) {
|
||||
if (0 === $i) {
|
||||
$headers['http_code'] = $line;
|
||||
} else {
|
||||
[$key, $value] = explode(': ', $line);
|
||||
$headers[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$this->response_code = curl_getinfo($connection, CURLINFO_HTTP_CODE);
|
||||
$this->response_obj = json_decode($this->response_json);
|
||||
|
||||
if (!in_array($this->response_code, [200, 201, 202], true)) {
|
||||
throw new \Exception($this->response_obj->error->message);
|
||||
} else {
|
||||
if ('200' === $this->response_code) {
|
||||
$this->_update_rate_limit($headers);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->response_obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\Services;
|
||||
|
||||
/**
|
||||
* This class handles everything related to the Company lookup API.
|
||||
*/
|
||||
class Clearbit_Company extends Clearbit_Base
|
||||
{
|
||||
public function __construct($api_key)
|
||||
{
|
||||
parent::__construct($api_key);
|
||||
$this->_baseUri = 'https://company.clearbit.com/';
|
||||
$this->_resourceUri = '/companies/find';
|
||||
}
|
||||
|
||||
public function lookupByDomain($search)
|
||||
{
|
||||
$this->_execute(['domain' => $search]);
|
||||
|
||||
return $this->response_obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticClearbitBundle\Services;
|
||||
|
||||
/**
|
||||
* This class handles everything related to the Person lookup API.
|
||||
*/
|
||||
class Clearbit_Person extends Clearbit_Base
|
||||
{
|
||||
protected $_resourceUri = '/people/find';
|
||||
|
||||
protected $_baseUri = 'https://person.clearbit.com/';
|
||||
|
||||
public function lookupByEmail($search)
|
||||
{
|
||||
$this->_execute(['email' => $search]);
|
||||
|
||||
return $this->response_obj;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
mautic.integration.clearbit.apikey="Clearbit API Key"
|
||||
mautic.plugin.clearbit.button.caption="Lookup using Clearbit"
|
||||
mautic.plugin.clearbit.lookup.header="Clearbit - Lookup information for %item%"
|
||||
mautic.plugin.clearbit.test_api="Test API and get Stats"
|
||||
mautic.plugin.clearbit.stats="Test Results"
|
||||
mautic.plugin.clearbit.toomany="You can only lookup 20 contacts at once!"
|
||||
mautic.plugin.clearbit.comptoomany="You can only lookup 20 companies at once!"
|
||||
mautic.plugin.clearbit.empty="There are no contacts to lookup!"
|
||||
mautic.plugin.clearbit.compempty="There are no company domains to lookup!<br/>(Company website is empty?)"
|
||||
mautic.plugin.clearbit.forbidden="You don't have permissions to update this contact"
|
||||
mautic.plugin.clearbit.compforbidden="You don't have permissions to update this company"
|
||||
mautic.plugin.clearbit.auto_update="Automatically update on save?"
|
||||
mautic.plugin.clearbit.auto_update.tooltip="WARNING: This could easily exhaust your quota of API calls per month."
|
||||
mautic.plugin.clearbit.notify="Show a notification when the information has been received."
|
||||
mautic.plugin.clearbit.contact_retrieved="The contact information for %s has been retrieved"
|
||||
mautic.plugin.clearbit.company_retrieved="The company information for %s has been retrieved"
|
||||
mautic.plugin.clearbit.unable="Unable to save the information: %s"
|
||||
mautic.plugin.clearbit.webhook_info="For the plugin to work, you must use the following as the Webhook URL in your account settings on the <a href=\"https://dashboard.clearbit.com/account\" target=\"_blank\">Clearbit Dashboard</a>:"
|
||||
mautic.plugin.clearbit.public_info="<strong>Warning!</strong> This must be a public accessible URL for the Webhook to work."
|
||||
mautic.plugin.clearbit.submit="Click submit to lookup the information for:"
|
||||
mautic.plugin.clearbit.submit_items="Click submit to lookup the information for the selected item(s)."
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "mautic/plugin-clearbit",
|
||||
"description": "Clearbit Plugin",
|
||||
"type": "mautic-plugin",
|
||||
"keywords": [
|
||||
"mautic",
|
||||
"plugin",
|
||||
"integration"
|
||||
],
|
||||
"extra": {
|
||||
"install-directory-name": "MauticClearbitBundle"
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"require": {
|
||||
"mautic/core-lib": "^7.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user