diff --git a/core/modules/aggregator/aggregator.admin.inc b/core/modules/aggregator/aggregator.admin.inc
index 3eee1f0..1d902b3 100644
--- a/core/modules/aggregator/aggregator.admin.inc
+++ b/core/modules/aggregator/aggregator.admin.inc
@@ -7,8 +7,6 @@
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Drupal\aggregator\Plugin\Core\Entity\Feed;
-use Guzzle\Http\Exception\RequestException;
-use Guzzle\Http\Exception\BadResponseException;
/**
* Page callback: Displays the aggregator administration page.
@@ -114,176 +112,6 @@ function aggregator_feed_add() {
}
/**
- * Form constructor for importing feeds from OPML.
- *
- * @ingroup forms
- * @see aggregator_menu()
- * @see aggregator_form_opml_validate()
- * @see aggregator_form_opml_submit()
- */
-function aggregator_form_opml($form, &$form_state) {
- $period = drupal_map_assoc(array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200), 'format_interval');
-
- $form['upload'] = array(
- '#type' => 'file',
- '#title' => t('OPML File'),
- '#description' => t('Upload an OPML file containing a list of feeds to be imported.'),
- );
- $form['remote'] = array(
- '#type' => 'url',
- '#title' => t('OPML Remote URL'),
- '#maxlength' => 1024,
- '#description' => t('Enter the URL of an OPML file. This file will be downloaded and processed only once on submission of the form.'),
- );
- $form['refresh'] = array(
- '#type' => 'select',
- '#title' => t('Update interval'),
- '#default_value' => 3600,
- '#options' => $period,
- '#description' => t('The length of time between feed updates. Requires a correctly configured cron maintenance task.', array('@cron' => url('admin/reports/status'))),
- );
- $form['block'] = array('#type' => 'select',
- '#title' => t('News items in block'),
- '#default_value' => 5,
- '#options' => drupal_map_assoc(array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)),
- '#description' => t("Drupal can make a block with the most recent news items of a feed. You can configure blocks to be displayed in the sidebar of your page. This setting lets you configure the number of news items to show in a feed's block. If you choose '0' these feeds' blocks will be disabled.", array('@block-admin' => url('admin/structure/block'))),
- );
-
- // Handling of categories.
- $options = array_map('check_plain', db_query("SELECT cid, title FROM {aggregator_category} ORDER BY title")->fetchAllKeyed());
- if ($options) {
- $form['category'] = array(
- '#type' => 'checkboxes',
- '#title' => t('Categorize news items'),
- '#options' => $options,
- '#description' => t('New feed items are automatically filed in the checked categories.'),
- );
- }
- $form['actions'] = array('#type' => 'actions');
- $form['actions']['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Import')
- );
-
- return $form;
-}
-
-/**
- * Form validation handler for aggregator_form_opml().
- *
- * @see aggregator_form_opml_submit()
- */
-function aggregator_form_opml_validate($form, &$form_state) {
- // If both fields are empty or filled, cancel.
- if (empty($form_state['values']['remote']) == empty($_FILES['files']['name']['upload'])) {
- form_set_error('remote', t('You must either upload a file or enter a URL.'));
- }
-}
-
-/**
- * Form submission handler for aggregator_form_opml().
- *
- * @see aggregator_form_opml_validate()
- */
-function aggregator_form_opml_submit($form, &$form_state) {
- $data = '';
- $validators = array('file_validate_extensions' => array('opml xml'));
- if ($file = file_save_upload('upload', $validators, FALSE, 0)) {
- $data = file_get_contents($file->uri);
- }
- else {
- try {
- $response = Drupal::httpClient()
- ->get($form_state['values']['remote'])
- ->send();
- $data = $response->getBody(TRUE);
- }
- catch (BadResponseException $e) {
- $response = $e->getResponse();
- watchdog('aggregator', 'Failed to download OPML file due to "%error".', array('%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase()), WATCHDOG_WARNING);
- drupal_set_message(t('Failed to download OPML file due to "%error".', array('%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase())));
- return;
- }
- catch (RequestException $e) {
- watchdog('aggregator', 'Failed to download OPML file due to "%error".', array('%error' => $e->getMessage()), WATCHDOG_WARNING);
- drupal_set_message(t('Failed to download OPML file due to "%error".', array('%error' => $e->getMessage())));
- return;
- }
- }
-
- $feeds = _aggregator_parse_opml($data);
- if (empty($feeds)) {
- drupal_set_message(t('No new feed has been added.'));
- return;
- }
-
- foreach ($feeds as $feed) {
- // Ensure URL is valid.
- if (!valid_url($feed['url'], TRUE)) {
- drupal_set_message(t('The URL %url is invalid.', array('%url' => $feed['url'])), 'warning');
- continue;
- }
-
- // Check for duplicate titles or URLs.
- $result = db_query("SELECT title, url FROM {aggregator_feed} WHERE title = :title OR url = :url", array(':title' => $feed['title'], ':url' => $feed['url']));
- foreach ($result as $old) {
- if (strcasecmp($old->title, $feed['title']) == 0) {
- drupal_set_message(t('A feed named %title already exists.', array('%title' => $old->title)), 'warning');
- continue 2;
- }
- if (strcasecmp($old->url, $feed['url']) == 0) {
- drupal_set_message(t('A feed with the URL %url already exists.', array('%url' => $old->url)), 'warning');
- continue 2;
- }
- }
-
- $new_feed = entity_create('aggregator_feed', array(
- 'title' => $feed['title'],
- 'url' => $feed['url'],
- 'refresh' => $form_state['values']['refresh'],
- 'block' => $form_state['values']['block'],
- ));
- $new_feed->categories = $form_state['values']['category'];
- $new_feed->save();
- }
-
- $form_state['redirect'] = 'admin/config/services/aggregator';
-}
-
-/**
- * Parses an OPML file.
- *
- * Feeds are recognized as elements with the attributes "text" and
- * "xmlurl" set.
- *
- * @param $opml
- * The complete contents of an OPML document.
- *
- * @return
- * An array of feeds, each an associative array with a "title" and a "url"
- * element, or NULL if the OPML document failed to be parsed. An empty array
- * will be returned if the document is valid but contains no feeds, as some
- * OPML documents do.
- */
-function _aggregator_parse_opml($opml) {
- $feeds = array();
- $xml_parser = drupal_xml_parser_create($opml);
- if (xml_parse_into_struct($xml_parser, $opml, $values)) {
- foreach ($values as $entry) {
- if ($entry['tag'] == 'OUTLINE' && isset($entry['attributes'])) {
- $item = $entry['attributes'];
- if (!empty($item['XMLURL']) && !empty($item['TEXT'])) {
- $feeds[] = array('title' => $item['TEXT'], 'url' => $item['XMLURL']);
- }
- }
- }
- }
- xml_parser_free($xml_parser);
-
- return $feeds;
-}
-
-/**
* Page callback: Refreshes a feed, then redirects to the overview page.
*
* @param \Drupal\aggregator\Plugin\Core\Entity\Feed $feed
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index a2ba059..a27ae52 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -114,11 +114,8 @@ function aggregator_menu() {
);
$items['admin/config/services/aggregator/add/opml'] = array(
'title' => 'Import OPML',
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('aggregator_form_opml'),
- 'access arguments' => array('administer news feeds'),
'type' => MENU_LOCAL_ACTION,
- 'file' => 'aggregator.admin.inc',
+ 'route_name' => 'aggregator_opml_add',
);
$items['admin/config/services/aggregator/remove/%aggregator_feed'] = array(
'title' => 'Remove items',
diff --git a/core/modules/aggregator/aggregator.routing.yml b/core/modules/aggregator/aggregator.routing.yml
index 9b12cd1..dc5c3f6 100644
--- a/core/modules/aggregator/aggregator.routing.yml
+++ b/core/modules/aggregator/aggregator.routing.yml
@@ -18,3 +18,10 @@ aggregator_feed_delete:
_form: '\Drupal\aggregator\Form\FeedDelete'
requirements:
_permission: 'administer news feeds'
+
+aggregator_opml_add:
+ pattern: '/admin/config/services/aggregator/add/opml'
+ defaults:
+ _form: '\Drupal\aggregator\Form\OpmlFeedAdd'
+ requirements:
+ _permission: 'administer news feeds'
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php b/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php
new file mode 100644
index 0000000..1b49f96
--- /dev/null
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Form/OpmlFeedAdd.php
@@ -0,0 +1,267 @@
+database = $database;
+ $this->queryFactory = $query_factory;
+ $this->entityManager = $entity_manager;
+ $this->httpClient = $http_client;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('database'),
+ $container->get('entity.query'),
+ $container->get('plugin.manager.entity'),
+ $container->get('http_default_client')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFormID() {
+ return 'aggregator_opml_add';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(array $form, array &$form_state) {
+ $period = drupal_map_assoc(array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200,
+ 64800, 86400, 172800, 259200, 604800, 1209600, 2419200), 'format_interval');
+
+ $form['upload'] = array(
+ '#type' => 'file',
+ '#title' => t('OPML File'),
+ '#description' => t('Upload an OPML file containing a list of feeds to be imported.'),
+ );
+ $form['remote'] = array(
+ '#type' => 'url',
+ '#title' => t('OPML Remote URL'),
+ '#maxlength' => 1024,
+ '#description' => t('Enter the URL of an OPML file. This file will be downloaded and processed only once on submission of the form.'),
+ );
+ $form['refresh'] = array(
+ '#type' => 'select',
+ '#title' => t('Update interval'),
+ '#default_value' => 3600,
+ '#options' => $period,
+ '#description' => t('The length of time between feed updates. Requires a correctly configured cron maintenance task.', array('@cron' => url('admin/reports/status'))),
+ );
+ $form['block'] = array(
+ '#type' => 'select',
+ '#title' => t('News items in block'),
+ '#default_value' => 5,
+ '#options' => drupal_map_assoc(array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20)),
+ '#description' => t("Drupal can make a block with the most recent news items of a feed. You can configure blocks to be displayed in the sidebar of your page. This setting lets you configure the number of news items to show in a feed's block. If you choose '0' these feeds' blocks will be disabled.", array('@block-admin' => url('admin/structure/block'))),
+ );
+
+ // Handling of categories.
+ $options = array_map('check_plain', $this->database->query("SELECT cid, title FROM {aggregator_category} ORDER BY title")->fetchAllKeyed());
+ if ($options) {
+ $form['category'] = array(
+ '#type' => 'checkboxes',
+ '#title' => t('Categorize news items'),
+ '#options' => $options,
+ '#description' => t('New feed items are automatically filed in the checked categories.'),
+ );
+ }
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Import'),
+ );
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateForm(array &$form, array &$form_state) {
+ // If both fields are empty or filled, cancel.
+ if (empty($form_state['values']['remote']) == empty($_FILES['files']['name']['upload'])) {
+ form_set_error('remote', t('You must either upload a file or enter a URL.'));
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, array &$form_state) {
+ $data = '';
+ $validators = array('file_validate_extensions' => array('opml xml'));
+ if ($file = file_save_upload('upload', $validators)) {
+ $data = file_get_contents($file->uri);
+ }
+ else {
+ // @todo Move this to a fetcher implementation.
+ try {
+ $response = $this->httpClient->get($form_state['values']['remote'])->send();
+ $data = $response->getBody(TRUE);
+ }
+ catch (BadResponseException $e) {
+ $response = $e->getResponse();
+ watchdog('aggregator', 'Failed to download OPML file due to "%error".', array('%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase()), WATCHDOG_WARNING);
+ drupal_set_message(t('Failed to download OPML file due to "%error".', array('%error' => $response->getStatusCode() . ' ' . $response->getReasonPhrase())));
+ return;
+ }
+ catch (RequestException $e) {
+ watchdog('aggregator', 'Failed to download OPML file due to "%error".', array('%error' => $e->getMessage()), WATCHDOG_WARNING);
+ drupal_set_message(t('Failed to download OPML file due to "%error".', array('%error' => $e->getMessage())));
+ return;
+ }
+ }
+
+ $feeds = $this->parseOpml($data);
+ if (empty($feeds)) {
+ drupal_set_message(t('No new feed has been added.'));
+ return;
+ }
+
+ // @todo Move this functionality to a processor.
+ foreach ($feeds as $feed) {
+ // Ensure URL is valid.
+ if (!valid_url($feed['url'], TRUE)) {
+ drupal_set_message(t('The URL %url is invalid.', array('%url' => $feed['url'])), 'warning');
+ continue;
+ }
+
+ // Check for duplicate titles or URLs.
+ $query = $this->queryFactory->get('aggregator_feed');
+ $condition = $query->orConditionGroup()
+ ->condition('title', $feed['title'])
+ ->condition('url', $feed['url']);
+ $ids = $query
+ ->condition($condition)
+ ->execute();
+ $result = $this->entityManager
+ ->getStorageController('aggregator_feed')
+ ->load($ids);
+ foreach ($result as $old) {
+ if (strcasecmp($old->label(), $feed['title']) == 0) {
+ drupal_set_message(t('A feed named %title already exists.', array('%title' => $old->label())), 'warning');
+ continue 2;
+ }
+ if (strcasecmp($old->url->value, $feed['url']) == 0) {
+ drupal_set_message(t('A feed with the URL %url already exists.', array('%url' => $old->url->value)), 'warning');
+ continue 2;
+ }
+ }
+
+ $new_feed = $this->entityManager
+ ->getStorageController('aggregator_feed')
+ ->create(array(
+ 'title' => $feed['title'],
+ 'url' => $feed['url'],
+ 'refresh' => $form_state['values']['refresh'],
+ 'block' => $form_state['values']['block'],
+ ));
+ $new_feed->categories = $form_state['values']['category'];
+ $new_feed->save();
+ }
+
+ $form_state['redirect'] = 'admin/config/services/aggregator';
+ }
+
+ /**
+ * Parses an OPML file.
+ *
+ * Feeds are recognized as elements with the attributes "text" and
+ * "xmlurl" set.
+ *
+ * @todo Move this functionality to a parser.
+ *
+ * @param $opml
+ * The complete contents of an OPML document.
+ *
+ * @return
+ * An array of feeds, each an associative array with a "title" and a "url"
+ * element, or NULL if the OPML document failed to be parsed. An empty array
+ * will be returned if the document is valid but contains no feeds, as some
+ * OPML documents do.
+ */
+ protected function parseOpml($opml) {
+ $feeds = array();
+ $xml_parser = drupal_xml_parser_create($opml);
+ if (xml_parse_into_struct($xml_parser, $opml, $values)) {
+ foreach ($values as $entry) {
+ if ($entry['tag'] == 'OUTLINE' && isset($entry['attributes'])) {
+ $item = $entry['attributes'];
+ if (!empty($item['XMLURL']) && !empty($item['TEXT'])) {
+ $feeds[] = array('title' => $item['TEXT'], 'url' => $item['XMLURL']);
+ }
+ }
+ }
+ }
+ xml_parser_free($xml_parser);
+
+ return $feeds;
+ }
+
+}