Yaffs site version 1.1
[yaffs-website] / web / modules / contrib / media_entity_instagram / src / Plugin / MediaEntity / Type / Instagram.php
1 <?php
2
3 namespace Drupal\media_entity_instagram\Plugin\MediaEntity\Type;
4
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_instagram\InstagramEmbedFetcher;
12 use GuzzleHttp\Client;
13 use Symfony\Component\DependencyInjection\ContainerInterface;
14
15 /**
16  * Provides media type plugin for Instagram.
17  *
18  * @MediaType(
19  *   id = "instagram",
20  *   label = @Translation("Instagram"),
21  *   description = @Translation("Provides business logic and metadata for Instagram.")
22  * )
23  */
24 class Instagram extends MediaTypeBase {
25
26   /**
27    * Config factory service.
28    *
29    * @var \Drupal\Core\Config\ConfigFactoryInterface
30    */
31   protected $configFactory;
32
33   /**
34    * The instagram fetcher.
35    *
36    * @var \Drupal\media_entity_instagram\InstagramEmbedFetcher
37    */
38   protected $fetcher;
39
40   /**
41    * Guzzle client.
42    *
43    * @var \GuzzleHttp\Client
44    */
45   protected $httpClient;
46
47   /**
48    * Constructs a new class instance.
49    *
50    * @param array $configuration
51    *   A configuration array containing information about the plugin instance.
52    * @param string $plugin_id
53    *   The plugin_id for the plugin instance.
54    * @param mixed $plugin_definition
55    *   The plugin implementation definition.
56    * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
57    *   Entity type manager service.
58    * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
59    *   Entity field manager service.
60    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
61    *   Config factory service.
62    * @param \Drupal\media_entity_instagram\InstagramEmbedFetcher $fetcher
63    *   Instagram fetcher service.
64    * @param \GuzzleHttp\Client $httpClient
65    *   Guzzle client.
66    */
67   public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, ConfigFactoryInterface $config_factory, InstagramEmbedFetcher $fetcher, Client $httpClient) {
68     parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $entity_field_manager, $config_factory->get('media_entity.settings'));
69     $this->configFactory = $config_factory;
70     $this->fetcher = $fetcher;
71     $this->httpClient = $httpClient;
72   }
73
74   /**
75    * {@inheritdoc}
76    */
77   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
78     return new static(
79       $configuration,
80       $plugin_id,
81       $plugin_definition,
82       $container->get('entity_type.manager'),
83       $container->get('entity_field.manager'),
84       $container->get('config.factory'),
85       $container->get('media_entity_instagram.instagram_embed_fetcher'),
86       $container->get('http_client')
87     );
88   }
89
90   /**
91    * List of validation regular expressions.
92    *
93    * @var array
94    */
95   public static $validationRegexp = [
96     '@((http|https):){0,1}//(www\.){0,1}instagram\.com/p/(?<shortcode>[a-z0-9_-]+)@i' => 'shortcode',
97     '@((http|https):){0,1}//(www\.){0,1}instagr\.am/p/(?<shortcode>[a-z0-9_-]+)@i' => 'shortcode',
98   ];
99
100   /**
101    * {@inheritdoc}
102    */
103   public function providedFields() {
104     return [
105       'shortcode' => $this->t('Instagram shortcode'),
106       'id' => $this->t('Media ID'),
107       'type' => $this->t('Media type: image or video'),
108       'thumbnail' => $this->t('Link to the thumbnail'),
109       'thumbnail_local' => $this->t("Copies thumbnail locally and return it's URI"),
110       'thumbnail_local_uri' => $this->t('Returns local URI of the thumbnail'),
111       'username' => $this->t('Author of the post'),
112       'caption' => $this->t('Caption'),
113     ];
114   }
115
116   /**
117    * {@inheritdoc}
118    */
119   public function getField(MediaInterface $media, $name) {
120     $matches = $this->matchRegexp($media);
121
122     if (!$matches['shortcode']) {
123       return FALSE;
124     }
125
126     if ($name == 'shortcode') {
127       return $matches['shortcode'];
128     }
129
130     // If we have auth settings return the other fields.
131     if ($instagram = $this->fetcher->fetchInstagramEmbed($matches['shortcode'])) {
132       switch ($name) {
133         case 'id':
134           if (isset($instagram['media_id'])) {
135             return $instagram['media_id'];
136           }
137           return FALSE;
138
139         case 'type':
140           if (isset($instagram['type'])) {
141             return $instagram['type'];
142           }
143           return FALSE;
144
145         case 'thumbnail':
146           return 'http://instagram.com/p/' . $matches['shortcode'] . '/media/?size=m';
147
148         case 'thumbnail_local':
149           $local_uri = $this->getField($media, 'thumbnail_local_uri');
150
151           if ($local_uri) {
152             if (file_exists($local_uri)) {
153               return $local_uri;
154             }
155             else {
156
157               $directory = dirname($local_uri);
158               file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
159
160               $image_url = $this->getField($media, 'thumbnail');
161
162               $response = $this->httpClient->get($image_url);
163               if ($response->getStatusCode() == 200) {
164                 return file_unmanaged_save_data($response->getBody(), $local_uri, FILE_EXISTS_REPLACE);
165               }
166             }
167           }
168           return FALSE;
169
170         case 'thumbnail_local_uri':
171           if (isset($instagram['thumbnail_url'])) {
172             return $this->configFactory->get('media_entity_instagram.settings')->get('local_images') . '/' . $matches['shortcode'] . '.' . pathinfo(parse_url($instagram['thumbnail_url'], PHP_URL_PATH), PATHINFO_EXTENSION);
173           }
174           return FALSE;
175
176         case 'username':
177           if (isset($instagram['author_name'])) {
178             return $instagram['author_name'];
179           }
180           return FALSE;
181
182         case 'caption':
183           if (isset($instagram['title'])) {
184             return $instagram['title'];
185           }
186           return FALSE;
187
188       }
189     }
190
191     return FALSE;
192   }
193
194   /**
195    * {@inheritdoc}
196    */
197   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
198     $options = [];
199     $bundle = $form_state->getFormObject()->getEntity();
200     $allowed_field_types = ['string', 'string_long', 'link'];
201     foreach ($this->entityFieldManager->getFieldDefinitions('media', $bundle->id()) as $field_name => $field) {
202       if (in_array($field->getType(), $allowed_field_types) && !$field->getFieldStorageDefinition()->isBaseField()) {
203         $options[$field_name] = $field->getLabel();
204       }
205     }
206
207     $form['source_field'] = [
208       '#type' => 'select',
209       '#title' => $this->t('Field with source information'),
210       '#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.'),
211       '#default_value' => empty($this->configuration['source_field']) ? NULL : $this->configuration['source_field'],
212       '#options' => $options,
213     ];
214
215     return $form;
216   }
217
218   /**
219    * {@inheritdoc}
220    */
221   public function attachConstraints(MediaInterface $media) {
222     parent::attachConstraints($media);
223
224     if (isset($this->configuration['source_field'])) {
225       $source_field_name = $this->configuration['source_field'];
226       if ($media->hasField($source_field_name)) {
227         foreach ($media->get($source_field_name) as &$embed_code) {
228           /** @var \Drupal\Core\TypedData\DataDefinitionInterface $typed_data */
229           $typed_data = $embed_code->getDataDefinition();
230           $typed_data->addConstraint('InstagramEmbedCode');
231         }
232       }
233     }
234   }
235
236   /**
237    * Runs preg_match on embed code/URL.
238    *
239    * @param \Drupal\media_entity\MediaInterface $media
240    *   Media object.
241    *
242    * @return array|bool
243    *   Array of preg matches or FALSE if no match.
244    *
245    * @see preg_match()
246    */
247   protected function matchRegexp(MediaInterface $media) {
248     $matches = [];
249
250     if (isset($this->configuration['source_field'])) {
251       $source_field = $this->configuration['source_field'];
252       if ($media->hasField($source_field)) {
253         $property_name = $media->{$source_field}->first()->mainPropertyName();
254         foreach (static::$validationRegexp as $pattern => $key) {
255           if (preg_match($pattern, $media->{$source_field}->{$property_name}, $matches)) {
256             return $matches;
257           }
258         }
259       }
260     }
261     return FALSE;
262   }
263
264   /**
265    * {@inheritdoc}
266    */
267   public function getDefaultThumbnail() {
268     return $this->config->get('icon_base') . '/instagram.png';
269   }
270
271   /**
272    * {@inheritdoc}
273    */
274   public function thumbnail(MediaInterface $media) {
275     if ($local_image = $this->getField($media, 'thumbnail_local')) {
276       return $local_image;
277     }
278
279     return $this->getDefaultThumbnail();
280   }
281
282   /**
283    * {@inheritdoc}
284    */
285   public function getDefaultName(MediaInterface $media) {
286     // Try to get some fields that need the API, if not available, just use the
287     // shortcode as default name.
288     $username = $this->getField($media, 'username');
289     $id = $this->getField($media, 'id');
290     if ($username && $id) {
291       return $username . ' - ' . $id;
292     }
293     else {
294       $code = $this->getField($media, 'shortcode');
295       if (!empty($code)) {
296         return $code;
297       }
298     }
299
300     return parent::getDefaultName($media);
301   }
302
303 }