Initial commit: CloudOps infrastructure platform
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MauticPlugin\MauticSocialBundle\Integration;
|
||||
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
|
||||
final class Config
|
||||
{
|
||||
public function __construct(private IntegrationHelper $integrationsHelper)
|
||||
{
|
||||
}
|
||||
|
||||
public function isPublished(): bool
|
||||
{
|
||||
$integration = $this->integrationsHelper->getIntegrationObject(TwitterIntegration::NAME);
|
||||
|
||||
return $integration && $integration->getIntegrationSettings()->getIsPublished();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticSocialBundle\Integration;
|
||||
|
||||
use MauticPlugin\MauticSocialBundle\Form\Type\FacebookType;
|
||||
|
||||
class FacebookIntegration extends SocialIntegration
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Facebook';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getIdentifierFields(): array
|
||||
{
|
||||
return [
|
||||
'facebook',
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportedFeatures(): array
|
||||
{
|
||||
return [
|
||||
'share_button',
|
||||
'login_button',
|
||||
'public_profile',
|
||||
];
|
||||
}
|
||||
|
||||
public function getAuthenticationUrl(): string
|
||||
{
|
||||
return 'https://www.facebook.com/dialog/oauth';
|
||||
}
|
||||
|
||||
public function getAccessTokenUrl(): string
|
||||
{
|
||||
return 'https://graph.facebook.com/oauth/access_token';
|
||||
}
|
||||
|
||||
public function getAuthScope(): string
|
||||
{
|
||||
return 'email';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param bool $postAuthorization
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function parseCallbackResponse($data, $postAuthorization = false)
|
||||
{
|
||||
// Facebook is inconsistent in that it returns errors as json and data as parameter list
|
||||
$values = parent::parseCallbackResponse($data, $postAuthorization);
|
||||
|
||||
if (null === $values) {
|
||||
parse_str($data, $values);
|
||||
|
||||
$this->requestStack->getSession()->set($this->getName().'_tokenResponse', $values);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
public function getApiUrl($endpoint): string
|
||||
{
|
||||
return "https://graph.facebook.com/$endpoint";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get public data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getUserData($identifier, &$socialCache)
|
||||
{
|
||||
$this->persistNewLead = false;
|
||||
$accessToken = $this->getContactAccessToken($socialCache);
|
||||
|
||||
if (!isset($accessToken['access_token'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = $this->getApiUrl('v2.8/me');
|
||||
$fields = array_keys($this->getAvailableLeadFields());
|
||||
|
||||
$parameters = [
|
||||
'access_token' => $accessToken['access_token'],
|
||||
'fields' => implode(',', $fields),
|
||||
];
|
||||
|
||||
$data = $this->makeRequest($url, $parameters, 'GET', ['auth_type' => 'rest']);
|
||||
|
||||
if (is_object($data) && isset($data->id)) {
|
||||
$info = $this->matchUpData($data);
|
||||
|
||||
if (isset($data->username)) {
|
||||
$info['profileHandle'] = $data->username;
|
||||
} elseif (isset($data->link)) {
|
||||
if (preg_match("/www.facebook.com\/(app_scoped_user_id\/)?(.*?)($|\/)/", $data->link, $matches)) {
|
||||
$info['profileHandle'] = $matches[2];
|
||||
}
|
||||
} else {
|
||||
$info['profileHandle'] = $data->id;
|
||||
}
|
||||
$info['profileImage'] = "https://graph.facebook.com/{$data->id}/picture?type=large";
|
||||
|
||||
$socialCache['id'] = $data->id;
|
||||
$socialCache['profile'] = $info;
|
||||
$socialCache['lastRefresh'] = new \DateTime();
|
||||
$socialCache['accessToken'] = $this->encryptApiKeys($accessToken);
|
||||
|
||||
$this->getMauticLead($info, $this->persistNewLead, $socialCache, $identifier);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getAvailableLeadFields($settings = []): array
|
||||
{
|
||||
return [
|
||||
'about' => ['type' => 'string'],
|
||||
'birthday' => ['type' => 'string'],
|
||||
'email' => ['type' => 'string'],
|
||||
'first_name' => ['type' => 'string'],
|
||||
'gender' => ['type' => 'string'],
|
||||
'last_name' => ['type' => 'string'],
|
||||
'link' => ['type' => 'string'],
|
||||
'locale' => ['type' => 'string'],
|
||||
'middle_name' => ['type' => 'string'],
|
||||
'name' => ['type' => 'string'],
|
||||
'political' => ['type' => 'string'],
|
||||
'quotes' => ['type' => 'string'],
|
||||
'religion' => ['type' => 'string'],
|
||||
'timezone' => ['type' => 'string'],
|
||||
'website' => ['type' => 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
public function getFormType(): string
|
||||
{
|
||||
return FacebookType::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticSocialBundle\Integration;
|
||||
|
||||
class FoursquareIntegration extends SocialIntegration
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Foursquare';
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getIdentifierFields(): array
|
||||
{
|
||||
return [
|
||||
'email',
|
||||
'twitter', // foursquare allows searching directly by twitter handle
|
||||
];
|
||||
}
|
||||
|
||||
public function getAuthenticationUrl(): string
|
||||
{
|
||||
return 'https://foursquare.com/oauth2/authenticate';
|
||||
}
|
||||
|
||||
public function getAccessTokenUrl(): string
|
||||
{
|
||||
return 'https://foursquare.com/oauth2/access_token';
|
||||
}
|
||||
|
||||
public function getAuthenticationType(): string
|
||||
{
|
||||
return 'oauth2';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $endpoint
|
||||
* @param string $m
|
||||
*/
|
||||
public function getApiUrl($endpoint, $m = 'foursquare'): string
|
||||
{
|
||||
return "https://api.foursquare.com/v2/$endpoint?v=20140806&m={$m}";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $parameters
|
||||
* @param string $method
|
||||
* @param array $settings
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function makeRequest($url, $parameters = [], $method = 'GET', $settings = [])
|
||||
{
|
||||
$settings[$this->getAuthTokenKey()] = 'oauth_token';
|
||||
|
||||
return parent::makeRequest($url, $parameters, $method, $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get public data.
|
||||
*/
|
||||
public function getUserData($identifier, &$socialCache): void
|
||||
{
|
||||
if ($id = $this->getContactUserId($identifier, $socialCache)) {
|
||||
$url = $this->getApiUrl("users/{$id}");
|
||||
$data = $this->makeRequest($url);
|
||||
if (!empty($data) && isset($data->response->user)) {
|
||||
$result = $data->response->user;
|
||||
$socialCache['profile'] = $this->matchUpData($result);
|
||||
if (isset($result->photo)) {
|
||||
$socialCache['profile']['profileImage'] = $result->photo->prefix.'300x300'.$result->photo->suffix;
|
||||
}
|
||||
$socialCache['profile']['profileHandle'] = $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getPublicActivity($identifier, &$socialCache): void
|
||||
{
|
||||
if ($id = $this->getContactUserId($identifier, $socialCache)) {
|
||||
$activity = [
|
||||
// 'mayorships' => array(),
|
||||
'tips' => [],
|
||||
// 'lists' => array()
|
||||
];
|
||||
|
||||
/*
|
||||
//mayorships
|
||||
$url = $this->getApiUrl("users/{$id}/mayorships");
|
||||
$data = $this->makeRequest($url);
|
||||
|
||||
if (isset($data->response->mayorships) && count($data->response->mayorships->items)) {
|
||||
$limit = 5;
|
||||
foreach ($data->response->mayorships->items as $m) {
|
||||
if (empty($limit)) {
|
||||
break;
|
||||
}
|
||||
//find main category of venue
|
||||
$category = '';
|
||||
foreach ($m->venue->categories as $c) {
|
||||
if ($c->primary) {
|
||||
$category = $c->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$contact = (!empty($m->contact->formattedPhone)) ? $m->contact->formattedPhone : '';
|
||||
$activity['mayorships'][] = array(
|
||||
'venueName' => $m->venue->name,
|
||||
'venueLocation' => $m->venue->location->formattedAddress,
|
||||
'venueContact' => $contact,
|
||||
'venueCategory' => $category
|
||||
);
|
||||
$limit--;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// tips
|
||||
$url = $this->getApiUrl("users/{$id}/tips").'&limit=5&sort=recent';
|
||||
$data = $this->makeRequest($url);
|
||||
|
||||
if (isset($data->response->tips) && count($data->response->tips->items)) {
|
||||
foreach ($data->response->tips->items as $t) {
|
||||
// find main category of venue
|
||||
$category = '';
|
||||
foreach ($t->venue->categories as $c) {
|
||||
if ($c->primary) {
|
||||
$category = $c->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$contact = (!empty($t->contact->formattedPhone)) ? $t->contact->formattedPhone : '';
|
||||
$activity['tips'][] = [
|
||||
'createdAt' => $t->createdAt,
|
||||
'tipText' => $t->text,
|
||||
'tipUrl' => $t->canonicalUrl,
|
||||
'venueName' => $t->venue->name,
|
||||
'venueLocation' => $t->venue->location->formattedAddress,
|
||||
'venueContact' => $contact,
|
||||
'venueCategory' => $category,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
//lists
|
||||
$url = $this->getApiUrl("users/{$id}/lists") . "&limit=5&group=created";
|
||||
$data = $this->makeRequest($url);
|
||||
|
||||
if (isset($data->response->lists) && count($data->response->lists->items)) {
|
||||
foreach ($data->response->lists->items as $l) {
|
||||
if (!$l->listItems->count) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$item = array(
|
||||
'listName' => $l->name,
|
||||
'listDescription' => $l->description,
|
||||
'listUrl' => $l->canonicalUrl,
|
||||
'listCreatedAt' => (isset($l->createdAt)) ? $l->createdAt : '',
|
||||
'listUpdatedAt' => (isset($l->updatedAt)) ? $l->updatedAt : '',
|
||||
'listItems' => array()
|
||||
);
|
||||
|
||||
//get a sample of the list items
|
||||
$url = "https://api.foursquare.com/v2/lists/{$l->id}?limit=5&sort=recent&v=20140719&oauth_token={$keys['access_token']}";
|
||||
$listData = $this->makeRequest($url);
|
||||
|
||||
if (isset($listData->response->list->listItems) && count($listData->response->list->listItems->items)) {
|
||||
foreach ($listData->response->list->listItems->items as $li) {
|
||||
//find main category of venue
|
||||
$category = '';
|
||||
foreach ($li->venue->categories as $c) {
|
||||
if ($c->primary) {
|
||||
$category = $c->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$contact = (!empty($li->contact->formattedPhone)) ? $li->contact->formattedPhone : '';
|
||||
|
||||
$item['listItems'][] = array(
|
||||
'createdAt' => $li->createdAt,
|
||||
'venueName' => $li->venue->name,
|
||||
'venueLocation' => $li->venue->location->formattedAddress,
|
||||
'venueContact' => $contact,
|
||||
'venueCategory' => $category
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$activity['lists'][] = $item;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (!empty($activity)) {
|
||||
$socialCache['activity'] = $activity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getErrorsFromResponse($response): string
|
||||
{
|
||||
if (is_object($response) && isset($response->meta->errorDetail)) {
|
||||
return $response->meta->errorDetail.' ('.$response->meta->code.')';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function matchFieldName($field, $subfield = '')
|
||||
{
|
||||
if ('contact' == $field && in_array($subfield, ['facebook', 'twitter'])) {
|
||||
return $subfield.'ProfileHandle';
|
||||
}
|
||||
|
||||
return parent::matchFieldName($field, $subfield);
|
||||
}
|
||||
|
||||
public function getAvailableLeadFields($settings = []): array
|
||||
{
|
||||
return [
|
||||
'profileHandle' => ['type' => 'string'],
|
||||
'firstName' => ['type' => 'string'],
|
||||
'lastName' => ['type' => 'string'],
|
||||
'gender' => ['type' => 'string'],
|
||||
'homeCity' => ['type' => 'string'],
|
||||
'bio' => ['type' => 'string'],
|
||||
'contact' => [
|
||||
'type' => 'object',
|
||||
'fields' => [
|
||||
'twitter',
|
||||
'facebook',
|
||||
'phone',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportedFeatures(): array
|
||||
{
|
||||
return [
|
||||
'public_profile',
|
||||
'public_activity',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
private function getContactUserId(&$identifier, &$socialCache)
|
||||
{
|
||||
if (!empty($socialCache['id'])) {
|
||||
return $socialCache['id'];
|
||||
} elseif (empty($identifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$cleaned = $this->cleanIdentifier($identifier);
|
||||
|
||||
if (!is_array($cleaned)) {
|
||||
$cleaned = [$cleaned];
|
||||
}
|
||||
|
||||
foreach ($cleaned as $type => $c) {
|
||||
$url = $this->getApiUrl('users/search')."&{$type}={$c}";
|
||||
$data = $this->makeRequest($url);
|
||||
|
||||
if (!empty($data) && isset($data->response->results) && count($data->response->results)) {
|
||||
$socialCache['id'] = $data->response->results[0]->id;
|
||||
|
||||
return $socialCache['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFormType(): null
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticSocialBundle\Integration;
|
||||
|
||||
class InstagramIntegration extends SocialIntegration
|
||||
{
|
||||
public function getName(): string
|
||||
{
|
||||
return 'Instagram';
|
||||
}
|
||||
|
||||
public function getSupportedFeatures(): array
|
||||
{
|
||||
return [
|
||||
'public_profile',
|
||||
'public_activity',
|
||||
];
|
||||
}
|
||||
|
||||
public function getIdentifierFields(): string
|
||||
{
|
||||
return 'instagram';
|
||||
}
|
||||
|
||||
public function getAuthenticationUrl(): string
|
||||
{
|
||||
return 'https://api.instagram.com/oauth/authorize';
|
||||
}
|
||||
|
||||
public function getAccessTokenUrl(): string
|
||||
{
|
||||
return 'https://api.instagram.com/oauth/access_token';
|
||||
}
|
||||
|
||||
public function getApiUrl($endpoint): string
|
||||
{
|
||||
return "https://api.instagram.com/v1/$endpoint";
|
||||
}
|
||||
|
||||
public function getUserData($identifier, &$socialCache): void
|
||||
{
|
||||
if ($id = $this->getContactUserId($identifier, $socialCache)) {
|
||||
$url = $this->getApiUrl('users/'.$id);
|
||||
$data = $this->makeRequest($url);
|
||||
|
||||
if (isset($data->data)) {
|
||||
$info = $this->matchUpData($data->data);
|
||||
|
||||
$info['profileImage'] = $data->data->profile_picture;
|
||||
$info['profileHandle'] = $data->data->username;
|
||||
$socialCache['profile'] = $info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getPublicActivity($identifier, &$socialCache): void
|
||||
{
|
||||
$socialCache['has']['activity'] = false;
|
||||
if ($id = $this->getContactUserId($identifier, $socialCache)) {
|
||||
// get more than 10 so we can weed out videos
|
||||
$data = $this->makeRequest($this->getApiUrl("users/$id/media/recent"), ['count' => 20]);
|
||||
|
||||
$socialCache['activity'] = [
|
||||
'photos' => [],
|
||||
'tags' => [],
|
||||
];
|
||||
|
||||
if (!empty($data->data)) {
|
||||
$socialCache['has']['activity'] = true;
|
||||
$count = 1;
|
||||
foreach ($data->data as $m) {
|
||||
if ($count > 10) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ('image' == $m->type) {
|
||||
$socialCache['activity']['photos'][] = [
|
||||
'url' => $m->images->standard_resolution->url,
|
||||
];
|
||||
|
||||
if (!empty($m->caption->text)) {
|
||||
preg_match_all("/#(\w+)/", $m->caption->text, $tags);
|
||||
foreach ($tags[1] as $tag) {
|
||||
if (isset($socialCache['activity']['tags'][$tag])) {
|
||||
++$socialCache['activity']['tags'][$tag]['count'];
|
||||
} else {
|
||||
$socialCache['activity']['tags'][$tag] = [
|
||||
'count' => 1,
|
||||
'url' => 'http://searchinstagram.com/'.$tag,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAvailableLeadFields($settings = []): array
|
||||
{
|
||||
return [
|
||||
'full_name' => ['type' => 'string'],
|
||||
'bio' => ['type' => 'string'],
|
||||
'website' => ['type' => 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
private function getContactUserId(&$identifier, &$socialCache)
|
||||
{
|
||||
if (!empty($socialCache['id'])) {
|
||||
return $socialCache['id'];
|
||||
} elseif (empty($identifier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$data = $this->makeRequest($this->getApiUrl('users/search'), ['q' => $identifier]);
|
||||
|
||||
if (!empty($data->data)) {
|
||||
foreach ($data->data as $user) {
|
||||
// its possible that instagram may return multiple users if the username is a base of another
|
||||
// for example, search for alan may return alanh, alanhartless, etc
|
||||
if (strtolower($user->username) == strtolower($identifier)) {
|
||||
$socialCache['id'] = $user->id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (!empty($socialCache['id'])) ? $socialCache['id'] : false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFormType(): null
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticSocialBundle\Integration;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Mautic\CoreBundle\Helper\CacheStorageHelper;
|
||||
use Mautic\CoreBundle\Helper\EncryptionHelper;
|
||||
use Mautic\CoreBundle\Helper\PathsHelper;
|
||||
use Mautic\CoreBundle\Model\NotificationModel;
|
||||
use Mautic\CoreBundle\Translation\Translator;
|
||||
use Mautic\LeadBundle\Field\FieldsWithUniqueIdentifier;
|
||||
use Mautic\LeadBundle\Model\CompanyModel;
|
||||
use Mautic\LeadBundle\Model\DoNotContact;
|
||||
use Mautic\LeadBundle\Model\FieldModel;
|
||||
use Mautic\LeadBundle\Model\LeadModel;
|
||||
use Mautic\PluginBundle\Helper\IntegrationHelper;
|
||||
use Mautic\PluginBundle\Integration\AbstractIntegration;
|
||||
use Mautic\PluginBundle\Model\IntegrationEntityModel;
|
||||
use Monolog\Logger;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\Form\FormBuilder;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Routing\Router;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
abstract class SocialIntegration extends AbstractIntegration
|
||||
{
|
||||
protected $persistNewLead = false;
|
||||
|
||||
/**
|
||||
* @var Translator
|
||||
*/
|
||||
protected TranslatorInterface $translator;
|
||||
|
||||
public function __construct(
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
CacheStorageHelper $cacheStorageHelper,
|
||||
EntityManager $entityManager,
|
||||
RequestStack $requestStack,
|
||||
Router $router,
|
||||
Translator $translator,
|
||||
Logger $logger,
|
||||
EncryptionHelper $encryptionHelper,
|
||||
LeadModel $leadModel,
|
||||
CompanyModel $companyModel,
|
||||
PathsHelper $pathsHelper,
|
||||
NotificationModel $notificationModel,
|
||||
FieldModel $fieldModel,
|
||||
FieldsWithUniqueIdentifier $fieldsWithUniqueIdentifier,
|
||||
IntegrationEntityModel $integrationEntityModel,
|
||||
DoNotContact $doNotContact,
|
||||
protected IntegrationHelper $integrationHelper,
|
||||
) {
|
||||
parent::__construct(
|
||||
$eventDispatcher,
|
||||
$cacheStorageHelper,
|
||||
$entityManager,
|
||||
$requestStack,
|
||||
$router,
|
||||
$translator,
|
||||
$logger,
|
||||
$encryptionHelper,
|
||||
$leadModel,
|
||||
$companyModel,
|
||||
$pathsHelper,
|
||||
$notificationModel,
|
||||
$fieldModel,
|
||||
$integrationEntityModel,
|
||||
$doNotContact,
|
||||
$fieldsWithUniqueIdentifier
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Mautic\PluginBundle\Integration\Form|FormBuilder $builder
|
||||
* @param array $data
|
||||
* @param string $formArea
|
||||
*/
|
||||
public function appendToForm(&$builder, $data, $formArea): void
|
||||
{
|
||||
if ('features' == $formArea) {
|
||||
$name = strtolower($this->getName());
|
||||
$formType = $this->getFormType();
|
||||
if ($formType) {
|
||||
$builder->add('shareButton', $formType, [
|
||||
'label' => 'mautic.integration.form.sharebutton',
|
||||
'required' => false,
|
||||
'data' => $data['shareButton'] ?? [],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $settings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getFormLeadFields($settings = [])
|
||||
{
|
||||
static $fields = [];
|
||||
|
||||
if (empty($fields)) {
|
||||
$s = $this->getName();
|
||||
$available = $this->getAvailableLeadFields($settings);
|
||||
if (empty($available)) {
|
||||
return [];
|
||||
}
|
||||
// create social profile fields
|
||||
$socialProfileUrls = $this->integrationHelper->getSocialProfileUrlRegex();
|
||||
|
||||
foreach ($available as $field => $details) {
|
||||
$label = (!empty($details['label'])) ? $details['label'] : false;
|
||||
$fn = $this->matchFieldName($field);
|
||||
switch ($details['type']) {
|
||||
case 'string':
|
||||
case 'boolean':
|
||||
$fields[$fn] = (!$label)
|
||||
? $this->translator->transConditional("mautic.integration.common.{$fn}", "mautic.integration.{$s}.{$fn}")
|
||||
: $label;
|
||||
break;
|
||||
case 'object':
|
||||
if (isset($details['fields'])) {
|
||||
foreach ($details['fields'] as $f) {
|
||||
$fn = $this->matchFieldName($field, $f);
|
||||
$fields[$fn] = (!$label)
|
||||
? $this->translator->transConditional("mautic.integration.common.{$fn}", "mautic.integration.{$s}.{$fn}")
|
||||
: $label;
|
||||
}
|
||||
} else {
|
||||
$fields[$field] = (!$label)
|
||||
? $this->translator->transConditional("mautic.integration.common.{$fn}", "mautic.integration.{$s}.{$fn}")
|
||||
: $label;
|
||||
}
|
||||
break;
|
||||
case 'array_object':
|
||||
if ('urls' == $field || 'url' == $field) {
|
||||
foreach ($socialProfileUrls as $p => $d) {
|
||||
$fields["{$p}ProfileHandle"] = (!$label)
|
||||
? $this->translator->transConditional("mautic.integration.common.{$p}ProfileHandle", "mautic.integration.{$s}.{$p}ProfileHandle")
|
||||
: $label;
|
||||
}
|
||||
foreach ($details['fields'] as $f) {
|
||||
$fields["{$p}Urls"] = (!$label)
|
||||
? $this->translator->transConditional("mautic.integration.common.{$f}Urls", "mautic.integration.{$s}.{$f}Urls")
|
||||
: $label;
|
||||
}
|
||||
} elseif (isset($details['fields'])) {
|
||||
foreach ($details['fields'] as $f) {
|
||||
$fn = $this->matchFieldName($field, $f);
|
||||
$fields[$fn] = (!$label)
|
||||
? $this->translator->transConditional("mautic.integration.common.{$fn}", "mautic.integration.{$s}.{$fn}")
|
||||
: $label;
|
||||
}
|
||||
} else {
|
||||
$fields[$fn] = (!$label)
|
||||
? $this->translator->transConditional("mautic.integration.common.{$fn}", "mautic.integration.{$s}.{$fn}")
|
||||
: $label;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($this->sortFieldsAlphabetically()) {
|
||||
uasort($fields, 'strnatcmp');
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function getFormCompanyFields($settings = [])
|
||||
{
|
||||
$settings['feature_settings']['objects'] = ['Company'];
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getAuthenticationType()
|
||||
{
|
||||
return 'oauth2';
|
||||
}
|
||||
|
||||
public function getRequiredKeyFields()
|
||||
{
|
||||
return [
|
||||
'client_id' => 'mautic.integration.keyfield.clientid',
|
||||
'client_secret' => 'mautic.integration.keyfield.clientsecret',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array key for clientId.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClientIdKey()
|
||||
{
|
||||
return 'client_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array key for client secret.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClientSecretKey()
|
||||
{
|
||||
return 'client_secret';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param bool $postAuthorization
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function parseCallbackResponse($data, $postAuthorization = false)
|
||||
{
|
||||
if ($postAuthorization) {
|
||||
return json_decode($data, true);
|
||||
} else {
|
||||
return json_decode($data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns notes specific to sections of the integration form (if applicable).
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function getFormNotes($section)
|
||||
{
|
||||
return ['', 'info'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template for social profiles.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getSocialProfileTemplate()
|
||||
{
|
||||
return "MauticSocialBundle:Integration/{$this->getName()}/Profile:view.html.twig";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access token from session or socialCache.
|
||||
*
|
||||
* @return array|mixed|null
|
||||
*/
|
||||
protected function getContactAccessToken(&$socialCache)
|
||||
{
|
||||
if (!$this->requestStack->getCurrentRequest()->hasSession()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!$this->requestStack->getSession()->isStarted()) {
|
||||
return (isset($socialCache['accessToken'])) ? $this->decryptApiKeys($socialCache['accessToken']) : null;
|
||||
}
|
||||
|
||||
$accessToken = $this->requestStack->getSession()->get($this->getName().'_tokenResponse', []);
|
||||
if (!isset($accessToken[$this->getAuthTokenKey()])) {
|
||||
if (isset($socialCache['accessToken'])) {
|
||||
$accessToken = $this->decryptApiKeys($socialCache['accessToken']);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$this->requestStack->getSession()->remove($this->getName().'_tokenResponse');
|
||||
$socialCache['accessToken'] = $this->encryptApiKeys($accessToken);
|
||||
|
||||
$this->persistNewLead = true;
|
||||
}
|
||||
|
||||
return $accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns form type.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
abstract public function getFormType();
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
<?php
|
||||
|
||||
namespace MauticPlugin\MauticSocialBundle\Integration;
|
||||
|
||||
use MauticPlugin\MauticSocialBundle\Form\Type\TwitterType;
|
||||
|
||||
class TwitterIntegration extends SocialIntegration
|
||||
{
|
||||
public const NAME = 'Twitter';
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
public function getPriority(): int
|
||||
{
|
||||
return 5000;
|
||||
}
|
||||
|
||||
public function getIdentifierFields(): string
|
||||
{
|
||||
return 'twitter';
|
||||
}
|
||||
|
||||
public function getSupportedFeatures(): array
|
||||
{
|
||||
return [
|
||||
'public_profile',
|
||||
'public_activity',
|
||||
'share_button',
|
||||
'login_button',
|
||||
];
|
||||
}
|
||||
|
||||
public function getAccessTokenUrl(): string
|
||||
{
|
||||
return 'https://api.twitter.com/oauth/access_token';
|
||||
}
|
||||
|
||||
public function getAuthLoginUrl(): string
|
||||
{
|
||||
$url = 'https://api.twitter.com/oauth/authorize';
|
||||
|
||||
// Get request token
|
||||
$requestToken = $this->getRequestToken();
|
||||
|
||||
if (isset($requestToken['oauth_token'])) {
|
||||
$url .= '?oauth_token='.$requestToken['oauth_token'];
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
public function getRequestTokenUrl(): string
|
||||
{
|
||||
return 'https://api.twitter.com/oauth/request_token';
|
||||
}
|
||||
|
||||
public function getAuthenticationType(): string
|
||||
{
|
||||
return 'oauth1a';
|
||||
}
|
||||
|
||||
public function prepareRequest($url, $parameters, $method, $settings, $authType)
|
||||
{
|
||||
// Prevent SSL issues
|
||||
$settings['ssl_verifypeer'] = false;
|
||||
|
||||
if (empty($settings['authorize_session']) && 'access_token' != $authType) {
|
||||
// Twitter requires oauth_token_secret to be part of composite key
|
||||
if (isset($this->keys['oauth_token_secret'])) {
|
||||
$settings['token_secret'] = $this->keys['oauth_token_secret'];
|
||||
}
|
||||
|
||||
// Twitter also requires double encoding of parameters in building base string
|
||||
$settings['double_encode_basestring_parameters'] = true;
|
||||
}
|
||||
|
||||
return parent::prepareRequest($url, $parameters, $method, $settings, $authType);
|
||||
}
|
||||
|
||||
public function getApiUrl($endpoint): string
|
||||
{
|
||||
return "https://api.twitter.com/1.1/$endpoint.json";
|
||||
}
|
||||
|
||||
public function getUserData($identifier, &$socialCache)
|
||||
{
|
||||
$accessToken = $this->getContactAccessToken($socialCache);
|
||||
|
||||
// Contact SSO
|
||||
if (isset($accessToken['oauth_token'])) {
|
||||
// note twitter requires params to be passed as strings
|
||||
$data = $this->makeRequest(
|
||||
$this->getApiUrl('account/verify_credentials'),
|
||||
[
|
||||
'include_email' => 'true',
|
||||
'include_entities' => 'false',
|
||||
'oauth_token' => $accessToken['oauth_token'],
|
||||
],
|
||||
'GET',
|
||||
['auth_type' => 'oauth1a']
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($data)) {
|
||||
// Try via user lookup
|
||||
$data = $this->makeRequest(
|
||||
$this->getApiUrl('users/lookup'),
|
||||
[
|
||||
'screen_name' => $this->cleanIdentifier($identifier),
|
||||
'include_entities' => 'false',
|
||||
]
|
||||
);
|
||||
|
||||
if (isset($data[0])) {
|
||||
$data = $data[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($data['id'])) {
|
||||
$socialCache['id'] = $data['id'];
|
||||
|
||||
$info = $this->matchUpData($data);
|
||||
$info['profileHandle'] = $data['screen_name'];
|
||||
// remove the size variant
|
||||
$image = $data['profile_image_url_https'];
|
||||
$image = str_replace(['_normal', '_bigger', '_mini'], '', $image);
|
||||
$info['profileImage'] = $image;
|
||||
|
||||
$socialCache['profile'] = $info;
|
||||
$socialCache['lastRefresh'] = new \DateTime();
|
||||
|
||||
$this->getMauticLead($info, $this->persistNewLead, $socialCache, $identifier);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getPublicActivity($identifier, &$socialCache): void
|
||||
{
|
||||
if (!isset($socialCache['id'])) {
|
||||
$this->getUserData($identifier, $socialCache);
|
||||
|
||||
if (!isset($socialCache['id'])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$id = $socialCache['id'];
|
||||
|
||||
// due to the way Twitter filters, get more than 10 tweets
|
||||
$data = $this->makeRequest($this->getApiUrl('/statuses/user_timeline'), [
|
||||
'user_id' => $id,
|
||||
'exclude_replies' => 'true',
|
||||
'count' => 25,
|
||||
'trim_user' => 'true',
|
||||
]);
|
||||
|
||||
if (!empty($data) && count($data)) {
|
||||
$socialCache['has']['activity'] = true;
|
||||
$socialCache['activity'] = [
|
||||
'tweets' => [],
|
||||
'photos' => [],
|
||||
'tags' => [],
|
||||
];
|
||||
|
||||
foreach ($data as $k => $d) {
|
||||
if (10 == $k) {
|
||||
break;
|
||||
}
|
||||
|
||||
$tweet = [
|
||||
'tweet' => $d['text'],
|
||||
'url' => "https://twitter.com/{$id}/status/{$d['id']}",
|
||||
'coordinates' => $d['coordinates'],
|
||||
'published' => $d['created_at'],
|
||||
];
|
||||
|
||||
$socialCache['activity']['tweets'][] = $tweet;
|
||||
|
||||
// images
|
||||
if (isset($d['entities']['media'])) {
|
||||
foreach ($d['entities']['media'] as $m) {
|
||||
if ('photo' == $m['type']) {
|
||||
$photo = [
|
||||
'url' => ($m['media_url_https'] ?? $m['media_url']),
|
||||
];
|
||||
|
||||
$socialCache['activity']['photos'][] = $photo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hastags
|
||||
if (isset($d['entities']['hashtags'])) {
|
||||
foreach ($d['entities']['hashtags'] as $h) {
|
||||
if (isset($socialCache['activity']['tags'][$h['text']])) {
|
||||
++$socialCache['activity']['tags'][$h['text']]['count'];
|
||||
} else {
|
||||
$socialCache['activity']['tags'][$h['text']] = [
|
||||
'count' => 1,
|
||||
'url' => 'https://twitter.com/search?q=%23'.$h['text'],
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAvailableLeadFields($settings = []): array
|
||||
{
|
||||
return [
|
||||
'profileHandle' => ['type' => 'string'],
|
||||
'name' => ['type' => 'string'],
|
||||
'location' => ['type' => 'string'],
|
||||
'description' => ['type' => 'string'],
|
||||
'url' => ['type' => 'string'],
|
||||
'time_zone' => ['type' => 'string'],
|
||||
'lang' => ['type' => 'string'],
|
||||
'email' => ['type' => 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
public function cleanIdentifier($identifier): string
|
||||
{
|
||||
if (preg_match('#https?://twitter.com/(.*?)(/.*?|$)#i', $identifier, $match)) {
|
||||
// extract the handle
|
||||
$identifier = $match[1];
|
||||
} elseif (str_starts_with($identifier, '@')) {
|
||||
$identifier = substr($identifier, 1);
|
||||
}
|
||||
|
||||
return urlencode($identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param bool $postAuthorization
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function parseCallbackResponse($data, $postAuthorization = false)
|
||||
{
|
||||
if ($postAuthorization) {
|
||||
parse_str($data, $parsed);
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
return json_decode($data, true);
|
||||
}
|
||||
|
||||
public function getFormType(): string
|
||||
{
|
||||
return TwitterType::class;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user