Initial commit: CloudOps infrastructure platform

This commit is contained in:
root
2026-04-09 19:58:57 +02:00
commit 1166a52f26
7762 changed files with 839452 additions and 0 deletions

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}