Skip to content

Commit

Permalink
Merge pull request mautic#3984 from mqueme/salesforce-notification-li…
Browse files Browse the repository at this point in the history
…nk-contact

Added link to notification errors for integrations and made notification errors for campaigns usable in listeners
  • Loading branch information
mqueme authored May 5, 2017
2 parents e92e695 + 3a384f1 commit 1a38b63
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 77 deletions.
87 changes: 50 additions & 37 deletions app/bundles/CampaignBundle/Model/EventModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ public function triggerEvent($type, $eventDetails = null, $channel = null, $chan
} elseif ($child['decisionPath'] == 'no') {
// non-action paths should not be processed by this because the contact already took action in order to get here
$childrenTriggered = true;
$this->logger->debug('CAMPAIGN: '.ucfirst($child['eventType']).' ID# '.$child['id'].' has a decision path of no');
} else {
$this->logger->debug('CAMPAIGN: '.ucfirst($child['eventType']).' ID# '.$child['id'].' is being processed');
}
Expand Down Expand Up @@ -626,8 +627,9 @@ public function triggerStartingEvents(
++$totalEventCount;

$event['campaign'] = [
'id' => $campaign->getId(),
'name' => $campaign->getName(),
'id' => $campaign->getId(),
'name' => $campaign->getName(),
'createdBy' => $campaign->getCreatedBy(),
];

$decisionEvent = [
Expand Down Expand Up @@ -769,8 +771,7 @@ public function triggerScheduledEvents(
) {
defined('MAUTIC_CAMPAIGN_SYSTEM_TRIGGERED') or define('MAUTIC_CAMPAIGN_SYSTEM_TRIGGERED', 1);

$campaignId = $campaign->getId();
$campaignName = $campaign->getName();
$campaignId = $campaign->getId();

$this->logger->debug('CAMPAIGN: Triggering scheduled events');

Expand Down Expand Up @@ -903,8 +904,9 @@ public function triggerScheduledEvents(

// Set campaign ID
$event['campaign'] = [
'id' => $campaignId,
'name' => $campaignName,
'id' => $campaign->getId(),
'name' => $campaign->getName(),
'createdBy' => $campaign->getCreatedBy(),
];

// Execute event
Expand Down Expand Up @@ -1011,8 +1013,6 @@ public function triggerNegativeEvents(
$this->logger->debug('CAMPAIGN: Triggering negative events');

$campaignId = $campaign->getId();
$campaignName = $campaign->getName();

$repo = $this->getRepository();
$campaignRepo = $this->getCampaignRepository();
$logRepo = $this->getLeadEventLogRepository();
Expand Down Expand Up @@ -1262,8 +1262,9 @@ public function triggerNegativeEvents(
// Set event
$event = $events[$id];
$event['campaign'] = [
'id' => $campaignId,
'name' => $campaignName,
'id' => $campaign->getId(),
'name' => $campaign->getName(),
'createdBy' => $campaign->getCreatedBy(),
];

// Set lead in case this is triggered by the system
Expand Down Expand Up @@ -1542,9 +1543,11 @@ public function executeEvent(
}

// Set campaign ID

$event['campaign'] = [
'id' => $campaign->getId(),
'name' => $campaign->getName(),
'id' => $campaign->getId(),
'name' => $campaign->getName(),
'createdBy' => $campaign->getCreatedBy(),
];

// Ensure properties is an array
Expand Down Expand Up @@ -1702,31 +1705,7 @@ public function executeEvent(
$repo->deleteEntity($log);
}

// Notify the lead owner if there is one otherwise campaign creator that there was a failure
if (!$owner = $lead->getOwner()) {
$ownerId = $campaign->getCreatedBy();
$owner = $this->userModel->getEntity($ownerId);
}

if ($owner && $owner->getId()) {
$this->notificationModel->addNotification(
$campaign->getName().' / '.$event['name'],
'error',
false,
$this->translator->trans(
'mautic.campaign.event.failed',
[
'%contact%' => '<a href="'.$this->router->generate(
'mautic_contact_action',
['objectAction' => 'view', 'objectId' => $lead->getId()]
).'" data-toggle="ajax">'.$lead->getPrimaryIdentifier().'</a>',
]
),
null,
null,
$owner
);
}
$this->notifyOfFailure($lead, $campaign->getCreatedBy(), $campaign->getName().' / '.$event['name']);

$this->logger->debug($debug);
} else {
Expand Down Expand Up @@ -2092,6 +2071,40 @@ public function getEventLineChartData($unit, \DateTime $dateFrom, \DateTime $dat
return $chart->render();
}

/**
* @param Lead $lead
* @param $campaignCreatedBy
* @param $header
*/
public function notifyOfFailure(Lead $lead, $campaignCreatedBy, $header)
{
// Notify the lead owner if there is one otherwise campaign creator that there was a failure
if (!$owner = $lead->getOwner()) {
$ownerId = (int) $campaignCreatedBy;
$owner = $this->userModel->getEntity($ownerId);
}

if ($owner && $owner->getId()) {
$this->notificationModel->addNotification(
$header,
'error',
false,
$this->translator->trans(
'mautic.campaign.event.failed',
[
'%contact%' => '<a href="'.$this->router->generate(
'mautic_contact_action',
['objectAction' => 'view', 'objectId' => $lead->getId()]
).'" data-toggle="ajax">'.$lead->getPrimaryIdentifier().'</a>',
]
),
null,
null,
$owner
);
}
}

/**
* Handles condition type events.
*
Expand Down
1 change: 1 addition & 0 deletions app/bundles/CampaignBundle/Translations/en_US/messages.ini
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mautic.campaign.event.inline.triggerimmediately="immediately"
mautic.campaign.event.inline.triggerinterval="+ %interval% %unit%"
mautic.campaign.event.last_error="Last execution error"
mautic.campaign.event.failed="Failed to execute campaign event for %contact%."
mautic.campaign.event.failed.timeline="Generic error."
mautic.campaign.event.intervalunit.choice.d="day(s)"
mautic.campaign.event.intervalunit.choice.h="hour(s)"
mautic.campaign.event.intervalunit.choice.i="minute(s)"
Expand Down
59 changes: 59 additions & 0 deletions app/bundles/PluginBundle/Exception/ApiErrorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,69 @@

namespace Mautic\PluginBundle\Exception;

use Mautic\LeadBundle\Entity\Lead;

class ApiErrorException extends \Exception
{
/**
* @var
*/
private $contactId;

/**
* @var Lead
*/
private $contact;

/**
* ApiErrorException constructor.
*
* @param string $message
* @param int $code
* @param \Exception|null $previous
*/
public function __construct($message = 'API error', $code = 0, \Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}

/**
* @return mixed
*/
public function getContactId()
{
return $this->contactId;
}

/**
* @param mixed $contactId
*
* @return ApiErrorException
*/
public function setContactId($contactId)
{
$this->contactId = $contactId;

return $this;
}

/**
* @return Lead
*/
public function getContact()
{
return $this->contact;
}

/**
* @param Lead $contact
*
* @return ApiErrorException
*/
public function setContact(Lead $contact)
{
$this->contact = $contact;

return $this;
}
}
90 changes: 85 additions & 5 deletions app/bundles/PluginBundle/Integration/AbstractIntegration.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Mautic\PluginBundle\Event\PluginIntegrationFormDisplayEvent;
use Mautic\PluginBundle\Event\PluginIntegrationKeyEvent;
use Mautic\PluginBundle\Event\PluginIntegrationRequestEvent;
use Mautic\PluginBundle\Exception\ApiErrorException;
use Mautic\PluginBundle\Helper\oAuthHelper;
use Mautic\PluginBundle\PluginEvents;
use Symfony\Component\Form\FormBuilder;
Expand Down Expand Up @@ -66,6 +67,18 @@ abstract class AbstractIntegration
*/
protected $em;

/**
* Used for notifications.
*
* @var array|null
*/
protected $adminUsers;

/**
* @var
*/
protected $notifications = [];

/**
* @param MauticFactory $factory
*
Expand Down Expand Up @@ -1806,17 +1819,84 @@ public function checkImageExists($url)
return $retcode == 200;
}

/**
* @return \Mautic\CoreBundle\Model\NotificationModel
*/
public function getNotificationModel()
{
return $this->factory->getModel('core.notification');
}

/**
* @param \Exception $e
* @param null $contact
*/
public function logIntegrationError(\Exception $e)
public function logIntegrationError(\Exception $e, Lead $contact = null)
{
$logger = $this->factory->getLogger();
if ('dev' == MAUTIC_ENV) {
$logger->addError('INTEGRATION ERROR: '.$this->getName().' - '.$e);
} else {
$logger->addError('INTEGRATION ERROR: '.$this->getName().' - '.$e->getMessage());

if ($e instanceof ApiErrorException) {
if (null === $this->adminUsers) {
$this->adminUsers = $this->em->getRepository('MauticUserBundle:User')->getEntities(
[
'filter' => [
'force' => [
[
'column' => 'r.isAdmin',
'expr' => 'eq',
'value' => true,
],
],
],
]
);
}

$errorMessage = ('dev' == MAUTIC_ENV) ? (string) $e : $e->getMessage();
$errorHeader = $this->getTranslator()->trans(
'mautic.integration.error',
[
'%name%' => $this->getName(),
]
);

if ($contact || $contact = $e->getContact()) {
// Append a link to the contact
$contactId = $contact->getId();
$contactName = $contact->getPrimaryIdentifier();
} elseif ($contactId = $e->getContactId()) {
$contactName = $this->getTranslator()->trans('mautic.integration.error.generic_contact_name', ['%id%' => $contactId]);
}

if ($contactId) {
$contactLink = $this->factory->getRouter()->generate('mautic_contact_action', [
'objectAction' => 'view', 'objectId' => $contactId,
],
UrlGeneratorInterface::ABSOLUTE_URL
);
$errorMessage .= ' <a href="'.$contactLink.'">'.$contactName.'</a>';
}

// Prevent a flood of the same messages
$messageHash = md5($errorMessage);
if (!array_key_exists($messageHash, $this->notifications)) {
foreach ($this->adminUsers as $user) {
$this->getNotificationModel()->addNotification(
$errorHeader,
$this->getName(),
false,
$errorMessage,
'text-danger fa-exclamation-circle',
null,
$user
);
}

$this->notifications[$messageHash] = true;
}
}

$logger->addError('INTEGRATION ERROR: '.$this->getName().' - '.$errorMessage);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions app/bundles/PluginBundle/Translations/en_US/messages.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
mautic.campaign.plugin.leadpush="Push contact"
mautic.integration.callbackuri="If applicable, use the following as the callback URL (may also be called the return URI) when configuring your application:"
mautic.integration.closewindow="Close Window"
mautic.integration.error="%name% Error"
mautic.integration.error.generic_contact_name="Contact ID# %id%"
mautic.integration.error.refreshtoken_expired="The refresh token has expired. Re-authorization is required."
mautic.integration.filter.all="Show all plugins"
mautic.integration.form.authorize="Authorize App"
Expand Down
3 changes: 0 additions & 3 deletions plugins/MauticCrmBundle/Api/SalesforceApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ public function request($operation, $elementData = [], $method = 'GET', $retry =
if (!$object) {
$object = $this->object;
}
$notificactionModel = $this->integration->getNotificationModel();
if (!$queryUrl) {
$queryUrl = $this->integration->getApiUrl();
$requestUrl = sprintf($queryUrl.'/%s/%s', $object, $operation);
Expand All @@ -53,7 +52,6 @@ public function request($operation, $elementData = [], $method = 'GET', $retry =
$response = $this->integration->makeRequest($requestUrl, $elementData, $method, $this->requestSettings);

if (!empty($response['errors'])) {
$notificactionModel->addNotification(implode(', ', $response['errors']), 'Salesforce', false, $this->integration->getName().':');
throw new ApiErrorException(implode(', ', $response['errors']));
} elseif (is_array($response)) {
$errors = [];
Expand All @@ -68,7 +66,6 @@ public function request($operation, $elementData = [], $method = 'GET', $retry =
}
}
$errors[] = $r['message'];
$notificactionModel->addNotification($r['message'], 'Salesforce', false, $this->integration->getName().':');
}
}

Expand Down
Loading

0 comments on commit 1a38b63

Please sign in to comment.