DefaultProcessor.php 9.43 KB
Newer Older
1 2 3 4
<?php

namespace Drupal\aggregator\Plugin\aggregator\processor;

5
use Drupal\aggregator\Entity\Item;
6
use Drupal\aggregator\ItemStorageInterface;
7
use Drupal\aggregator\Plugin\AggregatorPluginSettingsBase;
8
use Drupal\aggregator\Plugin\ProcessorInterface;
9
use Drupal\aggregator\FeedInterface;
10
use Drupal\Component\Utility\Unicode;
11
use Drupal\Core\Config\ConfigFactoryInterface;
12
use Drupal\Core\Datetime\DateFormatterInterface;
13
use Drupal\Core\Form\ConfigFormBaseTrait;
14
use Drupal\Core\Form\FormStateInterface;
15
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
16
use Drupal\Core\Routing\UrlGeneratorTrait;
17
use Symfony\Component\DependencyInjection\ContainerInterface;
18 19 20 21 22 23

/**
 * Defines a default processor implementation.
 *
 * Creates lightweight records from feed items.
 *
24
 * @AggregatorProcessor(
25 26 27 28 29
 *   id = "aggregator",
 *   title = @Translation("Default processor"),
 *   description = @Translation("Creates lightweight records from feed items.")
 * )
 */
30
class DefaultProcessor extends AggregatorPluginSettingsBase implements ProcessorInterface, ContainerFactoryPluginInterface {
31
  use ConfigFormBaseTrait;
32 33
  use UrlGeneratorTrait;

34 35 36
  /**
   * Contains the configuration object factory.
   *
37
   * @var \Drupal\Core\Config\ConfigFactoryInterface
38 39 40
   */
  protected $configFactory;

41
  /**
42
   * The entity storage for items.
43
   *
44
   * @var \Drupal\aggregator\ItemStorageInterface
45 46 47
   */
  protected $itemStorage;

48 49 50
  /**
   * The date formatter service.
   *
51
   * @var \Drupal\Core\Datetime\DateFormatterInterface
52 53 54
   */
  protected $dateFormatter;

55 56 57 58 59 60 61
  /**
   * Constructs a DefaultProcessor object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
62
   * @param mixed $plugin_definition
63
   *   The plugin implementation definition.
64
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config
65
   *   The configuration factory object.
66 67
   * @param \Drupal\aggregator\ItemStorageInterface $item_storage
   *   The entity storage for feed items.
68
   * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
69
   *   The date formatter service.
70
   */
71
  public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $config, ItemStorageInterface $item_storage, DateFormatterInterface $date_formatter) {
72
    $this->configFactory = $config;
73
    $this->itemStorage = $item_storage;
74
    $this->dateFormatter = $date_formatter;
75 76 77 78 79 80 81 82
    // @todo Refactor aggregator plugins to ConfigEntity so merging
    //   the configuration here is not needed.
    parent::__construct($configuration + $this->getConfiguration(), $plugin_id, $plugin_definition);
  }

  /**
   * {@inheritdoc}
   */
83
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
84 85 86 87
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
88
      $container->get('config.factory'),
89
      $container->get('entity_type.manager')->getStorage('aggregator_item'),
90
      $container->get('date.formatter')
91 92
    );
  }
93

94 95 96 97 98 99 100
  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames() {
    return ['aggregator.settings'];
  }

101
  /**
102
   * {@inheritdoc}
103
   */
104
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
105 106
    $config = $this->config('aggregator.settings');
    $processors = $config->get('processors');
107
    $info = $this->getPluginDefinition();
108
    $counts = [3, 5, 10, 15, 20, 25];
109
    $items = array_map(function ($count) {
110
      return $this->formatPlural($count, '1 item', '@count items');
111
    }, array_combine($counts, $counts));
112 113
    $intervals = [3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800];
    $period = array_map([$this->dateFormatter, 'formatInterval'], array_combine($intervals, $intervals));
114 115
    $period[AGGREGATOR_CLEAR_NEVER] = t('Never');

116
    $form['processors'][$info['id']] = [];
117 118
    // Only wrap into details if there is a basic configuration.
    if (isset($form['basic_conf'])) {
119
      $form['processors'][$info['id']] = [
120 121 122
        '#type' => 'details',
        '#title' => t('Default processor settings'),
        '#description' => $info['description'],
123
        '#open' => in_array($info['id'], $processors),
124
      ];
125 126
    }

127
    $form['processors'][$info['id']]['aggregator_summary_items'] = [
128 129
      '#type' => 'select',
      '#title' => t('Number of items shown in listing pages'),
130
      '#default_value' => $config->get('source.list_max'),
131 132
      '#empty_value' => 0,
      '#options' => $items,
133
    ];
134

135
    $form['processors'][$info['id']]['aggregator_clear'] = [
136 137
      '#type' => 'select',
      '#title' => t('Discard items older than'),
138
      '#default_value' => $config->get('items.expire'),
139
      '#options' => $period,
140 141
      '#description' => t('Requires a correctly configured <a href=":cron">cron maintenance task</a>.', [':cron' => $this->url('system.status')]),
    ];
142

143
    $lengths = [0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000];
144
    $options = array_map(function ($length) {
145
      return ($length == 0) ? t('Unlimited') : $this->formatPlural($length, '1 character', '@count characters');
146 147
    }, array_combine($lengths, $lengths));

148
    $form['processors'][$info['id']]['aggregator_teaser_length'] = [
149 150
      '#type' => 'select',
      '#title' => t('Length of trimmed description'),
151
      '#default_value' => $config->get('items.teaser_length'),
152
      '#options' => $options,
153
      '#description' => t('The maximum number of characters used in the trimmed version of content.'),
154
    ];
155 156 157 158
    return $form;
  }

  /**
159
   * {@inheritdoc}
160
   */
161
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
162 163 164
    $this->configuration['items']['expire'] = $form_state->getValue('aggregator_clear');
    $this->configuration['items']['teaser_length'] = $form_state->getValue('aggregator_teaser_length');
    $this->configuration['source']['list_max'] = $form_state->getValue('aggregator_summary_items');
165 166
    // @todo Refactor aggregator plugins to ConfigEntity so this is not needed.
    $this->setConfiguration($this->configuration);
167 168 169
  }

  /**
170
   * {@inheritdoc}
171
   */
172
  public function process(FeedInterface $feed) {
173 174 175 176
    if (!is_array($feed->items)) {
      return;
    }
    foreach ($feed->items as $item) {
177
      // @todo: The default entity view builder always returns an empty
178 179 180 181 182 183 184 185 186 187
      //   array, which is ignored in aggregator_save_item() currently. Should
      //   probably be fixed.
      if (empty($item['title'])) {
        continue;
      }

      // Save this item. Try to avoid duplicate entries as much as possible. If
      // we find a duplicate entry, we resolve it and pass along its ID is such
      // that we can update it if needed.
      if (!empty($item['guid'])) {
188
        $values = ['fid' => $feed->id(), 'guid' => $item['guid']];
189 190
      }
      elseif ($item['link'] && $item['link'] != $feed->link && $item['link'] != $feed->url) {
191
        $values = ['fid' => $feed->id(), 'link' => $item['link']];
192 193
      }
      else {
194
        $values = ['fid' => $feed->id(), 'title' => $item['title']];
195 196 197 198 199 200 201
      }

      // Try to load an existing entry.
      if ($entry = entity_load_multiple_by_properties('aggregator_item', $values)) {
        $entry = reset($entry);
      }
      else {
202
        $entry = Item::create(['langcode' => $feed->language()->getId()]);
203 204
      }
      if ($item['timestamp']) {
205
        $entry->setPostedTime($item['timestamp']);
206 207 208
      }

      // Make sure the item title and author fit in the 255 varchar column.
209 210
      $entry->setTitle(Unicode::truncate($item['title'], 255, TRUE, TRUE));
      $entry->setAuthor(Unicode::truncate($item['author'], 255, TRUE, TRUE));
211

212 213 214
      $entry->setFeedId($feed->id());
      $entry->setLink($item['link']);
      $entry->setGuid($item['guid']);
215 216 217 218 219 220 221

      $description = '';
      if (!empty($item['description'])) {
        $description = $item['description'];
      }
      $entry->setDescription($description);

222 223 224 225 226
      $entry->save();
    }
  }

  /**
227
   * {@inheritdoc}
228
   */
229
  public function delete(FeedInterface $feed) {
230 231
    if ($items = $this->itemStorage->loadByFeed($feed->id())) {
      $this->itemStorage->delete($items);
232 233
    }
    // @todo This should be moved out to caller with a different message maybe.
234
    drupal_set_message(t('The news items from %site have been deleted.', ['%site' => $feed->label()]));
235 236 237 238 239 240 241
  }

  /**
   * Implements \Drupal\aggregator\Plugin\ProcessorInterface::postProcess().
   *
   * Expires items from a feed depending on expiration settings.
   */
242
  public function postProcess(FeedInterface $feed) {
243
    $aggregator_clear = $this->configuration['items']['expire'];
244 245

    if ($aggregator_clear != AGGREGATOR_CLEAR_NEVER) {
246
      // Delete all items that are older than flush item timer.
247
      $age = REQUEST_TIME - $aggregator_clear;
248
      $result = $this->itemStorage->getQuery()
249 250 251 252 253 254
        ->condition('fid', $feed->id())
        ->condition('timestamp', $age, '<')
        ->execute();
      if ($result) {
        $entities = $this->itemStorage->loadMultiple($result);
        $this->itemStorage->delete($entities);
255 256 257 258
      }
    }
  }

259 260 261 262 263 264 265 266 267 268 269
  /**
   * {@inheritdoc}
   */
  public function getConfiguration() {
    return $this->configFactory->get('aggregator.settings')->get();
  }

  /**
   * {@inheritdoc}
   */
  public function setConfiguration(array $configuration) {
270
    $config = $this->config('aggregator.settings');
271 272 273 274 275 276
    foreach ($configuration as $key => $value) {
      $config->set($key, $value);
    }
    $config->save();
  }

277
}