View source
<?php
namespace Drupal\instagram_feeds;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Utility\Token;
use Drupal\instagram_feeds\Entity\InstagramAccountInterface;
use Drupal\instagram_feeds\Event\InstagramPostsObtainedEvent;
use Drupal\instagram_feeds\Event\MediaEntityInstantiatedEvent;
use GuzzleHttp\Client;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class CronHandler {
use InstagramApiTrait;
const SETTINGS = 'instagram_feeds.settings';
protected $config;
protected $mediaStorage;
protected $eventDispatcher;
protected $entityTypeManager;
protected $entityFieldManager;
protected $instagramAccounts;
protected $mediaTypeSources = [];
protected $mediaTypeSourceIDs = [];
public function __construct(ConfigFactoryInterface $config_factory, Client $http_client, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, EventDispatcherInterface $event_dispatcher, Token $token, LoggerChannelFactoryInterface $logger_factory) {
$config = $config_factory
->get(self::SETTINGS);
$this->config = $config;
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
$this->mediaStorage = $entity_type_manager
->getStorage('media');
$this->eventDispatcher = $event_dispatcher;
$this
->setHttpClient($http_client)
->setToken($token)
->setLogger($logger_factory);
}
protected function getInstagramAccounts() : array {
if (!isset($this->instagramAccounts)) {
$this->instagramAccounts = [];
$instagram_accounts = $this->entityTypeManager
->getStorage('instagram_account')
->loadMultiple();
foreach ($instagram_accounts as $account) {
if ($account
->isPublished() && $account
->tokenIsValid()) {
$this->instagramAccounts[$account
->id()] = $account;
}
}
}
return $this->instagramAccounts;
}
public function importInstagramPosts() {
foreach ($this
->getInstagramAccounts() as $account) {
$this
->processAccount($account);
}
return $this;
}
protected function getMedia(InstagramAccountInterface $account) {
try {
$request_url = $account::INSTAGRAM_GRAPH_ENDPOINT . '/me/media?' . http_build_query([
'fields' => 'id,caption,media_type,media_url,permalink,thumbnail_url,timestamp,username',
'access_token' => $account
->getToken(),
]);
$body = $this
->getInstagramResponceContents($request_url, TRUE);
$result = array_filter($body['data'] ?: [], [
$this,
'filterPostByPermalink',
]);
} catch (\Exception $e) {
$this->logger
->error($e
->getMessage());
}
$event = new InstagramPostsObtainedEvent($this->config, $account, $result ?? []);
$this->eventDispatcher
->dispatch(InstagramPostsObtainedEvent::getEventName(), $event);
return $event->posts;
}
protected function filterPostByPermalink($post) : bool {
return (bool) $post['permalink'];
}
protected function processAccount(InstagramAccountInterface $account) {
$post_count = 0;
$max_posts = $account
->getCronLimit();
$last_imported_timestamp = $account
->getLastImportTimestamp();
foreach ($this
->getMedia($account) as $post) {
if ($post_count >= $max_posts) {
break;
}
if ($post['timestamp'] > $account
->getLastImportTimestamp()) {
$this
->createMediaEntity($post, $account);
$last_imported_timestamp = $post['timestamp'];
$post_count++;
}
}
if ($post_count > 0) {
$account
->setLastImportTimestamp($last_imported_timestamp)
->save();
}
$logger_context = [
'@account' => $account
->label(),
'@count' => $post_count,
];
$this->logger
->info("@count post(s) imported from @account account.<br />\n", $logger_context);
}
protected function createMediaEntity(array $post, InstagramAccountInterface $account) {
$media_type = $account
->getMediaType();
$mapping = $this->config
->get('mapping.' . $media_type);
$source_plugin = $this
->getInstagramSourcePluginID($media_type);
$entity_array = [
'bundle' => $media_type,
];
switch ($source_plugin) {
case 'image':
$image = system_retrieve_file($post['media_url'], NULL, TRUE);
$entity_array[$this
->getInstagramSourceField($media_type)] = [
'target_id' => $image
->id(),
];
break;
case 'instagram':
$entity_array[$this
->getInstagramSourceField($media_type)] = $post['permalink'];
break;
}
foreach ($mapping as $entity_field_name => $post_field_name) {
if ($entity_field_name == 'name') {
$token_data = [
'instagram_account' => $account,
'instagram_post' => $post,
];
$entity_array['name'] = empty($post_field_name) ? $account
->label() . ' (' . date('m/d/Y', $post['timestamp']) . ')' : $this
->token()
->replace($post_field_name, $token_data, [
'clear' => TRUE,
]);
$entity_array['name'] = trim($entity_array['name']);
}
elseif ($post_field_name == 'tags' && $post[$post_field_name]) {
$fields = $this->entityFieldManager
->getFieldDefinitions('media', $media_type);
$field_definition = $fields[$entity_field_name];
$entity_array[$entity_field_name] = 'entity_reference' == $field_definition
->getType() ? $this
->getTerms($field_definition, $post['tags']) : $post['tags'];
}
elseif (!empty($post_field_name) && $post[$post_field_name]) {
$entity_array[$entity_field_name] = $post[$post_field_name];
}
}
$media_entity = $this->mediaStorage
->create($entity_array);
$event = new MediaEntityInstantiatedEvent($this->config, $account, $media_entity, $post);
$this->eventDispatcher
->dispatch(MediaEntityInstantiatedEvent::getEventName(), $event);
$event->mediaEntity
->save();
}
protected function getTerms(FieldDefinitionInterface $field_definition, $tags) {
$result = [];
$settings = $field_definition
->getSetting('handler_settings');
$term_storage = $this->entityTypeManager
->getStorage('taxonomy_term');
$vocabulary = $settings['auto_create_bundle'] ?: reset($settings['target_bundles']);
$terms = $term_storage
->loadByProperties([
'name' => $tags,
'vid' => $vocabulary,
]);
$existing_tags = [];
foreach ($terms as $term) {
$result[]['target_id'] = $term
->id();
$existing_tags[] = $term
->label();
}
$create_tags = $settings['auto_create'] ? array_diff($tags, $existing_tags) : [];
foreach ($create_tags as $tag) {
$term = $term_storage
->create([
'name' => $tag,
'vid' => $vocabulary,
]);
$term
->save();
$result[]['target_id'] = $term
->id();
}
return $result;
}
protected function getInstagramSourceField($media_type_name) : string {
if (!isset($this->mediaTypeSources[$media_type_name])) {
$mediaType = $this->entityTypeManager
->getStorage('media_type')
->load($media_type_name);
$this->mediaTypeSources[$media_type_name] = $mediaType
->getSource()
->getSourceFieldDefinition($mediaType)
->getName();
}
return $this->mediaTypeSources[$media_type_name];
}
protected function getInstagramSourcePluginID($media_type_name) : string {
if (!isset($this->mediaTypeSourceIDs[$media_type_name])) {
$mediaType = $this->entityTypeManager
->getStorage('media_type')
->load($media_type_name);
$this->mediaTypeSourceIDs[$media_type_name] = $mediaType
->getSource()
->getPluginId();
}
return $this->mediaTypeSourceIDs[$media_type_name];
}
public function refreshTokens() {
$current_time = \Drupal::time()
->getRequestTime();
$frequency = $this->config
->get('refresh_frequency');
foreach ($this
->getInstagramAccounts() as $account) {
if ($current_time > $account
->getTokenExpirationTime() - 5184000 + $frequency) {
$account
->refreshToken($this->httpClient, TRUE);
}
}
return $this;
}
}