3 namespace Drupal\aggregator\Plugin\aggregator\processor;
5 use Drupal\aggregator\Entity\Item;
6 use Drupal\aggregator\FeedInterface;
7 use Drupal\aggregator\ItemStorageInterface;
8 use Drupal\aggregator\Plugin\AggregatorPluginSettingsBase;
9 use Drupal\aggregator\Plugin\ProcessorInterface;
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\Messenger\MessengerInterface;
16 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
17 use Drupal\Core\Routing\UrlGeneratorTrait;
18 use Symfony\Component\DependencyInjection\ContainerInterface;
21 * Defines a default processor implementation.
23 * Creates lightweight records from feed items.
25 * @AggregatorProcessor(
27 * title = @Translation("Default processor"),
28 * description = @Translation("Creates lightweight records from feed items.")
31 class DefaultProcessor extends AggregatorPluginSettingsBase implements ProcessorInterface, ContainerFactoryPluginInterface {
33 use ConfigFormBaseTrait;
34 use UrlGeneratorTrait;
37 * Contains the configuration object factory.
39 * @var \Drupal\Core\Config\ConfigFactoryInterface
41 protected $configFactory;
44 * The entity storage for items.
46 * @var \Drupal\aggregator\ItemStorageInterface
48 protected $itemStorage;
51 * The date formatter service.
53 * @var \Drupal\Core\Datetime\DateFormatterInterface
55 protected $dateFormatter;
60 * @var \Drupal\Core\Messenger\MessengerInterface
65 * Constructs a DefaultProcessor object.
67 * @param array $configuration
68 * A configuration array containing information about the plugin instance.
69 * @param string $plugin_id
70 * The plugin_id for the plugin instance.
71 * @param mixed $plugin_definition
72 * The plugin implementation definition.
73 * @param \Drupal\Core\Config\ConfigFactoryInterface $config
74 * The configuration factory object.
75 * @param \Drupal\aggregator\ItemStorageInterface $item_storage
76 * The entity storage for feed items.
77 * @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
78 * The date formatter service.
79 * @param \Drupal\Core\Messenger\MessengerInterface $messenger
82 public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $config, ItemStorageInterface $item_storage, DateFormatterInterface $date_formatter, MessengerInterface $messenger) {
83 $this->configFactory = $config;
84 $this->itemStorage = $item_storage;
85 $this->dateFormatter = $date_formatter;
86 $this->messenger = $messenger;
87 // @todo Refactor aggregator plugins to ConfigEntity so merging
88 // the configuration here is not needed.
89 parent::__construct($configuration + $this->getConfiguration(), $plugin_id, $plugin_definition);
95 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
100 $container->get('config.factory'),
101 $container->get('entity_type.manager')->getStorage('aggregator_item'),
102 $container->get('date.formatter'),
103 $container->get('messenger')
110 protected function getEditableConfigNames() {
111 return ['aggregator.settings'];
117 public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
118 $config = $this->config('aggregator.settings');
119 $processors = $config->get('processors');
120 $info = $this->getPluginDefinition();
121 $counts = [3, 5, 10, 15, 20, 25];
122 $items = array_map(function ($count) {
123 return $this->formatPlural($count, '1 item', '@count items');
124 }, array_combine($counts, $counts));
125 $intervals = [3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800];
126 $period = array_map([$this->dateFormatter, 'formatInterval'], array_combine($intervals, $intervals));
127 $period[AGGREGATOR_CLEAR_NEVER] = t('Never');
129 $form['processors'][$info['id']] = [];
130 // Only wrap into details if there is a basic configuration.
131 if (isset($form['basic_conf'])) {
132 $form['processors'][$info['id']] = [
133 '#type' => 'details',
134 '#title' => t('Default processor settings'),
135 '#description' => $info['description'],
136 '#open' => in_array($info['id'], $processors),
140 $form['processors'][$info['id']]['aggregator_summary_items'] = [
142 '#title' => t('Number of items shown in listing pages'),
143 '#default_value' => $config->get('source.list_max'),
145 '#options' => $items,
148 $form['processors'][$info['id']]['aggregator_clear'] = [
150 '#title' => t('Discard items older than'),
151 '#default_value' => $config->get('items.expire'),
152 '#options' => $period,
153 '#description' => t('Requires a correctly configured <a href=":cron">cron maintenance task</a>.', [':cron' => $this->url('system.status')]),
156 $lengths = [0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000];
157 $options = array_map(function ($length) {
158 return ($length == 0) ? t('Unlimited') : $this->formatPlural($length, '1 character', '@count characters');
159 }, array_combine($lengths, $lengths));
161 $form['processors'][$info['id']]['aggregator_teaser_length'] = [
163 '#title' => t('Length of trimmed description'),
164 '#default_value' => $config->get('items.teaser_length'),
165 '#options' => $options,
166 '#description' => t('The maximum number of characters used in the trimmed version of content.'),
174 public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
175 $this->configuration['items']['expire'] = $form_state->getValue('aggregator_clear');
176 $this->configuration['items']['teaser_length'] = $form_state->getValue('aggregator_teaser_length');
177 $this->configuration['source']['list_max'] = $form_state->getValue('aggregator_summary_items');
178 // @todo Refactor aggregator plugins to ConfigEntity so this is not needed.
179 $this->setConfiguration($this->configuration);
185 public function process(FeedInterface $feed) {
186 if (!is_array($feed->items)) {
189 foreach ($feed->items as $item) {
190 // @todo: The default entity view builder always returns an empty
191 // array, which is ignored in aggregator_save_item() currently. Should
192 // probably be fixed.
193 if (empty($item['title'])) {
197 // Save this item. Try to avoid duplicate entries as much as possible. If
198 // we find a duplicate entry, we resolve it and pass along its ID is such
199 // that we can update it if needed.
200 if (!empty($item['guid'])) {
201 $values = ['fid' => $feed->id(), 'guid' => $item['guid']];
203 elseif ($item['link'] && $item['link'] != $feed->link && $item['link'] != $feed->url) {
204 $values = ['fid' => $feed->id(), 'link' => $item['link']];
207 $values = ['fid' => $feed->id(), 'title' => $item['title']];
210 // Try to load an existing entry.
211 if ($entry = entity_load_multiple_by_properties('aggregator_item', $values)) {
212 $entry = reset($entry);
215 $entry = Item::create(['langcode' => $feed->language()->getId()]);
217 if ($item['timestamp']) {
218 $entry->setPostedTime($item['timestamp']);
221 // Make sure the item title and author fit in the 255 varchar column.
222 $entry->setTitle(Unicode::truncate($item['title'], 255, TRUE, TRUE));
223 $entry->setAuthor(Unicode::truncate($item['author'], 255, TRUE, TRUE));
225 $entry->setFeedId($feed->id());
226 $entry->setLink($item['link']);
227 $entry->setGuid($item['guid']);
230 if (!empty($item['description'])) {
231 $description = $item['description'];
233 $entry->setDescription($description);
242 public function delete(FeedInterface $feed) {
243 if ($items = $this->itemStorage->loadByFeed($feed->id())) {
244 $this->itemStorage->delete($items);
246 // @todo This should be moved out to caller with a different message maybe.
247 $this->messenger->addStatus(t('The news items from %site have been deleted.', ['%site' => $feed->label()]));
251 * Implements \Drupal\aggregator\Plugin\ProcessorInterface::postProcess().
253 * Expires items from a feed depending on expiration settings.
255 public function postProcess(FeedInterface $feed) {
256 $aggregator_clear = $this->configuration['items']['expire'];
258 if ($aggregator_clear != AGGREGATOR_CLEAR_NEVER) {
259 // Delete all items that are older than flush item timer.
260 $age = REQUEST_TIME - $aggregator_clear;
261 $result = $this->itemStorage->getQuery()
262 ->condition('fid', $feed->id())
263 ->condition('timestamp', $age, '<')
266 $entities = $this->itemStorage->loadMultiple($result);
267 $this->itemStorage->delete($entities);
275 public function getConfiguration() {
276 return $this->configFactory->get('aggregator.settings')->get();
282 public function setConfiguration(array $configuration) {
283 $config = $this->config('aggregator.settings');
284 foreach ($configuration as $key => $value) {
285 $config->set($key, $value);