3 namespace Drupal\media_entity_instagram\Plugin\MediaEntity\Type;
5 use Drupal\Core\Config\ConfigFactoryInterface;
6 use Drupal\Core\Entity\EntityFieldManagerInterface;
7 use Drupal\Core\Entity\EntityTypeManagerInterface;
8 use Drupal\Core\Form\FormStateInterface;
9 use Drupal\media_entity\MediaInterface;
10 use Drupal\media_entity\MediaTypeBase;
11 use Drupal\media_entity\MediaTypeException;
12 use Symfony\Component\DependencyInjection\ContainerInterface;
13 use Instagram\Instagram as InstagramApi;
16 * Provides media type plugin for Instagram.
20 * label = @Translation("Instagram"),
21 * description = @Translation("Provides business logic and metadata for Instagram.")
24 class Instagram extends MediaTypeBase {
27 * Config factory service.
29 * @var \Drupal\Core\Config\ConfigFactoryInterface
31 protected $configFactory;
34 * Constructs a new class instance.
36 * @param array $configuration
37 * A configuration array containing information about the plugin instance.
38 * @param string $plugin_id
39 * The plugin_id for the plugin instance.
40 * @param mixed $plugin_definition
41 * The plugin implementation definition.
42 * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
43 * Entity type manager service.
44 * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
45 * Entity field manager service.
46 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
47 * Config factory service.
49 public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, ConfigFactoryInterface $config_factory) {
50 parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $entity_field_manager, $config_factory->get('media_entity.settings'));
51 $this->configFactory = $config_factory;
57 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
62 $container->get('entity_type.manager'),
63 $container->get('entity_field.manager'),
64 $container->get('config.factory')
71 public function defaultConfiguration() {
73 'use_instagram_api' => FALSE,
78 * List of validation regular expressions.
82 public static $validationRegexp = array(
83 '@((http|https):){0,1}//(www\.){0,1}instagram\.com/p/(?<shortcode>[a-z0-9_-]+)@i' => 'shortcode',
84 '@((http|https):){0,1}//(www\.){0,1}instagr\.am/p/(?<shortcode>[a-z0-9_-]+)@i' => 'shortcode',
90 public function providedFields() {
92 'shortcode' => $this->t('Instagram shortcode'),
95 if ($this->configuration['use_instagram_api']) {
97 'id' => $this->t('Media ID'),
98 'type' => $this->t('Media type: image or video'),
99 'thumbnail' => $this->t('Link to the thumbnail'),
100 'thumbnail_local' => $this->t("Copies thumbnail locally and return it's URI"),
101 'thumbnail_local_uri' => $this->t('Returns local URI of the thumbnail'),
102 'username' => $this->t('Author of the post'),
103 'caption' => $this->t('Caption'),
104 'tags' => $this->t('Tags'),
114 public function getField(MediaInterface $media, $name) {
115 $matches = $this->matchRegexp($media);
117 if (!$matches['shortcode']) {
121 if ($name == 'shortcode') {
122 return $matches['shortcode'];
125 // If we have auth settings return the other fields.
126 if ($this->configuration['use_instagram_api'] && $instagram = $this->fetchInstagram($matches['shortcode'])) {
129 if (isset($instagram->id)) {
130 return $instagram->id;
135 if (isset($instagram->type)) {
136 return $instagram->type;
141 if (isset($instagram->images->thumbnail->url)) {
142 return $instagram->images->thumbnail->url;
146 case 'thumbnail_local':
147 if (isset($instagram->images->thumbnail->url)) {
148 $local_uri = $this->configFactory->get('media_entity_instagram.settings')->get('local_images') . '/' . $matches['shortcode'] . '.' . pathinfo($instagram->images->thumbnail->url, PATHINFO_EXTENSION);
150 if (!file_exists($local_uri)) {
151 file_prepare_directory($local_uri, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
153 $image = file_get_contents($local_uri);
154 file_unmanaged_save_data($image, $local_uri, FILE_EXISTS_REPLACE);
161 case 'thumbnail_local_uri':
162 if (isset($instagram->images->thumbnail->url)) {
163 return $this->configFactory->get('media_entity_instagram.settings')->get('local_images') . '/' . $matches['shortcode'] . '.' . pathinfo($instagram->images->thumbnail->url, PATHINFO_EXTENSION);
168 if (isset($instagram->user->username)) {
169 return $instagram->user->username;
174 if (isset($instagram->caption->text)) {
175 return $instagram->caption->text;
180 if (isset($instagram->tags)) {
181 return implode(" ", $instagram->tags);
193 public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
195 $bundle = $form_state->getFormObject()->getEntity();
196 $allowed_field_types = ['string', 'string_long', 'link'];
197 foreach ($this->entityFieldManager->getFieldDefinitions('media', $bundle->id()) as $field_name => $field) {
198 if (in_array($field->getType(), $allowed_field_types) && !$field->getFieldStorageDefinition()->isBaseField()) {
199 $options[$field_name] = $field->getLabel();
203 $form['source_field'] = [
205 '#title' => $this->t('Field with source information'),
206 '#description' => $this->t('Field on media entity that stores Instagram embed code or URL. You can create a bundle without selecting a value for this dropdown initially. This dropdown can be populated after adding fields to the bundle.'),
207 '#default_value' => empty($this->configuration['source_field']) ? NULL : $this->configuration['source_field'],
208 '#options' => $options,
211 $form['use_instagram_api'] = [
213 '#title' => $this->t('Whether to use Instagram api to fetch instagrams or not.'),
214 '#description' => $this->t("In order to use Instagram's api you have to create a developer account and an application. For more information consult the readme file."),
215 '#default_value' => empty($this->configuration['use_instagram_api']) ? 0 : $this->configuration['use_instagram_api'],
218 1 => $this->t('Yes'),
222 // @todo Evaluate if this should be a site-wide configuration.
223 $form['client_id'] = [
224 '#type' => 'textfield',
225 '#title' => $this->t('Client ID'),
226 '#default_value' => empty($this->configuration['client_id']) ? NULL : $this->configuration['client_id'],
229 ':input[name="type_configuration[instagram][use_instagram_api]"]' => ['value' => '1'],
240 public function attachConstraints(MediaInterface $media) {
241 parent::attachConstraints($media);
243 if (isset($this->configuration['source_field'])) {
244 $source_field_name = $this->configuration['source_field'];
245 if ($media->hasField($source_field_name)) {
246 foreach ($media->get($source_field_name) as &$embed_code) {
247 /** @var \Drupal\Core\TypedData\DataDefinitionInterface $typed_data */
248 $typed_data = $embed_code->getDataDefinition();
249 $typed_data->addConstraint('InstagramEmbedCode');
256 * Runs preg_match on embed code/URL.
258 * @param MediaInterface $media
262 * Array of preg matches or FALSE if no match.
266 protected function matchRegexp(MediaInterface $media) {
269 if (isset($this->configuration['source_field'])) {
270 $source_field = $this->configuration['source_field'];
271 if ($media->hasField($source_field)) {
272 $property_name = $media->{$source_field}->first()->mainPropertyName();
273 foreach (static::$validationRegexp as $pattern => $key) {
274 if (preg_match($pattern, $media->{$source_field}->{$property_name}, $matches)) {
284 * Get a single instagram.
286 * @param string $shortcode
287 * The instagram shortcode.
289 protected function fetchInstagram($shortcode) {
290 $instagram = &drupal_static(__FUNCTION__);
292 if (!isset($instagram)) {
293 // Check for dependencies.
294 // @todo There is perhaps a better way to do that.
295 if (!class_exists('\Instagram\Instagram')) {
296 drupal_set_message($this->t('Instagram library is not available. Consult the README.md for installation instructions.'), 'error');
300 if (!isset($this->configuration['client_id'])) {
301 drupal_set_message($this->t('The client ID is not available. Consult the README.md for installation instructions.'), 'error');
304 if (empty($this->configuration['client_id'])) {
305 drupal_set_message($this->t('The client ID is missing. Please add it in your Instagram settings.'), 'error');
308 $instagram_object = new InstagramApi();
309 $instagram_object->setClientID($this->configuration['client_id']);
310 $result = $instagram_object->getMediaByShortcode($shortcode)->getData();
316 throw new MediaTypeException(NULL, 'The media could not be retrieved.');
324 public function getDefaultThumbnail() {
325 return $this->config->get('icon_base') . '/instagram.png';
331 public function thumbnail(MediaInterface $media) {
332 if ($local_image = $this->getField($media, 'thumbnail_local')) {
336 return $this->getDefaultThumbnail();
342 public function getDefaultName(MediaInterface $media) {
343 // Try to get some fields that need the API, if not available, just use the
344 // shortcode as default name.
346 $username = $this->getField($media, 'username');
347 $id = $this->getField($media, 'id');
348 if ($username && $id) {
349 return $username . ' - ' . $id;
352 $code = $this->getField($media, 'shortcode');
358 return parent::getDefaultName($media);