aggregator.module 10.4 KB
Newer Older
1
<?php
Dries's avatar
   
Dries committed
2

Dries's avatar
   
Dries committed
3
4
/**
 * @file
5
 * Used to aggregate syndicated content (RSS, RDF, and Atom).
Dries's avatar
   
Dries committed
6
7
 */

8
use Drupal\Core\Url;
9
use Drupal\aggregator\Entity\Feed;
10
use Drupal\Core\Routing\RouteMatchInterface;
11

Dries's avatar
   
Dries committed
12
/**
13
 * Implements hook_help().
Dries's avatar
   
Dries committed
14
 */
15
function aggregator_help($route_name, RouteMatchInterface $route_match) {
16
17
  switch ($route_name) {
    case 'help.page.aggregator':
18
      $path_validator = \Drupal::pathValidator();
19
20
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
21
      $output .= '<p>' . t('The Aggregator module is an on-site syndicator and news reader that gathers and displays fresh content from RSS-, RDF-, and Atom-based feeds made available across the web. Thousands of sites (particularly news sites and blogs) publish their latest headlines in feeds, using a number of standardized XML-based formats. For more information, see the <a href=":aggregator-module">online documentation for the Aggregator module</a>.', [':aggregator-module' => 'https://www.drupal.org/documentation/modules/aggregator']) . '</p>';
22
23
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
24
      // Check if the aggregator sources View is enabled.
25
26
      if ($url = $path_validator->getUrlIfValid('aggregator/sources')) {
        $output .= '<dt>' . t('Viewing feeds') . '</dt>';
27
        $output .= '<dd>' . t('Users view feed content in the <a href=":aggregator">main aggregator display</a>, or by <a href=":aggregator-sources">their source</a> (usually via an RSS feed reader). The most recent content in a feed can be displayed as a block through the <a href=":admin-block">Blocks administration page</a>.', [':aggregator' => Url::fromRoute('aggregator.page_last')->toString(), ':aggregator-sources' => $url->toString(), ':admin-block' => (\Drupal::moduleHandler()->moduleExists('block')) ? Url::fromRoute('block.admin_display')->toString() : '#']) . '</dd>';
28
      }
29
      $output .= '<dt>' . t('Adding, editing, and deleting feeds') . '</dt>';
30
      $output .= '<dd>' . t('Administrators can add, edit, and delete feeds, and choose how often to check each feed for newly updated items on the <a href=":feededit">Aggregator administration page</a>.', [':feededit' => Url::fromRoute('aggregator.admin_overview')->toString()]) . '</dd>';
31
      $output .= '<dt>' . t('Configuring the display of feed items') . '</dt>';
32
      $output .= '<dd>' . t('Administrators can choose how many items are displayed in the listing pages, which HTML tags are allowed in the content of feed items, and whether they should be trimmed to a maximum number of characters on the <a href=":settings">Aggregator settings page</a>.', [':settings' => Url::fromRoute('aggregator.admin_settings')->toString()]) . '</dd>';
33
      $output .= '<dt>' . t('Discarding old feed items') . '</dt>';
34
      $output .= '<dd>' . t('Administrators can choose whether to discard feed items that are older than a specified period of time on the <a href=":settings">Aggregator settings page</a>. This requires a correctly configured cron maintenance task (see below).', [':settings' => Url::fromRoute('aggregator.admin_settings')->toString()]) . '<dd>';
35

36
      $output .= '<dt>' . t('<abbr title="Outline Processor Markup Language">OPML</abbr> integration') . '</dt>';
37
      // Check if the aggregator opml View is enabled.
38
      if ($url = $path_validator->getUrlIfValid('aggregator/opml')) {
39
        $output .= '<dd>' . t('A <a href=":aggregator-opml">machine-readable OPML file</a> of all feeds is available. OPML is an XML-based file format used to share outline-structured information such as a list of RSS feeds. Feeds can also be <a href=":import-opml">imported via an OPML file</a>.', [':aggregator-opml' => $url->toString(), ':import-opml' => Url::fromRoute('aggregator.opml_add')->toString()]) . '</dd>';
40
      }
41
      $output .= '<dt>' . t('Configuring cron') . '</dt>';
42
      $output .= '<dd>' . t('A working <a href=":cron">cron maintenance task</a> is required to update feeds automatically.', [':cron' => Url::fromRoute('system.cron_settings')->toString()]) . '</dd>';
43
      $output .= '</dl>';
44
      return $output;
45
46

    case 'aggregator.admin_overview':
47
      // Don't use placeholders for possibility to change URLs for translators.
48
      $output = '<p>' . t('Many sites publish their headlines and posts in feeds, using a number of standardized XML-based formats. The aggregator supports <a href="http://en.wikipedia.org/wiki/Rss">RSS</a>, <a href="http://en.wikipedia.org/wiki/Resource_Description_Framework">RDF</a>, and <a href="http://en.wikipedia.org/wiki/Atom_%28standard%29">Atom</a>.') . '</p>';
49
      // cspell:ignore addfeed
50
      $output .= '<p>' . t('Current feeds are listed below, and <a href=":addfeed">new feeds may be added</a>. For each feed, the <em>@block_name</em> block may be enabled at the <a href=":block">block layout page</a>.', [':addfeed' => Url::fromRoute('aggregator.feed_add')->toString(), '@block_name' => t('Aggregator feed'), ':block' => (\Drupal::moduleHandler()->moduleExists('block')) ? Url::fromRoute('block.admin_display')->toString() : '#']) . '</p>';
51
      return $output;
52
53

    case 'aggregator.feed_add':
54
      return '<p>' . t('Add a feed in RSS, RDF or Atom format. A feed may only have one entry.') . '</p>';
55
56

    case 'aggregator.opml_add':
57
      return '<p>' . t('<abbr title="Outline Processor Markup Language">OPML</abbr> is an XML format for exchanging feeds between aggregators. A single OPML document may contain many feeds. Aggregator uses this file to import all feeds at once. Upload a file from your computer or enter a URL where the OPML file can be downloaded.') . '</p>';
Dries's avatar
   
Dries committed
58
  }
59
60
}

61
/**
62
 * Implements hook_theme().
63
64
 */
function aggregator_theme() {
65
66
  return [
    'aggregator_feed' => [
67
      'render element' => 'elements',
68
      'file' => 'aggregator.theme.inc',
69
70
    ],
    'aggregator_item' => [
71
      'render element' => 'elements',
72
      'file' => 'aggregator.theme.inc',
73
74
    ],
  ];
75
76
77
78
}

/**
 * Implements hook_entity_extra_field_info().
79
80
81
82
83
84
85
86
 *
 * By default this function creates pseudo-fields that mask the description and
 * image base fields. These pseudo-fields are omitted if:
 * - a module makes the field's display configurable via the field UI by means
 *   of BaseFieldDefinition::setDisplayConfigurable()
 * - AND the additional entity type property
 *   'enable_base_field_custom_preprocess_skipping' has been set using
 *   hook_entity_type_build().
87
88
 */
function aggregator_entity_extra_field_info() {
89
  $extra = [];
90
91
  $entity_type_manager = \Drupal::entityTypeManager();
  $entity_field_manager = \Drupal::service('entity_field.manager');
92

93
94
95
  $extra['aggregator_feed']['aggregator_feed'] = [
    'display' => [
      'items' => [
96
97
98
        'label' => t('Items'),
        'description' => t('Items associated with this feed'),
        'weight' => 0,
99
100
      ],
      'more_link' => [
101
102
103
        'label' => t('More link'),
        'description' => t('A more link to the feed detail page'),
        'weight' => 5,
104
105
      ],
      'feed_icon' => [
106
        'label' => t('Feed icon'),
107
        'description' => t('An icon that links to the feed URL'),
108
        'weight' => 6,
109
110
111
      ],
    ],
  ];
112

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
  // Create Feed image and description pseudo-fields. Skip this if the field
  // display is configurable and skipping has been enabled.
  // @todo https://www.drupal.org/project/drupal/issues/3015623
  //   Eventually delete this code and matching lines in FeedViewBuilder. Using
  //   field formatters is more flexible and consistent.
  $skip_custom_preprocessing = $entity_type_manager->getDefinition('aggregator_feed')->get('enable_base_field_custom_preprocess_skipping');
  $base_field_definitions = $entity_field_manager->getBaseFieldDefinitions('aggregator_feed');

  if (!$skip_custom_preprocessing || !$base_field_definitions['image']->isDisplayConfigurable('view')) {
    $extra['aggregator_feed']['aggregator_feed']['display']['image'] = [
      'label' => t('Image'),
      'description' => t('The feed image'),
      'weight' => 2,
    ];
  }

  if (!$skip_custom_preprocessing || !$base_field_definitions['description']->isDisplayConfigurable('view')) {
    $extra['aggregator_feed']['aggregator_feed']['display']['description'] = [
      'label' => t('Description'),
      'description' => t('The description of this feed'),
      'weight' => 3,
    ];
  }

  // Create Item description pseudo-field. Skip this if the field display is
  // configurable and skipping has been enabled.
  // @todo https://www.drupal.org/project/drupal/issues/3015623
  //   Eventually delete this code and matching lines in ItemViewBuilder. Using
  //   field formatters is more flexible and consistent.
  $skip_custom_preprocessing = $entity_type_manager->getDefinition('aggregator_item')->get('enable_base_field_custom_preprocess_skipping');
  $base_field_definitions = $entity_field_manager->getBaseFieldDefinitions('aggregator_item');

  if (!$skip_custom_preprocessing || !$base_field_definitions['description']->isDisplayConfigurable('view')) {
    $extra['aggregator_item']['aggregator_item']['display']['description'] = [
      'label' => t('Description'),
      'description' => t('The description of this feed item'),
      'weight' => 2,
    ];
  }
152
153

  return $extra;
154
}
155

Dries's avatar
   
Dries committed
156
/**
157
 * Implements hook_cron().
Dries's avatar
   
Dries committed
158
 *
159
 * Queues news feeds for updates once their refresh interval has elapsed.
Dries's avatar
   
Dries committed
160
 */
Dries's avatar
   
Dries committed
161
function aggregator_cron() {
162
  $queue = \Drupal::queue('aggregator_feeds');
163

164
  $ids = \Drupal::entityTypeManager()->getStorage('aggregator_feed')->getFeedIdsToRefresh();
165
  foreach (Feed::loadMultiple($ids) as $feed) {
166
167
    if ($queue->createItem($feed)) {
      // Add timestamp to avoid queueing item more than once.
168
      $feed->setQueuedTime(REQUEST_TIME);
169
      $feed->save();
170
    }
Dries's avatar
   
Dries committed
171
  }
172

173
  // Delete queued timestamp after 6 hours assuming the update has failed.
174
  $ids = \Drupal::entityQuery('aggregator_feed')
175
    ->accessCheck(FALSE)
176
177
    ->condition('queued', REQUEST_TIME - (3600 * 6), '<')
    ->execute();
178

179
180
  if ($ids) {
    $feeds = Feed::loadMultiple($ids);
181
182
183
184
185
    foreach ($feeds as $feed) {
      $feed->setQueuedTime(0);
      $feed->save();
    }
  }
Dries's avatar
   
Dries committed
186
187
}

188
/**
189
 * Gets the list of allowed tags.
190
 *
191
192
 * @return array
 *   The list of allowed tags.
193
 *
194
 * @internal
195
 */
196
197
function _aggregator_allowed_tags() {
  return preg_split('/\s+|<|>/', \Drupal::config('aggregator.settings')->get('items.allowed_html'), -1, PREG_SPLIT_NO_EMPTY);
198
}
199

200
/**
201
 * Implements hook_preprocess_HOOK() for block templates.
202
203
 */
function aggregator_preprocess_block(&$variables) {
204
  if ($variables['configuration']['provider'] == 'aggregator') {
205
    $variables['attributes']['role'] = 'complementary';
206
207
  }
}