525ef34f13ae370ea43100b9ff1b664705495f3d
[yaffs-website] / web / core / modules / aggregator / src / Plugin / aggregator / processor / DefaultProcessor.php
1 <?php
2
3 namespace Drupal\aggregator\Plugin\aggregator\processor;
4
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  * Defines a default processor implementation.
21  *
22  * Creates lightweight records from feed items.
23  *
24  * @AggregatorProcessor(
25  *   id = "aggregator",
26  *   title = @Translation("Default processor"),
27  *   description = @Translation("Creates lightweight records from feed items.")
28  * )
29  */
30 class DefaultProcessor extends AggregatorPluginSettingsBase implements ProcessorInterface, ContainerFactoryPluginInterface {
31   use ConfigFormBaseTrait;
32   use UrlGeneratorTrait;
33
34   /**
35    * Contains the configuration object factory.
36    *
37    * @var \Drupal\Core\Config\ConfigFactoryInterface
38    */
39   protected $configFactory;
40
41   /**
42    * The entity storage for items.
43    *
44    * @var \Drupal\aggregator\ItemStorageInterface
45    */
46   protected $itemStorage;
47
48   /**
49    * The date formatter service.
50    *
51    * @var \Drupal\Core\Datetime\DateFormatterInterface
52    */
53   protected $dateFormatter;
54
55   /**
56    * Constructs a DefaultProcessor object.
57    *
58    * @param array $configuration
59    *   A configuration array containing information about the plugin instance.
60    * @param string $plugin_id
61    *   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    * @param \Drupal\aggregator\ItemStorageInterface $item_storage
67    *   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     // @todo Refactor aggregator plugins to ConfigEntity so merging
76     //   the configuration here is not needed.
77     parent::__construct($configuration + $this->getConfiguration(), $plugin_id, $plugin_definition);
78   }
79
80   /**
81    * {@inheritdoc}
82    */
83   public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
84     return new static(
85       $configuration,
86       $plugin_id,
87       $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    * {@inheritdoc}
96    */
97   protected function getEditableConfigNames() {
98     return ['aggregator.settings'];
99   }
100
101   /**
102    * {@inheritdoc}
103    */
104   public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
105     $config = $this->config('aggregator.settings');
106     $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     $intervals = [3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800];
113     $period = array_map([$this->dateFormatter, 'formatInterval'], array_combine($intervals, $intervals));
114     $period[AGGREGATOR_CLEAR_NEVER] = t('Never');
115
116     $form['processors'][$info['id']] = [];
117     // Only wrap into details if there is a basic configuration.
118     if (isset($form['basic_conf'])) {
119       $form['processors'][$info['id']] = [
120         '#type' => 'details',
121         '#title' => t('Default processor settings'),
122         '#description' => $info['description'],
123         '#open' => in_array($info['id'], $processors),
124       ];
125     }
126
127     $form['processors'][$info['id']]['aggregator_summary_items'] = [
128       '#type' => 'select',
129       '#title' => t('Number of items shown in listing pages'),
130       '#default_value' => $config->get('source.list_max'),
131       '#empty_value' => 0,
132       '#options' => $items,
133     ];
134
135     $form['processors'][$info['id']]['aggregator_clear'] = [
136       '#type' => 'select',
137       '#title' => t('Discard items older than'),
138       '#default_value' => $config->get('items.expire'),
139       '#options' => $period,
140       '#description' => t('Requires a correctly configured <a href=":cron">cron maintenance task</a>.', [':cron' => $this->url('system.status')]),
141     ];
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     }, array_combine($lengths, $lengths));
147
148     $form['processors'][$info['id']]['aggregator_teaser_length'] = [
149       '#type' => 'select',
150       '#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     return $form;
156   }
157
158   /**
159    * {@inheritdoc}
160    */
161   public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
162     $this->configuration['items']['expire'] = $form_state->getValue('aggregator_clear');
163     $this->configuration['items']['teaser_length'] = $form_state->getValue('aggregator_teaser_length');
164     $this->configuration['source']['list_max'] = $form_state->getValue('aggregator_summary_items');
165     // @todo Refactor aggregator plugins to ConfigEntity so this is not needed.
166     $this->setConfiguration($this->configuration);
167   }
168
169   /**
170    * {@inheritdoc}
171    */
172   public function process(FeedInterface $feed) {
173     if (!is_array($feed->items)) {
174       return;
175     }
176     foreach ($feed->items as $item) {
177       // @todo: The default entity view builder always returns an empty
178       //   array, which is ignored in aggregator_save_item() currently. Should
179       //   probably be fixed.
180       if (empty($item['title'])) {
181         continue;
182       }
183
184       // Save this item. Try to avoid duplicate entries as much as possible. If
185       // we find a duplicate entry, we resolve it and pass along its ID is such
186       // that we can update it if needed.
187       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       // Try to load an existing entry.
198       if ($entry = entity_load_multiple_by_properties('aggregator_item', $values)) {
199         $entry = reset($entry);
200       }
201       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       $entry->setTitle(Unicode::truncate($item['title'], 255, TRUE, TRUE));
210       $entry->setAuthor(Unicode::truncate($item['author'], 255, TRUE, TRUE));
211
212       $entry->setFeedId($feed->id());
213       $entry->setLink($item['link']);
214       $entry->setGuid($item['guid']);
215
216       $description = '';
217       if (!empty($item['description'])) {
218         $description = $item['description'];
219       }
220       $entry->setDescription($description);
221
222       $entry->save();
223     }
224   }
225
226   /**
227    * {@inheritdoc}
228    */
229   public function delete(FeedInterface $feed) {
230     if ($items = $this->itemStorage->loadByFeed($feed->id())) {
231       $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    * Implements \Drupal\aggregator\Plugin\ProcessorInterface::postProcess().
239    *
240    * Expires items from a feed depending on expiration settings.
241    */
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         ->condition('fid', $feed->id())
250         ->condition('timestamp', $age, '<')
251         ->execute();
252       if ($result) {
253         $entities = $this->itemStorage->loadMultiple($result);
254         $this->itemStorage->delete($entities);
255       }
256     }
257   }
258
259   /**
260    * {@inheritdoc}
261    */
262   public function getConfiguration() {
263     return $this->configFactory->get('aggregator.settings')->get();
264   }
265
266   /**
267    * {@inheritdoc}
268    */
269   public function setConfiguration(array $configuration) {
270     $config = $this->config('aggregator.settings');
271     foreach ($configuration as $key => $value) {
272       $config->set($key, $value);
273     }
274     $config->save();
275   }
276
277 }