1<?php
2
3namespace Drupal\aggregator\Entity;
4
5use Drupal\aggregator\FeedStorageInterface;
6use Drupal\Core\Entity\ContentEntityBase;
7use Drupal\Core\Entity\EntityTypeInterface;
8use Drupal\Core\Field\BaseFieldDefinition;
9use Drupal\Core\Entity\EntityStorageInterface;
10use Drupal\aggregator\FeedInterface;
11
12/**
13 * Defines the aggregator feed entity class.
14 *
15 * @ContentEntityType(
16 *   id = "aggregator_feed",
17 *   label = @Translation("Aggregator feed"),
18 *   label_collection = @Translation("Aggregator feeds"),
19 *   label_singular = @Translation("aggregator feed"),
20 *   label_plural = @Translation("aggregator feeds"),
21 *   label_count = @PluralTranslation(
22 *     singular = "@count aggregator feed",
23 *     plural = "@count aggregator feeds",
24 *   ),
25 *   handlers = {
26 *     "storage" = "Drupal\aggregator\FeedStorage",
27 *     "storage_schema" = "Drupal\aggregator\FeedStorageSchema",
28 *     "view_builder" = "Drupal\aggregator\FeedViewBuilder",
29 *     "access" = "Drupal\aggregator\FeedAccessControlHandler",
30 *     "views_data" = "Drupal\aggregator\AggregatorFeedViewsData",
31 *     "form" = {
32 *       "default" = "Drupal\aggregator\FeedForm",
33 *       "delete" = "Drupal\aggregator\Form\FeedDeleteForm",
34 *       "delete_items" = "Drupal\aggregator\Form\FeedItemsDeleteForm",
35 *     },
36 *     "route_provider" = {
37 *       "html" = "Drupal\aggregator\FeedHtmlRouteProvider",
38 *     },
39 *   },
40 *   links = {
41 *     "canonical" = "/aggregator/sources/{aggregator_feed}",
42 *     "edit-form" = "/aggregator/sources/{aggregator_feed}/configure",
43 *     "delete-form" = "/aggregator/sources/{aggregator_feed}/delete",
44 *   },
45 *   field_ui_base_route = "aggregator.admin_overview",
46 *   base_table = "aggregator_feed",
47 *   render_cache = FALSE,
48 *   entity_keys = {
49 *     "id" = "fid",
50 *     "label" = "title",
51 *     "langcode" = "langcode",
52 *     "uuid" = "uuid",
53 *   }
54 * )
55 */
56class Feed extends ContentEntityBase implements FeedInterface {
57
58  /**
59   * {@inheritdoc}
60   */
61  public function label() {
62    return $this->get('title')->value;
63  }
64
65  /**
66   * {@inheritdoc}
67   */
68  public function deleteItems() {
69    \Drupal::service('aggregator.items.importer')->delete($this);
70
71    // Reset feed.
72    $this->setLastCheckedTime(0);
73    $this->setHash('');
74    $this->setEtag('');
75    $this->setLastModified(0);
76    $this->save();
77
78    return $this;
79  }
80
81  /**
82   * {@inheritdoc}
83   */
84  public function refreshItems() {
85    $success = \Drupal::service('aggregator.items.importer')->refresh($this);
86
87    // Regardless of successful or not, indicate that it has been checked.
88    $this->setLastCheckedTime(REQUEST_TIME);
89    $this->setQueuedTime(0);
90    $this->save();
91
92    return $success;
93  }
94
95  /**
96   * {@inheritdoc}
97   */
98  public static function preCreate(EntityStorageInterface $storage, array &$values) {
99    $values += [
100      'link' => '',
101      'description' => '',
102      'image' => '',
103    ];
104  }
105
106  /**
107   * {@inheritdoc}
108   */
109  public static function preDelete(EntityStorageInterface $storage, array $entities) {
110    foreach ($entities as $entity) {
111      // Notify processors to delete stored items.
112      \Drupal::service('aggregator.items.importer')->delete($entity);
113    }
114  }
115
116  /**
117   * {@inheritdoc}
118   */
119  public static function postDelete(EntityStorageInterface $storage, array $entities) {
120    parent::postDelete($storage, $entities);
121    if (\Drupal::moduleHandler()->moduleExists('block')) {
122      // Make sure there are no active blocks for these feeds.
123      $ids = \Drupal::entityQuery('block')
124        ->condition('plugin', 'aggregator_feed_block')
125        ->condition('settings.feed', array_keys($entities))
126        ->execute();
127      if ($ids) {
128        $block_storage = \Drupal::entityTypeManager()->getStorage('block');
129        $block_storage->delete($block_storage->loadMultiple($ids));
130      }
131    }
132  }
133
134  /**
135   * {@inheritdoc}
136   */
137  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
138    /** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
139    $fields = parent::baseFieldDefinitions($entity_type);
140
141    $fields['fid']->setLabel(t('Feed ID'))
142      ->setDescription(t('The ID of the aggregator feed.'));
143
144    $fields['uuid']->setDescription(t('The aggregator feed UUID.'));
145
146    $fields['langcode']->setLabel(t('Language code'))
147      ->setDescription(t('The feed language code.'));
148
149    $fields['title'] = BaseFieldDefinition::create('string')
150      ->setLabel(t('Title'))
151      ->setDescription(t('The name of the feed (or the name of the website providing the feed).'))
152      ->setRequired(TRUE)
153      ->setSetting('max_length', 255)
154      ->setDisplayOptions('form', [
155        'type' => 'string_textfield',
156        'weight' => -5,
157      ])
158      ->setDisplayConfigurable('form', TRUE)
159      ->addConstraint('FeedTitle');
160
161    $fields['url'] = BaseFieldDefinition::create('uri')
162      ->setLabel(t('URL'))
163      ->setDescription(t('The fully-qualified URL of the feed.'))
164      ->setRequired(TRUE)
165      ->setDisplayOptions('form', [
166        'type' => 'uri',
167        'weight' => -3,
168      ])
169      ->setDisplayConfigurable('form', TRUE)
170      ->addConstraint('FeedUrl');
171
172    $intervals = [900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200];
173    $period = array_map([\Drupal::service('date.formatter'), 'formatInterval'], array_combine($intervals, $intervals));
174    $period[FeedStorageInterface::CLEAR_NEVER] = t('Never');
175
176    $fields['refresh'] = BaseFieldDefinition::create('list_integer')
177      ->setLabel(t('Update interval'))
178      ->setDescription(t('The length of time between feed updates. Requires a correctly configured cron maintenance task.'))
179      ->setDefaultValue(3600)
180      ->setSetting('unsigned', TRUE)
181      ->setRequired(TRUE)
182      ->setSetting('allowed_values', $period)
183      ->setDisplayOptions('form', [
184        'type' => 'options_select',
185        'weight' => -2,
186      ])
187      ->setDisplayConfigurable('form', TRUE);
188
189    $fields['checked'] = BaseFieldDefinition::create('timestamp')
190      ->setLabel(t('Checked', [], ['context' => 'Examined']))
191      ->setDescription(t('Last time feed was checked for new items, as Unix timestamp.'))
192      ->setDefaultValue(0)
193      ->setDisplayOptions('view', [
194        'label' => 'inline',
195        'type' => 'timestamp_ago',
196        'weight' => 1,
197      ])
198      ->setDisplayConfigurable('view', TRUE);
199
200    $fields['queued'] = BaseFieldDefinition::create('timestamp')
201      ->setLabel(t('Queued'))
202      ->setDescription(t('Time when this feed was queued for refresh, 0 if not queued.'))
203      ->setDefaultValue(0);
204
205    $fields['link'] = BaseFieldDefinition::create('uri')
206      ->setLabel(t('URL'))
207      ->setDescription(t('The link of the feed.'))
208      ->setDisplayOptions('view', [
209        'label' => 'inline',
210        'weight' => 4,
211      ])
212      ->setDisplayConfigurable('view', TRUE);
213
214    $fields['description'] = BaseFieldDefinition::create('string_long')
215      ->setLabel(t('Description'))
216      ->setDescription(t("The parent website's description that comes from the @description element in the feed.", ['@description' => '<description>']));
217
218    $fields['image'] = BaseFieldDefinition::create('uri')
219      ->setLabel(t('Image'))
220      ->setDescription(t('An image representing the feed.'));
221
222    $fields['hash'] = BaseFieldDefinition::create('string')
223      ->setLabel(t('Hash'))
224      ->setSetting('is_ascii', TRUE)
225      ->setDescription(t('Calculated hash of the feed data, used for validating cache.'));
226
227    $fields['etag'] = BaseFieldDefinition::create('string')
228      ->setLabel(t('Etag'))
229      ->setDescription(t('Entity tag HTTP response header, used for validating cache.'));
230
231    // This is updated by the fetcher and not when the feed is saved, therefore
232    // it's a timestamp and not a changed field.
233    $fields['modified'] = BaseFieldDefinition::create('timestamp')
234      ->setLabel(t('Modified'))
235      ->setDescription(t('When the feed was last modified, as a Unix timestamp.'));
236
237    return $fields;
238  }
239
240  /**
241   * {@inheritdoc}
242   */
243  public function getUrl() {
244    return $this->get('url')->value;
245  }
246
247  /**
248   * {@inheritdoc}
249   */
250  public function getRefreshRate() {
251    return $this->get('refresh')->value;
252  }
253
254  /**
255   * {@inheritdoc}
256   */
257  public function getLastCheckedTime() {
258    return $this->get('checked')->value;
259  }
260
261  /**
262   * {@inheritdoc}
263   */
264  public function getQueuedTime() {
265    return $this->get('queued')->value;
266  }
267
268  /**
269   * {@inheritdoc}
270   */
271  public function getWebsiteUrl() {
272    return $this->get('link')->value;
273  }
274
275  /**
276   * {@inheritdoc}
277   */
278  public function getDescription() {
279    return $this->get('description')->value;
280  }
281
282  /**
283   * {@inheritdoc}
284   */
285  public function getImage() {
286    return $this->get('image')->value;
287  }
288
289  /**
290   * {@inheritdoc}
291   */
292  public function getHash() {
293    return $this->get('hash')->value;
294  }
295
296  /**
297   * {@inheritdoc}
298   */
299  public function getEtag() {
300    return $this->get('etag')->value;
301  }
302
303  /**
304   * {@inheritdoc}
305   */
306  public function getLastModified() {
307    return $this->get('modified')->value;
308  }
309
310  /**
311   * {@inheritdoc}
312   */
313  public function setTitle($title) {
314    $this->set('title', $title);
315    return $this;
316  }
317
318  /**
319   * {@inheritdoc}
320   */
321  public function setUrl($url) {
322    $this->set('url', $url);
323    return $this;
324  }
325
326  /**
327   * {@inheritdoc}
328   */
329  public function setRefreshRate($refresh) {
330    $this->set('refresh', $refresh);
331    return $this;
332  }
333
334  /**
335   * {@inheritdoc}
336   */
337  public function setLastCheckedTime($checked) {
338    $this->set('checked', $checked);
339    return $this;
340  }
341
342  /**
343   * {@inheritdoc}
344   */
345  public function setQueuedTime($queued) {
346    $this->set('queued', $queued);
347    return $this;
348  }
349
350  /**
351   * {@inheritdoc}
352   */
353  public function setWebsiteUrl($link) {
354    $this->set('link', $link);
355    return $this;
356  }
357
358  /**
359   * {@inheritdoc}
360   */
361  public function setDescription($description) {
362    $this->set('description', $description);
363    return $this;
364  }
365
366  /**
367   * {@inheritdoc}
368   */
369  public function setImage($image) {
370    $this->set('image', $image);
371    return $this;
372  }
373
374  /**
375   * {@inheritdoc}
376   */
377  public function setHash($hash) {
378    $this->set('hash', $hash);
379    return $this;
380  }
381
382  /**
383   * {@inheritdoc}
384   */
385  public function setEtag($etag) {
386    $this->set('etag', $etag);
387    return $this;
388  }
389
390  /**
391   * {@inheritdoc}
392   */
393  public function setLastModified($modified) {
394    $this->set('modified', $modified);
395    return $this;
396  }
397
398}
399