updated core and modules

This commit is contained in:
Bachir Soussi Chiadmi
2017-09-09 16:27:51 +02:00
parent 027aa99b32
commit 41863f872c
7810 changed files with 318922 additions and 83631 deletions
+3 -3
View File
@@ -6,8 +6,8 @@ package: Core
# core: 8.x
configure: entity.action.collection
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233790
datestamp: 1502903957
+3 -3
View File
@@ -15,13 +15,13 @@ function action_help($route_name, RouteMatchInterface $route_match) {
case 'help.page.action':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Actions module provides tasks that can be executed by the site such as unpublishing content, sending email messages, or blocking a user. Other modules can trigger these actions when specific system events happen; for example, when new content is posted or when a user logs in. Modules can also provide additional actions. For more information, see the <a href=":documentation">online documentation for the Actions module</a>.', array(':documentation' => 'https://www.drupal.org/documentation/modules/action')) . '</p>';
$output .= '<p>' . t('The Actions module provides tasks that can be executed by the site such as unpublishing content, sending email messages, or blocking a user. Other modules can trigger these actions when specific system events happen; for example, when new content is posted or when a user logs in. Modules can also provide additional actions. For more information, see the <a href=":documentation">online documentation for the Actions module</a>.', [':documentation' => 'https://www.drupal.org/documentation/modules/action']) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Using simple actions') . '</dt>';
$output .= '<dd>' . t('<em>Simple actions</em> do not require configuration and are listed automatically as available on the <a href=":actions">Actions page</a>.', array(':actions' => \Drupal::url('entity.action.collection'))) . '</dd>';
$output .= '<dd>' . t('<em>Simple actions</em> do not require configuration and are listed automatically as available on the <a href=":actions">Actions page</a>.', [':actions' => \Drupal::url('entity.action.collection')]) . '</dd>';
$output .= '<dt>' . t('Creating and configuring advanced actions') . '</dt>';
$output .= '<dd>' . t('<em>Advanced actions</em> are user-created and have to be configured individually. Create an advanced action on the <a href=":actions">Actions page</a> by selecting an action type from the drop-down list. Then configure your action, for example by specifying the recipient of an automated email message.', array(':actions' => \Drupal::url('entity.action.collection'))) . '</dd>';
$output .= '<dd>' . t('<em>Advanced actions</em> are user-created and have to be configured individually. Create an advanced action on the <a href=":actions">Actions page</a> by selecting an action type from the drop-down list. Then configure your action, for example by specifying the recipient of an automated email message.', [':actions' => \Drupal::url('entity.action.collection')]) . '</dd>';
$output .= '</dl>';
return $output;
@@ -9,12 +9,12 @@
* Implements hook_views_form_substitutions().
*/
function action_views_form_substitutions() {
$select_all = array(
$select_all = [
'#type' => 'checkbox',
'#default_value' => FALSE,
'#attributes' => array('class' => array('action-table-select-all')),
);
return array(
'#attributes' => ['class' => ['action-table-select-all']],
];
return [
'<!--action-bulk-form-select-all-->' => drupal_render($select_all),
);
];
}
@@ -0,0 +1,14 @@
id: action_settings
label: Action configuration
migration_tags:
- Drupal 6
- Drupal 7
source:
plugin: variable
variables:
- actions_max_stack
process:
recursion_limit: actions_max_stack
destination:
plugin: config
config_name: action.settings
+11 -11
View File
@@ -58,32 +58,32 @@ abstract class ActionFormBase extends EntityForm {
* {@inheritdoc}
*/
public function form(array $form, FormStateInterface $form_state) {
$form['label'] = array(
$form['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#default_value' => $this->entity->label(),
'#maxlength' => '255',
'#description' => $this->t('A unique label for this advanced action. This label will be displayed in the interface of modules that integrate with actions.'),
);
];
$form['id'] = array(
$form['id'] = [
'#type' => 'machine_name',
'#default_value' => $this->entity->id(),
'#disabled' => !$this->entity->isNew(),
'#maxlength' => 64,
'#description' => $this->t('A unique name for this action. It must only contain lowercase letters, numbers and underscores.'),
'#machine_name' => array(
'exists' => array($this, 'exists'),
),
);
$form['plugin'] = array(
'#machine_name' => [
'exists' => [$this, 'exists'],
],
];
$form['plugin'] = [
'#type' => 'value',
'#value' => $this->entity->get('plugin'),
);
$form['type'] = array(
];
$form['type'] = [
'#type' => 'value',
'#value' => $this->entity->getType(),
);
];
if ($this->plugin instanceof PluginFormInterface) {
$form += $this->plugin->buildConfigurationForm($form, $form_state);
@@ -86,10 +86,10 @@ class ActionListBuilder extends ConfigEntityListBuilder {
* {@inheritdoc}
*/
public function buildHeader() {
$header = array(
$header = [
'type' => t('Action type'),
'label' => t('Label'),
) + parent::buildHeader();
] + parent::buildHeader();
return $header;
}
@@ -97,7 +97,7 @@ class ActionListBuilder extends ConfigEntityListBuilder {
* {@inheritdoc}
*/
public function getDefaultOperations(EntityInterface $entity) {
$operations = $entity->isConfigurable() ? parent::getDefaultOperations($entity) : array();
$operations = $entity->isConfigurable() ? parent::getDefaultOperations($entity) : [];
if (isset($operations['edit'])) {
$operations['edit']['title'] = t('Configure');
}
@@ -108,10 +108,10 @@ class ActionListBuilder extends ConfigEntityListBuilder {
* {@inheritdoc}
*/
public function render() {
$build['action_header']['#markup'] = '<h3>' . t('Available actions:') . '</h3>';
$build['action_header']['#markup'] = '<h3>' . $this->t('Available actions:') . '</h3>';
$build['action_table'] = parent::render();
if (!$this->hasConfigurableActions) {
unset($build['action_table']['#header']['operations']);
unset($build['action_table']['table']['#header']['operations']);
}
$build['action_admin_manage_form'] = \Drupal::formBuilder()->getForm('Drupal\action\Form\ActionAdminManageForm');
return $build;
@@ -50,33 +50,33 @@ class ActionAdminManageForm extends FormBase {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$actions = array();
$actions = [];
foreach ($this->manager->getDefinitions() as $id => $definition) {
if (is_subclass_of($definition['class'], '\Drupal\Core\Plugin\PluginFormInterface')) {
$key = Crypt::hashBase64($id);
$actions[$key] = $definition['label'] . '...';
}
}
$form['parent'] = array(
$form['parent'] = [
'#type' => 'details',
'#title' => $this->t('Create an advanced action'),
'#attributes' => array('class' => array('container-inline')),
'#attributes' => ['class' => ['container-inline']],
'#open' => TRUE,
);
$form['parent']['action'] = array(
];
$form['parent']['action'] = [
'#type' => 'select',
'#title' => $this->t('Action'),
'#title_display' => 'invisible',
'#options' => $actions,
'#empty_option' => $this->t('Choose an advanced action'),
);
$form['parent']['actions'] = array(
];
$form['parent']['actions'] = [
'#type' => 'actions'
);
$form['parent']['actions']['submit'] = array(
];
$form['parent']['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Create'),
);
];
return $form;
}
@@ -87,7 +87,7 @@ class ActionAdminManageForm extends FormBase {
if ($form_state->getValue('action')) {
$form_state->setRedirect(
'action.admin_add',
array('action_id' => $form_state->getValue('action'))
['action_id' => $form_state->getValue('action')]
);
}
}
@@ -84,9 +84,9 @@ class EmailAction extends ConfigurableActionBase implements ContainerFactoryPlug
* The entity manager.
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
* @param \Drupal\Core\Mail\MailManagerInterface
* @param \Drupal\Core\Mail\MailManagerInterface $mail_manager
* The mail manager.
* @param \Drupal\Core\Language\LanguageManagerInterface
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Egulias\EmailValidator\EmailValidator $email_validator
* The email validator.
@@ -129,7 +129,7 @@ class EmailAction extends ConfigurableActionBase implements ContainerFactoryPlug
// If the recipient is a registered user with a language preference, use
// the recipient's preferred language. Otherwise, use the system default
// language.
$recipient_accounts = $this->storage->loadByProperties(array('mail' => $recipient));
$recipient_accounts = $this->storage->loadByProperties(['mail' => $recipient]);
$recipient_account = reset($recipient_accounts);
if ($recipient_account) {
$langcode = $recipient_account->getPreferredLangcode();
@@ -137,13 +137,13 @@ class EmailAction extends ConfigurableActionBase implements ContainerFactoryPlug
else {
$langcode = $this->languageManager->getDefaultLanguage()->getId();
}
$params = array('context' => $this->configuration);
$params = ['context' => $this->configuration];
if ($this->mailManager->mail('system', 'action_send_email', $recipient, $langcode, $params)) {
$this->logger->notice('Sent email to %recipient', array('%recipient' => $recipient));
$this->logger->notice('Sent email to %recipient', ['%recipient' => $recipient]);
}
else {
$this->logger->error('Unable to send email to %recipient', array('%recipient' => $recipient));
$this->logger->error('Unable to send email to %recipient', ['%recipient' => $recipient]);
}
}
@@ -151,39 +151,39 @@ class EmailAction extends ConfigurableActionBase implements ContainerFactoryPlug
* {@inheritdoc}
*/
public function defaultConfiguration() {
return array(
return [
'recipient' => '',
'subject' => '',
'message' => '',
);
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['recipient'] = array(
$form['recipient'] = [
'#type' => 'textfield',
'#title' => t('Recipient'),
'#title' => t('Recipient email address'),
'#default_value' => $this->configuration['recipient'],
'#maxlength' => '254',
'#description' => t('The email address to which the message should be sent OR enter [node:author:mail], [comment:author:mail], etc. if you would like to send an email to the author of the original post.'),
);
$form['subject'] = array(
'#description' => t('You may also use tokens: [node:author:mail], [comment:author:mail], etc. Separate recipients with a comma.'),
];
$form['subject'] = [
'#type' => 'textfield',
'#title' => t('Subject'),
'#default_value' => $this->configuration['subject'],
'#maxlength' => '254',
'#description' => t('The subject of the message.'),
);
$form['message'] = array(
];
$form['message'] = [
'#type' => 'textarea',
'#title' => t('Message'),
'#default_value' => $this->configuration['message'],
'#cols' => '80',
'#rows' => '20',
'#description' => t('The message that should be sent. You may include placeholders like [node:title], [user:account-name], [user:display-name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
);
];
return $form;
}
@@ -193,7 +193,7 @@ class EmailAction extends ConfigurableActionBase implements ContainerFactoryPlug
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
if (!$this->emailValidator->isValid($form_state->getValue('recipient')) && strpos($form_state->getValue('recipient'), ':mail') === FALSE) {
// We want the literal %author placeholder to be emphasized in the error message.
$form_state->setErrorByName('recipient', t('Enter a valid email address or use a token email address such as %author.', array('%author' => '[node:author:mail]')));
$form_state->setErrorByName('recipient', t('Enter a valid email address or use a token email address such as %author.', ['%author' => '[node:author:mail]']));
}
}
@@ -103,22 +103,22 @@ class GotoAction extends ConfigurableActionBase implements ContainerFactoryPlugi
* {@inheritdoc}
*/
public function defaultConfiguration() {
return array(
return [
'url' => '',
);
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['url'] = array(
$form['url'] = [
'#type' => 'textfield',
'#title' => t('URL'),
'#description' => t('The URL to which the user should be redirected. This can be an internal URL like /node/1234 or an external URL like @url.', array('@url' => 'http://example.com')),
'#description' => t('The URL to which the user should be redirected. This can be an internal URL like /node/1234 or an external URL like @url.', ['@url' => 'http://example.com']),
'#default_value' => $this->configuration['url'],
'#required' => TRUE,
);
];
return $form;
}
@@ -84,23 +84,23 @@ class MessageAction extends ConfigurableActionBase implements ContainerFactoryPl
* {@inheritdoc}
*/
public function defaultConfiguration() {
return array(
return [
'message' => '',
);
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['message'] = array(
$form['message'] = [
'#type' => 'textarea',
'#title' => t('Message'),
'#default_value' => $this->configuration['message'],
'#required' => TRUE,
'#rows' => '8',
'#description' => t('The message to be displayed to the current user. You may include placeholders like [node:title], [user:account-name], [user:display-name] and [comment:body] to represent data that will be different each time message is sent. Not all placeholders will be available in all contexts.'),
);
];
return $form;
}
@@ -27,12 +27,12 @@ class Action extends DrupalSqlBase {
* {@inheritdoc}
*/
public function fields() {
$fields = array(
$fields = [
'aid' => $this->t('Action ID'),
'type' => $this->t('Module'),
'callback' => $this->t('Callback function'),
'parameters' => $this->t('Action configuration'),
);
];
if ($this->getModuleSchemaVersion('system') >= 7000) {
$fields['label'] = $this->t('Label of the action');
}
@@ -9,8 +9,8 @@ dependencies:
- views
- node
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233790
datestamp: 1502903957
@@ -132,7 +132,7 @@ display:
entity_type: node
filters:
status:
value: true
value: '1'
table: node_field_data
field: status
id: status
@@ -0,0 +1,37 @@
<?php
namespace Drupal\Tests\action\Functional;
use Drupal\Tests\BrowserTestBase;
/**
* Test behaviors when visiting the action listing page.
*
* @group action
*/
class ActionListTest extends BrowserTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['action'];
/**
* Tests the behavior when there are no actions to list in the admin page.
*/
public function testEmptyActionList() {
// Create a user with permission to view the actions administration pages.
$this->drupalLogin($this->drupalCreateUser(['administer actions']));
// Ensure the empty text appears on the action list page.
/** @var $storage \Drupal\Core\Entity\EntityStorageInterface */
$storage = $this->container->get('entity.manager')->getStorage('action');
$actions = $storage->loadMultiple();
$storage->delete($actions);
$this->drupalGet('/admin/config/system/actions');
$this->assertRaw('There is no Action yet.');
}
}
@@ -17,17 +17,19 @@ class ActionUninstallTest extends BrowserTestBase {
*
* @var array
*/
public static $modules = array('views', 'action');
public static $modules = ['views', 'action'];
/**
* Tests Action uninstall.
*/
public function testActionUninstall() {
\Drupal::service('module_installer')->uninstall(array('action'));
\Drupal::service('module_installer')->uninstall(['action']);
$this->assertTrue(entity_load('action', 'user_block_user_action', TRUE), 'Configuration entity \'user_block_user_action\' still exists after uninstalling action module.' );
$storage = $this->container->get('entity_type.manager')->getStorage('action');
$storage->resetCache(['user_block_user_action']);
$this->assertTrue($storage->load('user_block_user_action'), 'Configuration entity \'user_block_user_action\' still exists after uninstalling action module.' );
$admin_user = $this->drupalCreateUser(array('administer users'));
$admin_user = $this->drupalCreateUser(['administer users']);
$this->drupalLogin($admin_user);
$this->drupalGet('admin/people');
@@ -18,7 +18,7 @@ class BulkFormTest extends BrowserTestBase {
*
* @var array
*/
public static $modules = array('node', 'action_bulk_test');
public static $modules = ['node', 'action_bulk_test'];
/**
* Tests the bulk form.
@@ -31,30 +31,30 @@ class BulkFormTest extends BrowserTestBase {
$this->drupalGet('test_bulk_form_empty');
$this->assertText(t('This view is empty.'), 'Empty text found on empty bulk form.');
$nodes = array();
$nodes = [];
for ($i = 0; $i < 10; $i++) {
// Ensure nodes are sorted in the same order they are inserted in the
// array.
$timestamp = REQUEST_TIME - $i;
$nodes[] = $this->drupalCreateNode(array(
$nodes[] = $this->drupalCreateNode([
'sticky' => FALSE,
'created' => $timestamp,
'changed' => $timestamp,
));
]);
}
$this->drupalGet('test_bulk_form');
// Test that the views edit header appears first.
$first_form_element = $this->xpath('//form/div[1][@id = :id]', array(':id' => 'edit-header'));
$first_form_element = $this->xpath('//form/div[1][@id = :id]', [':id' => 'edit-header']);
$this->assertTrue($first_form_element, 'The views form edit header appears first.');
$this->assertFieldById('edit-action', NULL, 'The action select field appears.');
// Make sure a checkbox appears on all rows.
$edit = array();
$edit = [];
for ($i = 0; $i < 10; $i++) {
$this->assertFieldById('edit-node-bulk-form-' . $i, NULL, format_string('The checkbox on row @row appears.', array('@row' => $i)));
$this->assertFieldById('edit-node-bulk-form-' . $i, NULL, format_string('The checkbox on row @row appears.', ['@row' => $i]));
$edit["node_bulk_form[$i]"] = TRUE;
}
@@ -67,12 +67,12 @@ class BulkFormTest extends BrowserTestBase {
$this->drupalGet('test_bulk_form');
// Set all nodes to sticky and check that.
$edit += array('action' => 'node_make_sticky_action');
$this->drupalPostForm(NULL, $edit, t('Apply'));
$edit += ['action' => 'node_make_sticky_action'];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
foreach ($nodes as $node) {
$changed_node = $node_storage->load($node->id());
$this->assertTrue($changed_node->isSticky(), format_string('Node @nid got marked as sticky.', array('@nid' => $node->id())));
$this->assertTrue($changed_node->isSticky(), format_string('Node @nid got marked as sticky.', ['@nid' => $node->id()]));
}
$this->assertText('Make content sticky was applied to 10 items.');
@@ -81,18 +81,18 @@ class BulkFormTest extends BrowserTestBase {
$node = $node_storage->load($nodes[0]->id());
$this->assertTrue($node->isPublished(), 'The node is published.');
$edit = array('node_bulk_form[0]' => TRUE, 'action' => 'node_unpublish_action');
$this->drupalPostForm(NULL, $edit, t('Apply'));
$edit = ['node_bulk_form[0]' => TRUE, 'action' => 'node_unpublish_action'];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$this->assertText('Unpublish content was applied to 1 item.');
// Load the node again.
$node_storage->resetCache(array($node->id()));
$node_storage->resetCache([$node->id()]);
$node = $node_storage->load($node->id());
$this->assertFalse($node->isPublished(), 'A single node has been unpublished.');
// The second node should still be published.
$node_storage->resetCache(array($nodes[1]->id()));
$node_storage->resetCache([$nodes[1]->id()]);
$node = $node_storage->load($nodes[1]->id());
$this->assertTrue($node->isPublished(), 'An unchecked node is still published.');
@@ -105,7 +105,7 @@ class BulkFormTest extends BrowserTestBase {
$view->save();
$this->drupalGet('test_bulk_form');
$options = $this->xpath('//select[@id=:id]/option', array(':id' => 'edit-action'));
$options = $this->xpath('//select[@id=:id]/option', [':id' => 'edit-action']);
$this->assertEqual(count($options), 2);
$this->assertOption('edit-action', 'node_make_sticky_action');
$this->assertOption('edit-action', 'node_make_unsticky_action');
@@ -123,7 +123,7 @@ class BulkFormTest extends BrowserTestBase {
// Check the default title.
$this->drupalGet('test_bulk_form');
$result = $this->xpath('//label[@for="edit-action"]');
$this->assertEqual('With selection', $result[0]->getText());
$this->assertEqual('Action', $result[0]->getText());
// Setup up a different bulk form title.
$view = Views::getView('test_bulk_form');
@@ -137,17 +137,17 @@ class BulkFormTest extends BrowserTestBase {
$this->drupalGet('test_bulk_form');
// Call the node delete action.
$edit = array();
$edit = [];
for ($i = 0; $i < 5; $i++) {
$edit["node_bulk_form[$i]"] = TRUE;
}
$edit += array('action' => 'node_delete_action');
$this->drupalPostForm(NULL, $edit, t('Apply'));
$edit += ['action' => 'node_delete_action'];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
// Make sure we don't show an action message while we are still on the
// confirmation page.
$errors = $this->xpath('//div[contains(@class, "messages--status")]');
$this->assertFalse($errors, 'No action message shown.');
$this->drupalPostForm(NULL, array(), t('Delete'));
$this->drupalPostForm(NULL, [], t('Delete'));
$this->assertText(t('Deleted 5 posts.'));
// Check if we got redirected to the original page.
$this->assertUrl('test_bulk_form');
@@ -3,6 +3,7 @@
namespace Drupal\Tests\action\Functional;
use Drupal\Component\Utility\Crypt;
use Drupal\system\Entity\Action;
use Drupal\Tests\BrowserTestBase;
/**
@@ -18,24 +19,24 @@ class ConfigurationTest extends BrowserTestBase {
*
* @var array
*/
public static $modules = array('action');
public static $modules = ['action'];
/**
* Tests configuration of advanced actions through administration interface.
*/
function testActionConfiguration() {
public function testActionConfiguration() {
// Create a user with permission to view the actions administration pages.
$user = $this->drupalCreateUser(array('administer actions'));
$user = $this->drupalCreateUser(['administer actions']);
$this->drupalLogin($user);
// Make a POST request to admin/config/system/actions.
$edit = array();
$edit = [];
$edit['action'] = Crypt::hashBase64('action_goto_action');
$this->drupalPostForm('admin/config/system/actions', $edit, t('Create'));
$this->assertResponse(200);
// Make a POST request to the individual action configuration page.
$edit = array();
$edit = [];
$action_label = $this->randomMachineName();
$edit['label'] = $action_label;
$edit['id'] = strtolower($action_label);
@@ -51,7 +52,7 @@ class ConfigurationTest extends BrowserTestBase {
$this->clickLink(t('Configure'));
preg_match('|admin/config/system/actions/configure/(.+)|', $this->getUrl(), $matches);
$aid = $matches[1];
$edit = array();
$edit = [];
$new_action_label = $this->randomMachineName();
$edit['label'] = $new_action_label;
$edit['url'] = 'admin';
@@ -71,17 +72,17 @@ class ConfigurationTest extends BrowserTestBase {
$this->drupalGet('admin/config/system/actions');
$this->clickLink(t('Delete'));
$this->assertResponse(200);
$edit = array();
$edit = [];
$this->drupalPostForm("admin/config/system/actions/configure/$aid/delete", $edit, t('Delete'));
$this->assertResponse(200);
// Make sure that the action was actually deleted.
$this->assertRaw(t('The action %action has been deleted.', array('%action' => $new_action_label)), 'Make sure that we get a delete confirmation message.');
$this->assertRaw(t('The action %action has been deleted.', ['%action' => $new_action_label]), 'Make sure that we get a delete confirmation message.');
$this->drupalGet('admin/config/system/actions');
$this->assertResponse(200);
$this->assertNoText($new_action_label, "Make sure the action label does not appear on the overview page after we've deleted the action.");
$action = entity_load('action', $aid);
$action = Action::load($aid);
$this->assertFalse($action, 'Make sure the action is gone after being deleted.');
}
@@ -2,7 +2,7 @@
namespace Drupal\Tests\action\Kernel\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
@@ -24,7 +24,7 @@ class MigrateActionConfigsTest extends MigrateDrupal6TestBase {
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('d6_action_settings');
$this->executeMigration('action_settings');
}
/**
@@ -0,0 +1,39 @@
<?php
namespace Drupal\Tests\action\Kernel\Migrate\d7;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
/**
* Upgrade variables to action.settings.yml.
*
* @group migrate_drupal_7
*/
class MigrateActionConfigsTest extends MigrateDrupal7TestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['action'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->executeMigration('action_settings');
}
/**
* Tests migration of action variables to action.settings.yml.
*/
public function testActionSettings() {
$config = $this->config('action.settings');
$this->assertSame(28, $config->get('recursion_limit'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'action.settings', $config->get());
}
}
@@ -0,0 +1,62 @@
<?php
namespace Drupal\Tests\action\Kernel\Plugin\migrate\source;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests actions source plugin.
*
* @covers \Drupal\action\Plugin\migrate\source\Action
* @group action
*/
class ActionTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['action', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
$tests[0][0]['actions'] = [
[
'aid' => 'Redirect to node list page',
'type' => 'system',
'callback' => 'system_goto_action',
'parameters' => 'a:1:{s:3:"url";s:4:"node";}',
'description' => 'Redirect to node list page',
],
[
'aid' => 'Test notice email',
'type' => 'system',
'callback' => 'system_send_email_action',
'parameters' => 'a:3:{s:9:"recipient";s:7:"%author";s:7:"subject";s:4:"Test";s:7:"message";s:4:"Test',
'description' => 'Test notice email',
],
[
'aid' => 'comment_publish_action',
'type' => 'comment',
'callback' => 'comment_publish_action',
'parameters' => NULL,
'description' => NULL,
],
[
'aid' => 'node_publish_action',
'type' => 'comment',
'callback' => 'node_publish_action',
'parameters' => NULL,
'description' => NULL,
],
];
// The expected results are identical to the source data.
$tests[0][1] = $tests[0][0]['actions'];
return $tests;
}
}
@@ -12,7 +12,7 @@ use Drupal\Tests\Core\Menu\LocalTaskIntegrationTestBase;
class ActionLocalTasksTest extends LocalTaskIntegrationTestBase {
protected function setUp() {
$this->directoryList = array('action' => 'core/modules/action');
$this->directoryList = ['action' => 'core/modules/action'];
parent::setUp();
}
@@ -20,7 +20,7 @@ class ActionLocalTasksTest extends LocalTaskIntegrationTestBase {
* Tests local task existence.
*/
public function testActionLocalTasks() {
$this->assertLocalTasks('entity.action.collection', array(array('action.admin')));
$this->assertLocalTasks('entity.action.collection', [['action.admin']]);
}
}
+3 -3
View File
@@ -9,8 +9,8 @@ dependencies:
- file
- options
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233792
datestamp: 1502903957
+13 -10
View File
@@ -10,23 +10,18 @@
*/
function aggregator_requirements($phase) {
$has_curl = function_exists('curl_init');
$requirements = array();
$requirements['curl'] = array(
$requirements = [];
$requirements['curl'] = [
'title' => t('cURL'),
'value' => $has_curl ? t('Enabled') : t('Not found'),
);
];
if (!$has_curl) {
$requirements['curl']['severity'] = REQUIREMENT_ERROR;
$requirements['curl']['description'] = t('The Aggregator module could not be installed because the PHP <a href="http://php.net/manual/curl.setup.php">cURL</a> library is not available.');
$requirements['curl']['description'] = t('The Aggregator module requires the <a href="https://secure.php.net/manual/en/curl.setup.php">PHP cURL library</a>. For more information, see the <a href="https://www.drupal.org/requirements/php/curl">online information on installing the PHP cURL extension</a>.');
}
return $requirements;
}
/**
* @addtogroup updates-8.0.0-rc
* @{
*/
/**
* The simple presence of this update function clears cached field definitions.
*/
@@ -35,5 +30,13 @@ function aggregator_update_8001() {
}
/**
* @} End of "addtogroup updates-8.0.0-rc".
* Make the 'Source feed' field for aggregator items required.
*/
function aggregator_update_8200() {
// aggregator_update_8001() did not update the last installed field storage
// definition for the aggregator item's 'Source feed' field.
$definition_update_manager = \Drupal::entityDefinitionUpdateManager();
$field_definition = $definition_update_manager->getFieldStorageDefinition('fid', 'aggregator_item');
$field_definition->setRequired(TRUE);
$definition_update_manager->updateFieldStorageDefinition($field_definition);
}
@@ -1,11 +1,11 @@
aggregator.admin_overview:
title: 'Feed aggregator'
title: 'Aggregator'
description: 'Add feeds or import OPMLs to gather external content and configure how often they are updated.'
route_name: aggregator.admin_overview
parent: system.admin_config_services
weight: 10
aggregator.page_last:
title: 'Feed aggregator'
title: 'Aggregator'
weight: 5
route_name: aggregator.page_last
aggregator.feed_add:
+38 -35
View File
@@ -10,6 +10,9 @@ use Drupal\Core\Routing\RouteMatchInterface;
/**
* Denotes that a feed's items should never expire.
*
* @deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.0.
* Use \Drupal\aggregator\FeedStorageInterface::CLEAR_NEVER instead.
*/
const AGGREGATOR_CLEAR_NEVER = 0;
@@ -22,35 +25,35 @@ function aggregator_help($route_name, RouteMatchInterface $route_match) {
$path_validator = \Drupal::pathValidator();
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$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>.', array(':aggregator-module' => 'https://www.drupal.org/documentation/modules/aggregator')) . '</p>';
$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>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
// Check if the aggregator sources View is enabled.
if ($url = $path_validator->getUrlIfValid('aggregator/sources')) {
$output .= '<dt>' . t('Viewing feeds') . '</dt>';
$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>.', array(':aggregator' => \Drupal::url('aggregator.page_last'), ':aggregator-sources' => $url->toString(), ':admin-block' => (\Drupal::moduleHandler()->moduleExists('block')) ? \Drupal::url('block.admin_display') : '#')) . '</dd>';
$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' => \Drupal::url('aggregator.page_last'), ':aggregator-sources' => $url->toString(), ':admin-block' => (\Drupal::moduleHandler()->moduleExists('block')) ? \Drupal::url('block.admin_display') : '#']) . '</dd>';
}
$output .= '<dt>' . t('Adding, editing, and deleting feeds') . '</dt>';
$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">Feed aggregator page</a>.', array(':feededit' => \Drupal::url('aggregator.admin_overview'))) . '</dd>';
$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' => \Drupal::url('aggregator.admin_overview')]) . '</dd>';
$output .= '<dt>' . t('Configuring the display of feed items') . '</dt>';
$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">Feed aggregator settings page</a>.', array(':settings' => \Drupal::url('aggregator.admin_settings'))) . '</dd>';
$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' => \Drupal::url('aggregator.admin_settings')]) . '</dd>';
$output .= '<dt>' . t('Discarding old feed items') . '</dt>';
$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">Feed aggregator settings page</a>. This requires a correctly configured cron maintenance task (see below).', array(':settings' => \Drupal::url('aggregator.admin_settings'))) . '<dd>';
$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' => \Drupal::url('aggregator.admin_settings')]) . '<dd>';
$output .= '<dt>' . t('<abbr title="Outline Processor Markup Language">OPML</abbr> integration') . '</dt>';
// Check if the aggregator opml View is enabled.
if ($url = $path_validator->getUrlIfValid('aggregator/opml')) {
$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>.', array(':aggregator-opml' => $url->toString(), ':import-opml' => \Drupal::url('aggregator.opml_add'))) . '</dd>';
$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' => \Drupal::url('aggregator.opml_add')]) . '</dd>';
}
$output .= '<dt>' . t('Configuring cron') . '</dt>';
$output .= '<dd>' . t('A working <a href=":cron">cron maintenance task</a> is required to update feeds automatically.', array(':cron' => \Drupal::url('system.cron_settings'))) . '</dd>';
$output .= '<dd>' . t('A working <a href=":cron">cron maintenance task</a> is required to update feeds automatically.', [':cron' => \Drupal::url('system.cron_settings')]) . '</dd>';
$output .= '</dl>';
return $output;
case 'aggregator.admin_overview':
// Don't use placeholders for possibility to change URLs for translators.
$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>';
$output .= '<p>' . t('Current feeds are listed below, and <a href=":addfeed">new feeds may be added</a>. For each feed, the <em>latest items</em> block may be enabled at the <a href=":block">blocks administration page</a>.', array(':addfeed' => \Drupal::url('aggregator.feed_add'), ':block' => (\Drupal::moduleHandler()->moduleExists('block')) ? \Drupal::url('block.admin_display') : '#')) . '</p>';
$output .= '<p>' . t('Current feeds are listed below, and <a href=":addfeed">new feeds may be added</a>. For each feed, the <em>latest items</em> block may be enabled at the <a href=":block">blocks administration page</a>.', [':addfeed' => \Drupal::url('aggregator.feed_add'), ':block' => (\Drupal::moduleHandler()->moduleExists('block')) ? \Drupal::url('block.admin_display') : '#']) . '</p>';
return $output;
case 'aggregator.feed_add':
@@ -65,66 +68,66 @@ function aggregator_help($route_name, RouteMatchInterface $route_match) {
* Implements hook_theme().
*/
function aggregator_theme() {
return array(
'aggregator_feed' => array(
return [
'aggregator_feed' => [
'render element' => 'elements',
'file' => 'aggregator.theme.inc',
),
'aggregator_item' => array(
],
'aggregator_item' => [
'render element' => 'elements',
'file' => 'aggregator.theme.inc',
),
);
],
];
}
/**
* Implements hook_entity_extra_field_info().
*/
function aggregator_entity_extra_field_info() {
$extra = array();
$extra = [];
$extra['aggregator_feed']['aggregator_feed'] = array(
'display' => array(
'items' => array(
$extra['aggregator_feed']['aggregator_feed'] = [
'display' => [
'items' => [
'label' => t('Items'),
'description' => t('Items associated with this feed'),
'weight' => 0,
),
],
// @todo Move to a formatter at https://www.drupal.org/node/2339917.
'image' => array(
'image' => [
'label' => t('Image'),
'description' => t('The feed image'),
'weight' => 2,
),
],
// @todo Move to a formatter at https://www.drupal.org/node/2149845.
'description' => array(
'description' => [
'label' => t('Description'),
'description' => t('The description of this feed'),
'weight' => 3,
),
'more_link' => array(
],
'more_link' => [
'label' => t('More link'),
'description' => t('A more link to the feed detail page'),
'weight' => 5,
),
'feed_icon' => array(
],
'feed_icon' => [
'label' => t('Feed icon'),
'description' => t('An icon that links to the feed URL'),
'weight' => 6,
),
),
);
],
],
];
$extra['aggregator_item']['aggregator_item'] = array(
'display' => array(
$extra['aggregator_item']['aggregator_item'] = [
'display' => [
// @todo Move to a formatter at https://www.drupal.org/node/2149845.
'description' => array(
'description' => [
'label' => t('Description'),
'description' => t('The description of this feed item'),
'weight' => 2,
),
),
);
],
],
];
return $extra;
}
@@ -2,7 +2,7 @@ aggregator.admin_overview:
path: '/admin/config/services/aggregator'
defaults:
_controller: '\Drupal\aggregator\Controller\AggregatorController::adminOverview'
_title: 'Feed aggregator'
_title: 'Aggregator'
requirements:
_permission: 'administer news feeds'
@@ -10,7 +10,7 @@ aggregator.admin_settings:
path: '/admin/config/services/aggregator/settings'
defaults:
_form: '\Drupal\aggregator\Form\SettingsForm'
_title: 'Feed aggregator settings'
_title: 'Aggregator settings'
requirements:
_permission: 'administer news feeds'
@@ -53,6 +53,6 @@ aggregator.page_last:
path: '/aggregator'
defaults:
_controller: '\Drupal\aggregator\Controller\AggregatorController::pageLast'
_title: 'Feed aggregator'
_title: 'Aggregator'
requirements:
_permission: 'access news feeds'
@@ -11,20 +11,26 @@ content:
checked:
type: timestamp_ago
weight: 1
region: content
settings: { }
third_party_settings: { }
label: inline
description:
weight: 3
region: content
feed_icon:
weight: 5
region: content
image:
weight: 2
region: content
items:
weight: 0
region: content
link:
type: uri_link
weight: 4
region: content
settings: { }
third_party_settings: { }
label: inline
@@ -12,8 +12,10 @@ mode: summary
content:
items:
weight: 0
region: content
more_link:
weight: 1
region: content
hidden:
checked: true
description: true
@@ -12,6 +12,7 @@ mode: summary
content:
timestamp:
weight: 0
region: content
hidden:
author: true
description: true
@@ -3,6 +3,7 @@ status: true
dependencies:
module:
- aggregator
- user
id: aggregator_rss_feed
label: 'Aggregator RSS feed'
module: aggregator
@@ -137,6 +138,8 @@ display:
- url.query_args
- user.permissions
cacheable: false
max-age: -1
tags: { }
feed_items:
display_plugin: feed
id: feed_items
@@ -154,3 +157,5 @@ display:
- 'languages:language_interface'
- user.permissions
cacheable: false
max-age: -1
tags: { }
@@ -143,7 +143,8 @@ display:
- 'languages:language_interface'
- url.query_args
- user.permissions
max-age: 0
max-age: -1
tags: { }
feed_1:
display_plugin: feed
id: feed_1
@@ -401,7 +402,8 @@ display:
- 'languages:language_content'
- 'languages:language_interface'
- user.permissions
max-age: 0
max-age: -1
tags: { }
page_1:
display_plugin: page
id: page_1
@@ -423,4 +425,5 @@ display:
- 'languages:language_interface'
- url.query_args
- user.permissions
max-age: 0
max-age: -1
tags: { }
@@ -7,7 +7,7 @@ source:
process:
iid: iid
fid:
plugin: migration
plugin: migration_lookup
migration: d6_aggregator_feed
source: fid
title: title
@@ -7,7 +7,7 @@ source:
process:
iid: iid
fid:
plugin: migration
plugin: migration_lookup
migration: d7_aggregator_feed
source: fid
title: title
@@ -15,12 +15,12 @@ class AggregatorFeedViewsData extends EntityViewsData {
public function getViewsData() {
$data = parent::getViewsData();
$data['aggregator_feed']['table']['join'] = array(
'aggregator_item' => array(
$data['aggregator_feed']['table']['join'] = [
'aggregator_item' => [
'left_field' => 'fid',
'field' => 'fid',
),
);
],
];
$data['aggregator_feed']['fid']['help'] = $this->t('The unique ID of the aggregator feed.');
$data['aggregator_feed']['fid']['argument']['id'] = 'aggregator_fid';
@@ -48,9 +48,9 @@ class AggregatorController extends ControllerBase {
*/
public function feedAdd() {
$feed = $this->entityManager()->getStorage('aggregator_feed')
->create(array(
->create([
'refresh' => 3600,
));
]);
return $this->entityFormBuilder()->getForm($feed);
}
@@ -67,15 +67,15 @@ class AggregatorController extends ControllerBase {
*/
protected function buildPageList(array $items, $feed_source = '') {
// Assemble output.
$build = array(
$build = [
'#type' => 'container',
'#attributes' => array('class' => array('aggregator-wrapper')),
);
$build['feed_source'] = is_array($feed_source) ? $feed_source : array('#markup' => $feed_source);
'#attributes' => ['class' => ['aggregator-wrapper']],
];
$build['feed_source'] = is_array($feed_source) ? $feed_source : ['#markup' => $feed_source];
if ($items) {
$build['items'] = $this->entityManager()->getViewBuilder('aggregator_item')
->viewMultiple($items, 'default');
$build['pager'] = array('#type' => 'pager');
$build['pager'] = ['#type' => 'pager'];
}
return $build;
}
@@ -94,8 +94,8 @@ class AggregatorController extends ControllerBase {
*/
public function feedRefresh(FeedInterface $aggregator_feed) {
$message = $aggregator_feed->refreshItems()
? $this->t('There is new syndicated content from %site.', array('%site' => $aggregator_feed->label()))
: $this->t('There is no new syndicated content from %site.', array('%site' => $aggregator_feed->label()));
? $this->t('There is new syndicated content from %site.', ['%site' => $aggregator_feed->label()])
: $this->t('There is no new syndicated content from %site.', ['%site' => $aggregator_feed->label()]);
drupal_set_message($message);
return $this->redirect('aggregator.admin_overview');
}
@@ -111,22 +111,22 @@ class AggregatorController extends ControllerBase {
$feeds = $entity_manager->getStorage('aggregator_feed')
->loadMultiple();
$header = array($this->t('Title'), $this->t('Items'), $this->t('Last update'), $this->t('Next update'), $this->t('Operations'));
$rows = array();
$header = [$this->t('Title'), $this->t('Items'), $this->t('Last update'), $this->t('Next update'), $this->t('Operations')];
$rows = [];
/** @var \Drupal\aggregator\FeedInterface[] $feeds */
foreach ($feeds as $feed) {
$row = array();
$row = [];
$row[] = $feed->link();
$row[] = $this->formatPlural($entity_manager->getStorage('aggregator_item')->getItemCount($feed), '1 item', '@count items');
$last_checked = $feed->getLastCheckedTime();
$refresh_rate = $feed->getRefreshRate();
$row[] = ($last_checked ? $this->t('@time ago', array('@time' => $this->dateFormatter->formatInterval(REQUEST_TIME - $last_checked))) : $this->t('never'));
$row[] = ($last_checked ? $this->t('@time ago', ['@time' => $this->dateFormatter->formatInterval(REQUEST_TIME - $last_checked)]) : $this->t('never'));
if (!$last_checked && $refresh_rate) {
$next_update = $this->t('imminently');
}
elseif ($last_checked && $refresh_rate) {
$next_update = $next = $this->t('%time left', array('%time' => $this->dateFormatter->formatInterval($last_checked + $refresh_rate - REQUEST_TIME)));
$next_update = $next = $this->t('%time left', ['%time' => $this->dateFormatter->formatInterval($last_checked + $refresh_rate - REQUEST_TIME)]);
}
else {
$next_update = $this->t('never');
@@ -136,33 +136,33 @@ class AggregatorController extends ControllerBase {
'title' => $this->t('Edit'),
'url' => Url::fromRoute('entity.aggregator_feed.edit_form', ['aggregator_feed' => $feed->id()]),
];
$links['delete'] = array(
$links['delete'] = [
'title' => $this->t('Delete'),
'url' => Url::fromRoute('entity.aggregator_feed.delete_form', ['aggregator_feed' => $feed->id()]),
);
$links['delete_items'] = array(
];
$links['delete_items'] = [
'title' => $this->t('Delete items'),
'url' => Url::fromRoute('aggregator.feed_items_delete', ['aggregator_feed' => $feed->id()]),
);
$links['update'] = array(
];
$links['update'] = [
'title' => $this->t('Update items'),
'url' => Url::fromRoute('aggregator.feed_refresh', ['aggregator_feed' => $feed->id()]),
);
$row[] = array(
'data' => array(
];
$row[] = [
'data' => [
'#type' => 'operations',
'#links' => $links,
),
);
],
];
$rows[] = $row;
}
$build['feeds'] = array(
$build['feeds'] = [
'#prefix' => '<h3>' . $this->t('Feed overview') . '</h3>',
'#type' => 'table',
'#header' => $header,
'#rows' => $rows,
'#empty' => $this->t('No feeds available. <a href=":link">Add feed</a>.', array(':link' => $this->url('aggregator.feed_add'))),
);
'#empty' => $this->t('No feeds available. <a href=":link">Add feed</a>.', [':link' => $this->url('aggregator.feed_add')]),
];
return $build;
}
@@ -176,7 +176,7 @@ class AggregatorController extends ControllerBase {
public function pageLast() {
$items = $this->entityManager()->getStorage('aggregator_item')->loadAll(20);
$build = $this->buildPageList($items);
$build['#attached']['feed'][] = array('aggregator/rss', $this->config('system.site')->get('name') . ' ' . $this->t('aggregator'));
$build['#attached']['feed'][] = ['aggregator/rss', $this->config('system.site')->get('name') . ' ' . $this->t('aggregator')];
return $build;
}
+24 -35
View File
@@ -88,11 +88,11 @@ class Feed extends ContentEntityBase implements FeedInterface {
* {@inheritdoc}
*/
public static function preCreate(EntityStorageInterface $storage, array &$values) {
$values += array(
$values += [
'link' => '',
'description' => '',
'image' => '',
);
];
}
/**
@@ -127,53 +127,42 @@ class Feed extends ContentEntityBase implements FeedInterface {
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['fid'] = BaseFieldDefinition::create('integer')
->setLabel(t('Feed ID'))
->setDescription(t('The ID of the aggregator feed.'))
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
$fields = parent::baseFieldDefinitions($entity_type);
$fields['uuid'] = BaseFieldDefinition::create('uuid')
->setLabel(t('UUID'))
->setDescription(t('The aggregator feed UUID.'))
->setReadOnly(TRUE);
$fields['fid']->setLabel(t('Feed ID'))
->setDescription(t('The ID of the aggregator feed.'));
$fields['uuid']->setDescription(t('The aggregator feed UUID.'));
$fields['langcode']->setLabel(t('Language code'))
->setDescription(t('The feed language code.'));
$fields['title'] = BaseFieldDefinition::create('string')
->setLabel(t('Title'))
->setDescription(t('The name of the feed (or the name of the website providing the feed).'))
->setRequired(TRUE)
->setSetting('max_length', 255)
->setDisplayOptions('form', array(
->setDisplayOptions('form', [
'type' => 'string_textfield',
'weight' => -5,
))
])
->setDisplayConfigurable('form', TRUE)
->addConstraint('FeedTitle');
$fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language code'))
->setDescription(t('The feed language code.'))
->setDisplayOptions('view', array(
'type' => 'hidden',
))
->setDisplayOptions('form', array(
'type' => 'language_select',
'weight' => 2,
));
$fields['url'] = BaseFieldDefinition::create('uri')
->setLabel(t('URL'))
->setDescription(t('The fully-qualified URL of the feed.'))
->setRequired(TRUE)
->setDisplayOptions('form', array(
->setDisplayOptions('form', [
'type' => 'uri',
'weight' => -3,
))
])
->setDisplayConfigurable('form', TRUE)
->addConstraint('FeedUrl');
$intervals = array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200);
$period = array_map(array(\Drupal::service('date.formatter'), 'formatInterval'), array_combine($intervals, $intervals));
$intervals = [900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200];
$period = array_map([\Drupal::service('date.formatter'), 'formatInterval'], array_combine($intervals, $intervals));
$period[AGGREGATOR_CLEAR_NEVER] = t('Never');
$fields['refresh'] = BaseFieldDefinition::create('list_integer')
@@ -182,21 +171,21 @@ class Feed extends ContentEntityBase implements FeedInterface {
->setSetting('unsigned', TRUE)
->setRequired(TRUE)
->setSetting('allowed_values', $period)
->setDisplayOptions('form', array(
->setDisplayOptions('form', [
'type' => 'options_select',
'weight' => -2,
))
])
->setDisplayConfigurable('form', TRUE);
$fields['checked'] = BaseFieldDefinition::create('timestamp')
->setLabel(t('Checked'))
->setDescription(t('Last time feed was checked for new items, as Unix timestamp.'))
->setDefaultValue(0)
->setDisplayOptions('view', array(
->setDisplayOptions('view', [
'label' => 'inline',
'type' => 'timestamp_ago',
'weight' => 1,
))
])
->setDisplayConfigurable('view', TRUE);
$fields['queued'] = BaseFieldDefinition::create('timestamp')
@@ -207,15 +196,15 @@ class Feed extends ContentEntityBase implements FeedInterface {
$fields['link'] = BaseFieldDefinition::create('uri')
->setLabel(t('URL'))
->setDescription(t('The link of the feed.'))
->setDisplayOptions('view', array(
->setDisplayOptions('view', [
'label' => 'inline',
'weight' => 4,
))
])
->setDisplayConfigurable('view', TRUE);
$fields['description'] = BaseFieldDefinition::create('string_long')
->setLabel(t('Description'))
->setDescription(t("The parent website's description that comes from the @description element in the feed.", array('@description' => '<description>')));
->setDescription(t("The parent website's description that comes from the @description element in the feed.", ['@description' => '<description>']));
$fields['image'] = BaseFieldDefinition::create('uri')
->setLabel(t('Image'))
+17 -18
View File
@@ -47,47 +47,46 @@ class Item extends ContentEntityBase implements ItemInterface {
* {@inheritdoc}
*/
public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
$fields['iid'] = BaseFieldDefinition::create('integer')
->setLabel(t('Aggregator item ID'))
->setDescription(t('The ID of the feed item.'))
->setReadOnly(TRUE)
->setSetting('unsigned', TRUE);
/** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
$fields = parent::baseFieldDefinitions($entity_type);
$fields['iid']->setLabel(t('Aggregator item ID'))
->setDescription(t('The ID of the feed item.'));
$fields['langcode']->setLabel(t('Language code'))
->setDescription(t('The feed item language code.'));
$fields['fid'] = BaseFieldDefinition::create('entity_reference')
->setLabel(t('Source feed'))
->setRequired(TRUE)
->setDescription(t('The aggregator feed entity associated with this item.'))
->setSetting('target_type', 'aggregator_feed')
->setDisplayOptions('view', array(
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'entity_reference_label',
'weight' => 0,
))
])
->setDisplayConfigurable('form', TRUE);
$fields['title'] = BaseFieldDefinition::create('string')
->setLabel(t('Title'))
->setDescription(t('The title of the feed item.'));
$fields['langcode'] = BaseFieldDefinition::create('language')
->setLabel(t('Language code'))
->setDescription(t('The feed item language code.'));
$fields['link'] = BaseFieldDefinition::create('uri')
->setLabel(t('Link'))
->setDescription(t('The link of the feed item.'))
->setDisplayOptions('view', array(
'type' => 'hidden',
))
->setDisplayOptions('view', [
'region' => 'hidden',
])
->setDisplayConfigurable('view', TRUE);
$fields['author'] = BaseFieldDefinition::create('string')
->setLabel(t('Author'))
->setDescription(t('The author of the feed item.'))
->setDisplayOptions('view', array(
->setDisplayOptions('view', [
'label' => 'hidden',
'weight' => 3,
))
])
->setDisplayConfigurable('view', TRUE);
$fields['description'] = BaseFieldDefinition::create('string_long')
@@ -97,11 +96,11 @@ class Item extends ContentEntityBase implements ItemInterface {
$fields['timestamp'] = BaseFieldDefinition::create('created')
->setLabel(t('Posted on'))
->setDescription(t('Posted date of the feed item, as a Unix timestamp.'))
->setDisplayOptions('view', array(
->setDisplayOptions('view', [
'label' => 'hidden',
'type' => 'timestamp_ago',
'weight' => 1,
))
])
->setDisplayConfigurable('view', TRUE);
// @todo Convert to a real UUID field in
@@ -21,11 +21,9 @@ class FeedAccessControlHandler extends EntityAccessControlHandler {
switch ($operation) {
case 'view':
return AccessResult::allowedIfHasPermission($account, 'access news feeds');
break;
default:
return AccessResult::allowedIfHasPermission($account, 'administer news feeds');
break;
}
}
+7 -4
View File
@@ -16,13 +16,16 @@ class FeedForm extends ContentEntityForm {
*/
public function save(array $form, FormStateInterface $form_state) {
$feed = $this->entity;
if ($feed->save() == SAVED_UPDATED) {
drupal_set_message($this->t('The feed %feed has been updated.', array('%feed' => $feed->label())));
$status = $feed->save();
$label = $feed->label();
$view_link = $feed->link($label, 'canonical');
if ($status == SAVED_UPDATED) {
drupal_set_message($this->t('The feed %feed has been updated.', ['%feed' => $view_link]));
$form_state->setRedirectUrl($feed->urlInfo('canonical'));
}
else {
$this->logger('aggregator')->notice('Feed %feed added.', array('%feed' => $feed->label(), 'link' => $this->l($this->t('View'), new Url('aggregator.admin_overview'))));
drupal_set_message($this->t('The feed %feed has been added.', array('%feed' => $feed->label())));
$this->logger('aggregator')->notice('Feed %feed added.', ['%feed' => $feed->label(), 'link' => $this->l($this->t('View'), new Url('aggregator.admin_overview'))]);
drupal_set_message($this->t('The feed %feed has been added.', ['%feed' => $view_link]));
}
}
+2 -2
View File
@@ -16,10 +16,10 @@ class FeedStorage extends SqlContentEntityStorage implements FeedStorageInterfac
* {@inheritdoc}
*/
public function getFeedIdsToRefresh() {
return $this->database->query('SELECT fid FROM {aggregator_feed} WHERE queued = 0 AND checked + refresh < :time AND refresh <> :never', array(
return $this->database->query('SELECT fid FROM {aggregator_feed} WHERE queued = 0 AND checked + refresh < :time AND refresh <> :never', [
':time' => REQUEST_TIME,
':never' => AGGREGATOR_CLEAR_NEVER,
))->fetchCol();
])->fetchCol();
}
}
@@ -9,6 +9,11 @@ use Drupal\Core\Entity\ContentEntityStorageInterface;
*/
interface FeedStorageInterface extends ContentEntityStorageInterface {
/**
* Denotes that a feed's items should never expire.
*/
const CLEAR_NEVER = 0;
/**
* Returns the fids of feeds that need to be refreshed.
*
@@ -24,9 +24,6 @@ class FeedStorageSchema extends SqlContentEntityStorageSchema {
break;
case 'queued':
$this->addSharedTableFieldIndex($storage_definition, $schema, TRUE);
break;
case 'title':
$this->addSharedTableFieldIndex($storage_definition, $schema, TRUE);
break;
+22 -22
View File
@@ -68,65 +68,65 @@ class FeedViewBuilder extends EntityViewBuilder {
if ($view_mode == 'full') {
// Also add the pager.
$build[$id]['pager'] = array('#type' => 'pager');
$build[$id]['pager'] = ['#type' => 'pager'];
}
}
if ($display->getComponent('description')) {
$build[$id]['description'] = array(
$build[$id]['description'] = [
'#markup' => $entity->getDescription(),
'#allowed_tags' => _aggregator_allowed_tags(),
'#prefix' => '<div class="feed-description">',
'#suffix' => '</div>',
);
];
}
if ($display->getComponent('image')) {
$image_link = array();
$image_link = [];
// Render the image as link if it is available.
$image = $entity->getImage();
$label = $entity->label();
$link_href = $entity->getWebsiteUrl();
if ($image && $label && $link_href) {
$link_title = array(
$link_title = [
'#theme' => 'image',
'#uri' => $image,
'#alt' => $label,
);
$image_link = array(
];
$image_link = [
'#type' => 'link',
'#title' => $link_title,
'#url' => Url::fromUri($link_href),
'#options' => array(
'attributes' => array('class' => array('feed-image')),
),
);
'#options' => [
'attributes' => ['class' => ['feed-image']],
],
];
}
$build[$id]['image'] = $image_link;
}
if ($display->getComponent('feed_icon')) {
$build[$id]['feed_icon'] = array(
$build[$id]['feed_icon'] = [
'#theme' => 'feed_icon',
'#url' => $entity->getUrl(),
'#title' => t('@title feed', array('@title' => $entity->label())),
);
'#title' => t('@title feed', ['@title' => $entity->label()]),
];
}
if ($display->getComponent('more_link')) {
$title_stripped = strip_tags($entity->label());
$build[$id]['more_link'] = array(
$build[$id]['more_link'] = [
'#type' => 'link',
'#title' => t('More<span class="visually-hidden"> posts about @title</span>', array(
'#title' => t('More<span class="visually-hidden"> posts about @title</span>', [
'@title' => $title_stripped,
)),
]),
'#url' => Url::fromRoute('entity.aggregator_feed.canonical', ['aggregator_feed' => $entity->id()]),
'#options' => array(
'attributes' => array(
'#options' => [
'attributes' => [
'title' => $title_stripped,
),
),
);
],
],
];
}
}
@@ -28,9 +28,9 @@ class FeedDeleteForm extends ContentEntityDeleteForm {
* {@inheritdoc}
*/
protected function getDeletionMessage() {
return $this->t('The feed %label has been deleted.', array(
return $this->t('The feed %label has been deleted.', [
'%label' => $this->entity->label(),
));
]);
}
}
@@ -15,7 +15,7 @@ class FeedItemsDeleteForm extends ContentEntityConfirmFormBase {
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to delete all items from the feed %feed?', array('%feed' => $this->entity->label()));
return $this->t('Are you sure you want to delete all items from the feed %feed?', ['%feed' => $this->entity->label()]);
}
/**
@@ -63,33 +63,33 @@ class OpmlFeedAdd extends FormBase {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$intervals = array(900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200);
$period = array_map(array(\Drupal::service('date.formatter'), 'formatInterval'), array_combine($intervals, $intervals));
$intervals = [900, 1800, 3600, 7200, 10800, 21600, 32400, 43200, 64800, 86400, 172800, 259200, 604800, 1209600, 2419200];
$period = array_map([\Drupal::service('date.formatter'), 'formatInterval'], array_combine($intervals, $intervals));
$form['upload'] = array(
$form['upload'] = [
'#type' => 'file',
'#title' => $this->t('OPML File'),
'#description' => $this->t('Upload an OPML file containing a list of feeds to be imported.'),
);
$form['remote'] = array(
];
$form['remote'] = [
'#type' => 'url',
'#title' => $this->t('OPML Remote URL'),
'#maxlength' => 1024,
'#description' => $this->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(
];
$form['refresh'] = [
'#type' => 'select',
'#title' => $this->t('Update interval'),
'#default_value' => 3600,
'#options' => $period,
'#description' => $this->t('The length of time between feed updates. Requires a correctly configured <a href=":cron">cron maintenance task</a>.', array(':cron' => $this->url('system.status'))),
);
'#description' => $this->t('The length of time between feed updates. Requires a correctly configured <a href=":cron">cron maintenance task</a>.', [':cron' => $this->url('system.status')]),
];
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
$form['actions'] = ['#type' => 'actions'];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Import'),
);
];
return $form;
}
@@ -109,7 +109,7 @@ class OpmlFeedAdd extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$validators = array('file_validate_extensions' => array('opml xml'));
$validators = ['file_validate_extensions' => ['opml xml']];
if ($file = file_save_upload('upload', $validators, FALSE, 0)) {
$data = file_get_contents($file->getFileUri());
}
@@ -120,8 +120,8 @@ class OpmlFeedAdd extends FormBase {
$data = (string) $response->getBody();
}
catch (RequestException $e) {
$this->logger('aggregator')->warning('Failed to download OPML file due to "%error".', array('%error' => $e->getMessage()));
drupal_set_message($this->t('Failed to download OPML file due to "%error".', array('%error' => $e->getMessage())));
$this->logger('aggregator')->warning('Failed to download OPML file due to "%error".', ['%error' => $e->getMessage()]);
drupal_set_message($this->t('Failed to download OPML file due to "%error".', ['%error' => $e->getMessage()]));
return;
}
}
@@ -136,7 +136,7 @@ class OpmlFeedAdd extends FormBase {
foreach ($feeds as $feed) {
// Ensure URL is valid.
if (!UrlHelper::isValid($feed['url'], TRUE)) {
drupal_set_message($this->t('The URL %url is invalid.', array('%url' => $feed['url'])), 'warning');
drupal_set_message($this->t('The URL %url is invalid.', ['%url' => $feed['url']]), 'warning');
continue;
}
@@ -151,20 +151,20 @@ class OpmlFeedAdd extends FormBase {
$result = $this->feedStorage->loadMultiple($ids);
foreach ($result as $old) {
if (strcasecmp($old->label(), $feed['title']) == 0) {
drupal_set_message($this->t('A feed named %title already exists.', array('%title' => $old->label())), 'warning');
drupal_set_message($this->t('A feed named %title already exists.', ['%title' => $old->label()]), 'warning');
continue 2;
}
if (strcasecmp($old->getUrl(), $feed['url']) == 0) {
drupal_set_message($this->t('A feed with the URL %url already exists.', array('%url' => $old->getUrl())), 'warning');
drupal_set_message($this->t('A feed with the URL %url already exists.', ['%url' => $old->getUrl()]), 'warning');
continue 2;
}
}
$new_feed = $this->feedStorage->create(array(
$new_feed = $this->feedStorage->create([
'title' => $feed['title'],
'url' => $feed['url'],
'refresh' => $form_state->getValue('refresh'),
));
]);
$new_feed->save();
}
@@ -189,14 +189,15 @@ class OpmlFeedAdd extends FormBase {
* @todo Move this to a parser in https://www.drupal.org/node/1963540.
*/
protected function parseOpml($opml) {
$feeds = array();
$xml_parser = drupal_xml_parser_create($opml);
$feeds = [];
$xml_parser = xml_parser_create();
xml_parser_set_option($xml_parser, XML_OPTION_TARGET_ENCODING, 'utf-8');
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']);
$feeds[] = ['title' => $item['TEXT'], 'url' => $item['XMLURL']];
}
}
}
@@ -21,25 +21,25 @@ class SettingsForm extends ConfigFormBase {
*
* @var \Drupal\aggregator\Plugin\AggregatorPluginManager[]
*/
protected $managers = array();
protected $managers = [];
/**
* The instantiated plugin instances that have configuration forms.
*
* @var \Drupal\Core\Plugin\PluginFormInterface[]
*/
protected $configurableInstances = array();
protected $configurableInstances = [];
/**
* The aggregator plugin definitions.
*
* @var array
*/
protected $definitions = array(
'fetcher' => array(),
'parser' => array(),
'processor' => array(),
);
protected $definitions = [
'fetcher' => [],
'parser' => [],
'processor' => [],
];
/**
* Constructs a \Drupal\aggregator\SettingsForm object.
@@ -58,15 +58,15 @@ class SettingsForm extends ConfigFormBase {
public function __construct(ConfigFactoryInterface $config_factory, AggregatorPluginManager $fetcher_manager, AggregatorPluginManager $parser_manager, AggregatorPluginManager $processor_manager, TranslationInterface $string_translation) {
parent::__construct($config_factory);
$this->stringTranslation = $string_translation;
$this->managers = array(
$this->managers = [
'fetcher' => $fetcher_manager,
'parser' => $parser_manager,
'processor' => $processor_manager,
);
];
// Get all available fetcher, parser and processor definitions.
foreach (array('fetcher', 'parser', 'processor') as $type) {
foreach (['fetcher', 'parser', 'processor'] as $type) {
foreach ($this->managers[$type]->getDefinitions() as $id => $definition) {
$this->definitions[$type][$id] = SafeMarkup::format('@title <span class="description">@description</span>', array('@title' => $definition['title'], '@description' => $definition['description']));
$this->definitions[$type][$id] = SafeMarkup::format('@title <span class="description">@description</span>', ['@title' => $definition['title'], '@description' => $definition['description']]);
}
}
}
@@ -105,56 +105,56 @@ class SettingsForm extends ConfigFormBase {
$config = $this->config('aggregator.settings');
// Global aggregator settings.
$form['aggregator_allowed_html_tags'] = array(
$form['aggregator_allowed_html_tags'] = [
'#type' => 'textfield',
'#title' => $this->t('Allowed HTML tags'),
'#size' => 80,
'#maxlength' => 255,
'#default_value' => $config->get('items.allowed_html'),
'#description' => $this->t('A space-separated list of HTML tags allowed in the content of feed items. Disallowed tags are stripped from the content.'),
);
];
// Only show basic configuration if there are actually options.
$basic_conf = array();
$basic_conf = [];
if (count($this->definitions['fetcher']) > 1) {
$basic_conf['aggregator_fetcher'] = array(
$basic_conf['aggregator_fetcher'] = [
'#type' => 'radios',
'#title' => $this->t('Fetcher'),
'#description' => $this->t('Fetchers download data from an external source. Choose a fetcher suitable for the external source you would like to download from.'),
'#options' => $this->definitions['fetcher'],
'#default_value' => $config->get('fetcher'),
);
];
}
if (count($this->definitions['parser']) > 1) {
$basic_conf['aggregator_parser'] = array(
$basic_conf['aggregator_parser'] = [
'#type' => 'radios',
'#title' => $this->t('Parser'),
'#description' => $this->t('Parsers transform downloaded data into standard structures. Choose a parser suitable for the type of feeds you would like to aggregate.'),
'#options' => $this->definitions['parser'],
'#default_value' => $config->get('parser'),
);
];
}
if (count($this->definitions['processor']) > 1) {
$basic_conf['aggregator_processors'] = array(
$basic_conf['aggregator_processors'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Processors'),
'#description' => $this->t('Processors act on parsed feed data, for example they store feed items. Choose the processors suitable for your task.'),
'#options' => $this->definitions['processor'],
'#default_value' => $config->get('processors'),
);
];
}
if (count($basic_conf)) {
$form['basic_conf'] = array(
$form['basic_conf'] = [
'#type' => 'details',
'#title' => $this->t('Basic configuration'),
'#description' => $this->t('For most aggregation tasks, the default settings are fine.'),
'#open' => TRUE,
);
];
$form['basic_conf'] += $basic_conf;
}
// Call buildConfigurationForm() on the active fetcher and parser.
foreach (array('fetcher', 'parser') as $type) {
foreach (['fetcher', 'parser'] as $type) {
$active = $config->get($type);
if (array_key_exists($active, $this->definitions[$type])) {
$instance = $this->managers[$type]->createInstance($active);
@@ -169,7 +169,7 @@ class SettingsForm extends ConfigFormBase {
}
// Implementing processor plugins will expect an array at $form['processors'].
$form['processors'] = array();
$form['processors'] = [];
// Call buildConfigurationForm() for each active processor.
foreach ($this->definitions['processor'] as $id => $definition) {
if (in_array($id, $config->get('processors'))) {
@@ -20,12 +20,12 @@ class ItemViewBuilder extends EntityViewBuilder {
$display = $displays[$bundle];
if ($display->getComponent('description')) {
$build[$id]['description'] = array(
$build[$id]['description'] = [
'#markup' => $entity->getDescription(),
'#allowed_tags' => _aggregator_allowed_tags(),
'#prefix' => '<div class="item-description">',
'#suffix' => '</div>',
);
];
}
}
}
@@ -95,7 +95,7 @@ class ItemsImporter implements ItemsImporterInterface {
}
// Store instances in an array so we dont have to instantiate new objects.
$processor_instances = array();
$processor_instances = [];
foreach ($this->config->get('processors') as $processor) {
try {
$processor_instances[$processor] = $this->processorManager->createInstance($processor);
@@ -124,10 +124,10 @@ class ItemsImporter implements ItemsImporterInterface {
// Log if feed URL has changed.
if ($feed->getUrl() != $feed_url) {
$this->logger->notice('Updated URL for feed %title to %url.', array('%title' => $feed->label(), '%url' => $feed->getUrl()));
$this->logger->notice('Updated URL for feed %title to %url.', ['%title' => $feed->label(), '%url' => $feed->getUrl()]);
}
$this->logger->notice('There is new syndicated content from %site.', array('%site' => $feed->label()));
$this->logger->notice('There is new syndicated content from %site.', ['%site' => $feed->label()]);
// If there are items on the feed, let enabled processors process them.
if (!empty($feed->items)) {
@@ -34,16 +34,16 @@ class AggregatorPluginManager extends DefaultPluginManager {
* The module handler.
*/
public function __construct($type, \Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
$type_annotations = array(
$type_annotations = [
'fetcher' => 'Drupal\aggregator\Annotation\AggregatorFetcher',
'parser' => 'Drupal\aggregator\Annotation\AggregatorParser',
'processor' => 'Drupal\aggregator\Annotation\AggregatorProcessor',
);
$plugin_interfaces = array(
];
$plugin_interfaces = [
'fetcher' => 'Drupal\aggregator\Plugin\FetcherInterface',
'parser' => 'Drupal\aggregator\Plugin\ParserInterface',
'processor' => 'Drupal\aggregator\Plugin\ProcessorInterface',
);
];
parent::__construct("Plugin/aggregator/$type", $namespaces, $module_handler, $plugin_interfaces[$type], $type_annotations[$type]);
$this->setCacheBackend($cache_backend, 'aggregator_' . $type . '_plugins');
@@ -25,7 +25,7 @@ abstract class AggregatorPluginSettingsBase extends PluginBase implements Plugin
* {@inheritdoc}
*/
public function defaultConfiguration() {
return array();
return [];
}
/**
@@ -38,7 +38,7 @@ abstract class AggregatorPluginSettingsBase extends PluginBase implements Plugin
* {@inheritdoc}
*/
public function calculateDependencies() {
return array();
return [];
}
}
@@ -7,7 +7,6 @@ use Drupal\aggregator\ItemStorageInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface;
@@ -38,13 +37,6 @@ class AggregatorFeedBlock extends BlockBase implements ContainerFactoryPluginInt
*/
protected $itemStorage;
/**
* The entity query object for feed items.
*
* @var \Drupal\Core\Entity\Query\QueryInterface
*/
protected $itemQuery;
/**
* Constructs an AggregatorFeedBlock object.
*
@@ -58,14 +50,11 @@ class AggregatorFeedBlock extends BlockBase implements ContainerFactoryPluginInt
* The entity storage for feeds.
* @param \Drupal\aggregator\ItemStorageInterface $item_storage
* The entity storage for feed items.
* @param \Drupal\Core\Entity\Query\QueryInterface $item_query
* The entity query object for feed items.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, FeedStorageInterface $feed_storage, ItemStorageInterface $item_storage, QueryInterface $item_query) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, FeedStorageInterface $feed_storage, ItemStorageInterface $item_storage) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->feedStorage = $feed_storage;
$this->itemStorage = $item_storage;
$this->itemQuery = $item_query;
}
@@ -77,9 +66,8 @@ class AggregatorFeedBlock extends BlockBase implements ContainerFactoryPluginInt
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity.manager')->getStorage('aggregator_feed'),
$container->get('entity.manager')->getStorage('aggregator_item'),
$container->get('entity.query')->get('aggregator_item')
$container->get('entity_type.manager')->getStorage('aggregator_feed'),
$container->get('entity_type.manager')->getStorage('aggregator_item')
);
}
@@ -89,10 +77,10 @@ class AggregatorFeedBlock extends BlockBase implements ContainerFactoryPluginInt
*/
public function defaultConfiguration() {
// By default, the block will contain 10 feed items.
return array(
return [
'block_count' => 10,
'feed' => NULL,
);
];
}
/**
@@ -108,23 +96,23 @@ class AggregatorFeedBlock extends BlockBase implements ContainerFactoryPluginInt
*/
public function blockForm($form, FormStateInterface $form_state) {
$feeds = $this->feedStorage->loadMultiple();
$options = array();
$options = [];
foreach ($feeds as $feed) {
$options[$feed->id()] = $feed->label();
}
$form['feed'] = array(
$form['feed'] = [
'#type' => 'select',
'#title' => $this->t('Select the feed that should be displayed'),
'#default_value' => $this->configuration['feed'],
'#options' => $options,
);
];
$range = range(2, 20);
$form['block_count'] = array(
$form['block_count'] = [
'#type' => 'select',
'#title' => $this->t('Number of news items in block'),
'#default_value' => $this->configuration['block_count'],
'#options' => array_combine($range, $range),
);
];
return $form;
}
@@ -142,7 +130,7 @@ class AggregatorFeedBlock extends BlockBase implements ContainerFactoryPluginInt
public function build() {
// Load the selected feed.
if ($feed = $this->feedStorage->load($this->configuration['feed'])) {
$result = $this->itemQuery
$result = $this->itemStorage->getQuery()
->condition('fid', $feed->id())
->range(0, $this->configuration['block_count'])
->sort('timestamp', 'DESC')
@@ -60,17 +60,17 @@ class AggregatorTitleFormatter extends FormatterBase {
}
foreach ($items as $delta => $item) {
if ($this->getSetting('display_as_link') && $url_string) {
$elements[$delta] = [
if ($this->getSetting('display_as_link') && $url_string) {
$elements[$delta] = [
'#type' => 'link',
'#title' => $item->value,
'#url' => Url::fromUri($url_string),
];
}
else {
$elements[$delta] = ['#markup' => $item->value];
}
];
}
else {
$elements[$delta] = ['#markup' => $item->value];
}
}
return $elements;
}
@@ -112,8 +112,8 @@ class DefaultFetcher implements FetcherInterface, ContainerFactoryPluginInterfac
return TRUE;
}
catch (RequestException $e) {
$this->logger->warning('The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->label(), '%error' => $e->getMessage()));
drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->label(), '%error' => $e->getMessage())), 'warning');
$this->logger->warning('The feed from %site seems to be broken because of error "%error".', ['%site' => $feed->label(), '%error' => $e->getMessage()]);
drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', ['%site' => $feed->label(), '%error' => $e->getMessage()]), 'warning');
return FALSE;
}
}
@@ -31,7 +31,7 @@ class DefaultParser implements ParserInterface {
}
catch (ExceptionInterface $e) {
watchdog_exception('aggregator', $e);
drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', array('%site' => $feed->label(), '%error' => $e->getMessage())), 'error');
drupal_set_message(t('The feed from %site seems to be broken because of error "%error".', ['%site' => $feed->label(), '%error' => $e->getMessage()]), 'error');
return FALSE;
}
@@ -42,10 +42,10 @@ class DefaultParser implements ParserInterface {
$feed->setImage($image['uri']);
}
// Initialize items array.
$feed->items = array();
$feed->items = [];
foreach ($channel as $item) {
// Reset the parsed item.
$parsed_item = array();
$parsed_item = [];
// Move the values to an array as expected by processors.
$parsed_item['title'] = $item->getTitle();
$parsed_item['guid'] = $item->getId();
@@ -10,7 +10,6 @@ use Drupal\aggregator\FeedInterface;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Entity\Query\QueryInterface;
use Drupal\Core\Form\ConfigFormBaseTrait;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -39,13 +38,6 @@ class DefaultProcessor extends AggregatorPluginSettingsBase implements Processor
*/
protected $configFactory;
/**
* The entity query object for feed items.
*
* @var \Drupal\Core\Entity\Query\QueryInterface
*/
protected $itemQuery;
/**
* The entity storage for items.
*
@@ -71,17 +63,14 @@ class DefaultProcessor extends AggregatorPluginSettingsBase implements Processor
* The plugin implementation definition.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
* The configuration factory object.
* @param \Drupal\Core\Entity\Query\QueryInterface $item_query
* The entity query object for feed items.
* @param \Drupal\aggregator\ItemStorageInterface $item_storage
* The entity storage for feed items.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $config, QueryInterface $item_query, ItemStorageInterface $item_storage, DateFormatterInterface $date_formatter) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $config, ItemStorageInterface $item_storage, DateFormatterInterface $date_formatter) {
$this->configFactory = $config;
$this->itemStorage = $item_storage;
$this->itemQuery = $item_query;
$this->dateFormatter = $date_formatter;
// @todo Refactor aggregator plugins to ConfigEntity so merging
// the configuration here is not needed.
@@ -97,8 +86,7 @@ class DefaultProcessor extends AggregatorPluginSettingsBase implements Processor
$plugin_id,
$plugin_definition,
$container->get('config.factory'),
$container->get('entity.query')->get('aggregator_item'),
$container->get('entity.manager')->getStorage('aggregator_item'),
$container->get('entity_type.manager')->getStorage('aggregator_item'),
$container->get('date.formatter')
);
}
@@ -117,53 +105,53 @@ class DefaultProcessor extends AggregatorPluginSettingsBase implements Processor
$config = $this->config('aggregator.settings');
$processors = $config->get('processors');
$info = $this->getPluginDefinition();
$counts = array(3, 5, 10, 15, 20, 25);
$counts = [3, 5, 10, 15, 20, 25];
$items = array_map(function ($count) {
return $this->formatPlural($count, '1 item', '@count items');
}, array_combine($counts, $counts));
$intervals = array(3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800);
$period = array_map(array($this->dateFormatter, 'formatInterval'), array_combine($intervals, $intervals));
$intervals = [3600, 10800, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800];
$period = array_map([$this->dateFormatter, 'formatInterval'], array_combine($intervals, $intervals));
$period[AGGREGATOR_CLEAR_NEVER] = t('Never');
$form['processors'][$info['id']] = array();
$form['processors'][$info['id']] = [];
// Only wrap into details if there is a basic configuration.
if (isset($form['basic_conf'])) {
$form['processors'][$info['id']] = array(
$form['processors'][$info['id']] = [
'#type' => 'details',
'#title' => t('Default processor settings'),
'#description' => $info['description'],
'#open' => in_array($info['id'], $processors),
);
];
}
$form['processors'][$info['id']]['aggregator_summary_items'] = array(
$form['processors'][$info['id']]['aggregator_summary_items'] = [
'#type' => 'select',
'#title' => t('Number of items shown in listing pages'),
'#default_value' => $config->get('source.list_max'),
'#empty_value' => 0,
'#options' => $items,
);
];
$form['processors'][$info['id']]['aggregator_clear'] = array(
$form['processors'][$info['id']]['aggregator_clear'] = [
'#type' => 'select',
'#title' => t('Discard items older than'),
'#default_value' => $config->get('items.expire'),
'#options' => $period,
'#description' => t('Requires a correctly configured <a href=":cron">cron maintenance task</a>.', array(':cron' => $this->url('system.status'))),
);
'#description' => t('Requires a correctly configured <a href=":cron">cron maintenance task</a>.', [':cron' => $this->url('system.status')]),
];
$lengths = array(0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000);
$lengths = [0, 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000];
$options = array_map(function($length) {
return ($length == 0) ? t('Unlimited') : $this->formatPlural($length, '1 character', '@count characters');
}, array_combine($lengths, $lengths));
$form['processors'][$info['id']]['aggregator_teaser_length'] = array(
$form['processors'][$info['id']]['aggregator_teaser_length'] = [
'#type' => 'select',
'#title' => t('Length of trimmed description'),
'#default_value' => $config->get('items.teaser_length'),
'#options' => $options,
'#description' => t('The maximum number of characters used in the trimmed version of content.'),
);
];
return $form;
}
@@ -197,13 +185,13 @@ class DefaultProcessor extends AggregatorPluginSettingsBase implements Processor
// 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'])) {
$values = array('fid' => $feed->id(), 'guid' => $item['guid']);
$values = ['fid' => $feed->id(), 'guid' => $item['guid']];
}
elseif ($item['link'] && $item['link'] != $feed->link && $item['link'] != $feed->url) {
$values = array('fid' => $feed->id(), 'link' => $item['link']);
$values = ['fid' => $feed->id(), 'link' => $item['link']];
}
else {
$values = array('fid' => $feed->id(), 'title' => $item['title']);
$values = ['fid' => $feed->id(), 'title' => $item['title']];
}
// Try to load an existing entry.
@@ -211,7 +199,7 @@ class DefaultProcessor extends AggregatorPluginSettingsBase implements Processor
$entry = reset($entry);
}
else {
$entry = Item::create(array('langcode' => $feed->language()->getId()));
$entry = Item::create(['langcode' => $feed->language()->getId()]);
}
if ($item['timestamp']) {
$entry->setPostedTime($item['timestamp']);
@@ -243,7 +231,7 @@ class DefaultProcessor extends AggregatorPluginSettingsBase implements Processor
$this->itemStorage->delete($items);
}
// @todo This should be moved out to caller with a different message maybe.
drupal_set_message(t('The news items from %site have been deleted.', array('%site' => $feed->label())));
drupal_set_message(t('The news items from %site have been deleted.', ['%site' => $feed->label()]));
}
/**
@@ -257,7 +245,7 @@ class DefaultProcessor extends AggregatorPluginSettingsBase implements Processor
if ($aggregator_clear != AGGREGATOR_CLEAR_NEVER) {
// Delete all items that are older than flush item timer.
$age = REQUEST_TIME - $aggregator_clear;
$result = $this->itemQuery
$result = $this->itemStorage->getQuery()
->condition('fid', $feed->id())
->condition('timestamp', $age, '<')
->execute();
@@ -26,7 +26,7 @@ class AggregatorFeed extends DrupalSqlBase {
* {@inheritdoc}
*/
public function fields() {
$fields = array(
$fields = [
'fid' => $this->t('The feed ID.'),
'title' => $this->t('Title of the feed.'),
'url' => $this->t('URL to the feed.'),
@@ -38,7 +38,7 @@ class AggregatorFeed extends DrupalSqlBase {
'etag' => $this->t('Entity tag HTTP response header.'),
'modified' => $this->t('When the feed was last modified.'),
'block' => $this->t("Number of items to display in the feed's block."),
);
];
if ($this->getModuleSchemaVersion('system') >= 7000) {
$fields['queued'] = $this->t('Time when this feed was queued for refresh, 0 if not queued.');
}
@@ -27,7 +27,7 @@ class AggregatorItem extends DrupalSqlBase {
* {@inheritdoc}
*/
public function fields() {
return array(
return [
'iid' => $this->t('Primary Key: Unique ID for feed item.'),
'fid' => $this->t('The {aggregator_feed}.fid to which this item belongs.'),
'title' => $this->t('Title of the feed item.'),
@@ -36,7 +36,7 @@ class AggregatorItem extends DrupalSqlBase {
'description' => $this->t('Body of the feed item.'),
'timestamp' => $this->t('Post date of feed item, as a Unix timestamp.'),
'guid' => $this->t('Unique identifier for the feed item.'),
);
];
}
/**
@@ -50,7 +50,7 @@ class Fid extends NumericArgument {
* {@inheritdoc}
*/
public function titleQuery() {
$titles = array();
$titles = [];
$feeds = $this->entityManager->getStorage('aggregator_feed')->loadMultiple($this->value);
foreach ($feeds as $feed) {
@@ -50,7 +50,7 @@ class Iid extends NumericArgument {
* {@inheritdoc}
*/
public function titleQuery() {
$titles = array();
$titles = [];
$items = $this->entityManager->getStorage('aggregator_item')->loadMultiple($this->value);
foreach ($items as $feed) {
@@ -48,31 +48,31 @@ class Rss extends RssPluginBase {
$item->{$name} = $field->value;
}
$item->elements = array(
array(
$item->elements = [
[
'key' => 'pubDate',
// views_view_row_rss takes care about the escaping.
'value' => gmdate('r', $entity->timestamp->value),
),
array(
],
[
'key' => 'dc:creator',
// views_view_row_rss takes care about the escaping.
'value' => $entity->author->value,
),
array(
],
[
'key' => 'guid',
// views_view_row_rss takes care about the escaping.
'value' => $entity->guid->value,
'attributes' => array('isPermaLink' => 'false'),
),
);
'attributes' => ['isPermaLink' => 'false'],
],
];
$build = array(
$build = [
'#theme' => $this->themeFunctions(),
'#view' => $this->view,
'#options' => $this->options,
'#row' => $item,
);
];
return $build;
}
@@ -9,6 +9,9 @@ use Drupal\aggregator\FeedInterface;
/**
* Defines a base class for testing the Aggregator module.
*
* @deprecated Scheduled for removal in Drupal 9.0.0.
* Use \Drupal\Tests\aggregator\Functional\AggregatorTestBase instead.
*/
abstract class AggregatorTestBase extends WebTestBase {
@@ -34,10 +37,10 @@ abstract class AggregatorTestBase extends WebTestBase {
// Create an Article node type.
if ($this->profile != 'standard') {
$this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
}
$this->adminUser = $this->drupalCreateUser(array('access administration pages', 'administer news feeds', 'access news feeds', 'create article content'));
$this->adminUser = $this->drupalCreateUser(['access administration pages', 'administer news feeds', 'access news feeds', 'create article content']);
$this->drupalLogin($this->adminUser);
$this->drupalPlaceBlock('local_tasks_block');
}
@@ -58,12 +61,16 @@ abstract class AggregatorTestBase extends WebTestBase {
*
* @see getFeedEditArray()
*/
public function createFeed($feed_url = NULL, array $edit = array()) {
public function createFeed($feed_url = NULL, array $edit = []) {
$edit = $this->getFeedEditArray($feed_url, $edit);
$this->drupalPostForm('aggregator/sources/add', $edit, t('Save'));
$this->assertRaw(t('The feed %name has been added.', array('%name' => $edit['title[0][value]'])), format_string('The feed @name has been added.', array('@name' => $edit['title[0][value]'])));
$this->assertText(t('The feed @name has been added.', ['@name' => $edit['title[0][value]']]), format_string('The feed @name has been added.', ['@name' => $edit['title[0][value]']]));
$fid = db_query("SELECT fid FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $edit['title[0][value]'], ':url' => $edit['url[0][value]']))->fetchField();
// Verify that the creation message contains a link to a feed.
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', [':href' => 'aggregator/sources/']);
$this->assert(isset($view_link), 'The message area contains a link to a feed');
$fid = db_query("SELECT fid FROM {aggregator_feed} WHERE title = :title AND url = :url", [':title' => $edit['title[0][value]'], ':url' => $edit['url[0][value]']])->fetchField();
$this->assertTrue(!empty($fid), 'The feed found in database.');
return Feed::load($fid);
}
@@ -75,8 +82,8 @@ abstract class AggregatorTestBase extends WebTestBase {
* Feed object representing the feed.
*/
public function deleteFeed(FeedInterface $feed) {
$this->drupalPostForm('aggregator/sources/' . $feed->id() . '/delete', array(), t('Delete'));
$this->assertRaw(t('The feed %title has been deleted.', array('%title' => $feed->label())), 'Feed deleted successfully.');
$this->drupalPostForm('aggregator/sources/' . $feed->id() . '/delete', [], t('Delete'));
$this->assertRaw(t('The feed %title has been deleted.', ['%title' => $feed->label()]), 'Feed deleted successfully.');
}
/**
@@ -91,19 +98,19 @@ abstract class AggregatorTestBase extends WebTestBase {
* @return array
* A feed array.
*/
public function getFeedEditArray($feed_url = NULL, array $edit = array()) {
public function getFeedEditArray($feed_url = NULL, array $edit = []) {
$feed_name = $this->randomMachineName(10);
if (!$feed_url) {
$feed_url = \Drupal::url('view.frontpage.feed_1', array(), array(
'query' => array('feed' => $feed_name),
$feed_url = \Drupal::url('view.frontpage.feed_1', [], [
'query' => ['feed' => $feed_name],
'absolute' => TRUE,
));
]);
}
$edit += array(
$edit += [
'title[0][value]' => $feed_name,
'url[0][value]' => $feed_url,
'refresh' => '900',
);
];
return $edit;
}
@@ -119,19 +126,19 @@ abstract class AggregatorTestBase extends WebTestBase {
* @return \Drupal\aggregator\FeedInterface
* A feed object.
*/
public function getFeedEditObject($feed_url = NULL, array $values = array()) {
public function getFeedEditObject($feed_url = NULL, array $values = []) {
$feed_name = $this->randomMachineName(10);
if (!$feed_url) {
$feed_url = \Drupal::url('view.frontpage.feed_1', array(
'query' => array('feed' => $feed_name),
$feed_url = \Drupal::url('view.frontpage.feed_1', [
'query' => ['feed' => $feed_name],
'absolute' => TRUE,
));
]);
}
$values += array(
$values += [
'title' => $feed_name,
'url' => $feed_url,
'refresh' => '900',
);
];
return Feed::create($values);
}
@@ -161,7 +168,7 @@ abstract class AggregatorTestBase extends WebTestBase {
public function updateFeedItems(FeedInterface $feed, $expected_count = NULL) {
// First, let's ensure we can get to the rss xml.
$this->drupalGet($feed->getUrl());
$this->assertResponse(200, format_string(':url is reachable.', array(':url' => $feed->getUrl())));
$this->assertResponse(200, format_string(':url is reachable.', [':url' => $feed->getUrl()]));
// Attempt to access the update link directly without an access token.
$this->drupalGet('admin/config/services/aggregator/update/' . $feed->id());
@@ -172,15 +179,15 @@ abstract class AggregatorTestBase extends WebTestBase {
$this->clickLink('Update items');
// Ensure we have the right number of items.
$result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->id()));
$feed->items = array();
$result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()]);
$feed->items = [];
foreach ($result as $item) {
$feed->items[] = $item->iid;
}
if ($expected_count !== NULL) {
$feed->item_count = count($feed->items);
$this->assertEqual($expected_count, $feed->item_count, format_string('Total items in feed equal to the total items in database (@val1 != @val2)', array('@val1' => $expected_count, '@val2' => $feed->item_count)));
$this->assertEqual($expected_count, $feed->item_count, format_string('Total items in feed equal to the total items in database (@val1 != @val2)', ['@val1' => $expected_count, '@val2' => $feed->item_count]));
}
}
@@ -191,8 +198,8 @@ abstract class AggregatorTestBase extends WebTestBase {
* Feed object representing the feed.
*/
public function deleteFeedItems(FeedInterface $feed) {
$this->drupalPostForm('admin/config/services/aggregator/delete/' . $feed->id(), array(), t('Delete items'));
$this->assertRaw(t('The news items from %title have been deleted.', array('%title' => $feed->label())), 'Feed items deleted.');
$this->drupalPostForm('admin/config/services/aggregator/delete/' . $feed->id(), [], t('Delete items'));
$this->assertRaw(t('The news items from %title have been deleted.', ['%title' => $feed->label()]), 'Feed items deleted.');
}
/**
@@ -205,10 +212,10 @@ abstract class AggregatorTestBase extends WebTestBase {
*/
public function updateAndDelete(FeedInterface $feed, $expected_count) {
$this->updateFeedItems($feed, $expected_count);
$count = db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->id()))->fetchField();
$count = db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField();
$this->assertTrue($count);
$this->deleteFeedItems($feed);
$count = db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', array(':fid' => $feed->id()))->fetchField();
$count = db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField();
$this->assertTrue($count == 0);
}
@@ -224,7 +231,7 @@ abstract class AggregatorTestBase extends WebTestBase {
* TRUE if feed is unique.
*/
public function uniqueFeed($feed_name, $feed_url) {
$result = db_query("SELECT COUNT(*) FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $feed_name, ':url' => $feed_url))->fetchField();
$result = db_query("SELECT COUNT(*) FROM {aggregator_feed} WHERE title = :title AND url = :url", [':title' => $feed_name, ':url' => $feed_url])->fetchField();
return (1 == $result);
}
@@ -267,7 +274,8 @@ abstract class AggregatorTestBase extends WebTestBase {
EOF;
$path = 'public://valid-opml.xml';
return file_unmanaged_save_data($opml, $path);
// Add the UTF-8 byte order mark.
return file_unmanaged_save_data(chr(239) . chr(187) . chr(191) . $opml, $path);
}
/**
@@ -350,7 +358,7 @@ EOF;
public function createSampleNodes($count = 5) {
// Post $count article nodes.
for ($i = 0; $i < $count; $i++) {
$edit = array();
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
$edit['body[0][value]'] = $this->randomMachineName();
$this->drupalPostForm('node/add/article', $edit, t('Save'));
@@ -364,10 +372,10 @@ EOF;
$this->config('aggregator.settings')
->set('fetcher', 'aggregator_test_fetcher')
->set('parser', 'aggregator_test_parser')
->set('processors', array(
->set('processors', [
'aggregator_test_processor' => 'aggregator_test_processor',
'aggregator' => 'aggregator',
))
])
->save();
}
@@ -0,0 +1,41 @@
<?php
namespace Drupal\aggregator\Tests\Update;
use Drupal\system\Tests\Update\UpdatePathTestBase;
/**
* Tests that node settings are properly updated during database updates.
*
* @group aggregator
*/
class AggregatorUpdateTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected function setDatabaseDumpFiles() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8.filled.standard.php.gz',
];
}
/**
* Tests that the 'Source feed' field is required.
*
* @see aggregator_update_8200()
*/
public function testSourceFeedRequired() {
// Check that the 'fid' field is not required prior to the update.
$field_definition = \Drupal::entityDefinitionUpdateManager()->getFieldStorageDefinition('fid', 'aggregator_item');
$this->assertFalse($field_definition->isRequired());
// Run updates.
$this->runUpdates();
// Check that the 'fid' field is now required.
$field_definition = \Drupal::entityDefinitionUpdateManager()->getFieldStorageDefinition('fid', 'aggregator_item');
$this->assertTrue($field_definition->isRequired());
}
}
@@ -5,8 +5,8 @@ package: Testing
# version: VERSION
# core: 8.x
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233792
datestamp: 1502903957
@@ -75,20 +75,20 @@ class TestProcessor extends AggregatorPluginSettingsBase implements ProcessorInt
$processors = $this->config('aggregator.settings')->get('processors');
$info = $this->getPluginDefinition();
$form['processors'][$info['id']] = array(
$form['processors'][$info['id']] = [
'#type' => 'details',
'#title' => t('Test processor settings'),
'#description' => $info['description'],
'#open' => in_array($info['id'], $processors),
);
];
// Add some dummy settings to verify settingsForm is called.
$form['processors'][$info['id']]['dummy_length'] = array(
$form['processors'][$info['id']]['dummy_length'] = [
'#title' => t('Dummy length setting'),
'#type' => 'number',
'#min' => 1,
'#max' => 1000,
'#default_value' => $this->configuration['items']['dummy_length'],
);
];
return $form;
}
@@ -8,8 +8,8 @@ dependencies:
- aggregator
- views
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233792
datestamp: 1502903957
@@ -0,0 +1,95 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
/**
* Add feed test.
*
* @group aggregator
*/
class AddFeedTest extends AggregatorTestBase {
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('page_title_block');
}
/**
* Creates and ensures that a feed is unique, checks source, and deletes feed.
*/
public function testAddFeed() {
$feed = $this->createFeed();
$feed->refreshItems();
// Check feed data.
$this->assertUrl(\Drupal::url('aggregator.feed_add', [], ['absolute' => TRUE]), [], 'Directed to correct URL.');
$this->assertTrue($this->uniqueFeed($feed->label(), $feed->getUrl()), 'The feed is unique.');
// Check feed source.
$this->drupalGet('aggregator/sources/' . $feed->id());
$this->assertResponse(200, 'Feed source exists.');
$this->assertText($feed->label(), 'Page title');
$this->assertRaw($feed->getWebsiteUrl());
// Try to add a duplicate.
$edit = [
'title[0][value]' => $feed->label(),
'url[0][value]' => $feed->getUrl(),
'refresh' => '900',
];
$this->drupalPostForm('aggregator/sources/add', $edit, t('Save'));
$this->assertRaw(t('A feed named %feed already exists. Enter a unique title.', ['%feed' => $feed->label()]));
$this->assertRaw(t('A feed with this URL %url already exists. Enter a unique URL.', ['%url' => $feed->getUrl()]));
// Delete feed.
$this->deleteFeed($feed);
}
/**
* Ensures that the feed label is escaping when rendering the feed icon.
*/
public function testFeedLabelEscaping() {
$feed = $this->createFeed(NULL, ['title[0][value]' => 'Test feed title <script>alert(123);</script>']);
$this->checkForMetaRefresh();
$this->drupalGet('aggregator/sources/' . $feed->id());
$this->assertResponse(200);
$this->assertEscaped('Test feed title <script>alert(123);</script>');
$this->assertNoRaw('Test feed title <script>alert(123);</script>');
// Ensure the feed icon title is escaped.
$this->assertTrue(strpos(str_replace(["\n", "\r"], '', $this->getRawContent()), 'class="feed-icon"> Subscribe to Test feed title &lt;script&gt;alert(123);&lt;/script&gt; feed</a>') !== FALSE);
}
/**
* Tests feeds with very long URLs.
*/
public function testAddLongFeed() {
// Create a feed with a URL of > 255 characters.
$long_url = "https://www.google.com/search?ix=heb&sourceid=chrome&ie=UTF-8&q=angie+byron#sclient=psy-ab&hl=en&safe=off&source=hp&q=angie+byron&pbx=1&oq=angie+byron&aq=f&aqi=&aql=&gs_sm=3&gs_upl=0l0l0l10534l0l0l0l0l0l0l0l0ll0l0&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&fp=a70b6b1f0abe28d8&biw=1629&bih=889&ix=heb";
$feed = $this->createFeed($long_url);
$feed->refreshItems();
// Create a second feed of > 255 characters, where the only difference is
// after the 255th character.
$long_url_2 = "https://www.google.com/search?ix=heb&sourceid=chrome&ie=UTF-8&q=angie+byron#sclient=psy-ab&hl=en&safe=off&source=hp&q=angie+byron&pbx=1&oq=angie+byron&aq=f&aqi=&aql=&gs_sm=3&gs_upl=0l0l0l10534l0l0l0l0l0l0l0l0ll0l0&bav=on.2,or.r_gc.r_pw.r_cp.,cf.osb&fp=a70b6b1f0abe28d8&biw=1629&bih=889";
$feed_2 = $this->createFeed($long_url_2);
$feed->refreshItems();
// Check feed data.
$this->assertTrue($this->uniqueFeed($feed->label(), $feed->getUrl()), 'The first long URL feed is unique.');
$this->assertTrue($this->uniqueFeed($feed_2->label(), $feed_2->getUrl()), 'The second long URL feed is unique.');
// Check feed source.
$this->drupalGet('aggregator/sources/' . $feed->id());
$this->assertResponse(200, 'Long URL feed source exists.');
$this->assertText($feed->label(), 'Page title');
// Delete feeds.
$this->deleteFeed($feed);
$this->deleteFeed($feed_2);
}
}
@@ -0,0 +1,88 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
/**
* Tests aggregator admin pages.
*
* @group aggregator
*/
class AggregatorAdminTest extends AggregatorTestBase {
/**
* Tests the settings form to ensure the correct default values are used.
*/
public function testSettingsPage() {
$this->drupalGet('admin/config');
$this->clickLink('Aggregator');
$this->clickLink('Settings');
// Make sure that test plugins are present.
$this->assertText('Test fetcher');
$this->assertText('Test parser');
$this->assertText('Test processor');
// Set new values and enable test plugins.
$edit = [
'aggregator_allowed_html_tags' => '<a>',
'aggregator_summary_items' => 10,
'aggregator_clear' => 3600,
'aggregator_teaser_length' => 200,
'aggregator_fetcher' => 'aggregator_test_fetcher',
'aggregator_parser' => 'aggregator_test_parser',
'aggregator_processors[aggregator_test_processor]' => 'aggregator_test_processor',
];
$this->drupalPostForm('admin/config/services/aggregator/settings', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'));
foreach ($edit as $name => $value) {
$this->assertFieldByName($name, $value, format_string('"@name" has correct default value.', ['@name' => $name]));
}
// Check for our test processor settings form.
$this->assertText(t('Dummy length setting'));
// Change its value to ensure that settingsSubmit is called.
$edit = [
'dummy_length' => 100,
];
$this->drupalPostForm('admin/config/services/aggregator/settings', $edit, t('Save configuration'));
$this->assertText(t('The configuration options have been saved.'));
$this->assertFieldByName('dummy_length', 100, '"dummy_length" has correct default value.');
// Make sure settings form is still accessible even after uninstalling a module
// that provides the selected plugins.
$this->container->get('module_installer')->uninstall(['aggregator_test']);
$this->resetAll();
$this->drupalGet('admin/config/services/aggregator/settings');
$this->assertResponse(200);
}
/**
* Tests the overview page.
*/
public function testOverviewPage() {
$feed = $this->createFeed($this->getRSS091Sample());
$this->drupalGet('admin/config/services/aggregator');
$result = $this->xpath('//table/tbody/tr');
// Check if the amount of feeds in the overview matches the amount created.
$this->assertEqual(1, count($result), 'Created feed is found in the overview');
// Check if the fields in the table match with what's expected.
$link = $this->xpath('//table/tbody/tr//td[1]/a');
$this->assertEquals($feed->label(), $link[0]->getText());
$count = $this->container->get('entity.manager')->getStorage('aggregator_item')->getItemCount($feed);
$td = $this->xpath('//table/tbody/tr//td[2]');
$this->assertEquals(\Drupal::translation()->formatPlural($count, '1 item', '@count items'), $td[0]->getText());
// Update the items of the first feed.
$feed->refreshItems();
$this->drupalGet('admin/config/services/aggregator');
$result = $this->xpath('//table/tbody/tr');
// Check if the fields in the table match with what's expected.
$link = $this->xpath('//table/tbody/tr//td[1]/a');
$this->assertEquals($feed->label(), $link[0]->getText());
$count = $this->container->get('entity.manager')->getStorage('aggregator_item')->getItemCount($feed);
$td = $this->xpath('//table/tbody/tr//td[2]');
$this->assertEquals(\Drupal::translation()->formatPlural($count, '1 item', '@count items'), $td[0]->getText());
}
}
@@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
use Drupal\Tests\Traits\Core\CronRunTrait;
/**
* Update feeds on cron.
*
* @group aggregator
*/
class AggregatorCronTest extends AggregatorTestBase {
use CronRunTrait;
/**
* Adds feeds and updates them via cron process.
*/
public function testCron() {
// Create feed and test basic updating on cron.
$this->createSampleNodes();
$feed = $this->createFeed();
$this->cronRun();
$this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField());
$this->deleteFeedItems($feed);
$this->assertEqual(0, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField());
$this->cronRun();
$this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField());
// Test feed locking when queued for update.
$this->deleteFeedItems($feed);
db_update('aggregator_feed')
->condition('fid', $feed->id())
->fields([
'queued' => REQUEST_TIME,
])
->execute();
$this->cronRun();
$this->assertEqual(0, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField());
db_update('aggregator_feed')
->condition('fid', $feed->id())
->fields([
'queued' => 0,
])
->execute();
$this->cronRun();
$this->assertEqual(5, db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField());
}
}
@@ -0,0 +1,154 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\views\Entity\View;
/**
* Tests display of aggregator items on the page.
*
* @group aggregator
*/
class AggregatorRenderingTest extends AggregatorTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'test_page_test'];
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('page_title_block');
}
/**
* Adds a feed block to the page and checks its links.
*/
public function testBlockLinks() {
// Create feed.
$this->createSampleNodes();
$feed = $this->createFeed();
$this->updateFeedItems($feed, $this->getDefaultFeedItemCount());
// Need admin user to be able to access block admin.
$admin_user = $this->drupalCreateUser([
'administer blocks',
'access administration pages',
'administer news feeds',
'access news feeds',
]);
$this->drupalLogin($admin_user);
$block = $this->drupalPlaceBlock("aggregator_feed_block", ['label' => 'feed-' . $feed->label()]);
// Configure the feed that should be displayed.
$block->getPlugin()->setConfigurationValue('feed', $feed->id());
$block->getPlugin()->setConfigurationValue('block_count', 2);
$block->save();
// Confirm that the block is now being displayed on pages.
$this->drupalGet('test-page');
$this->assertText($block->label(), 'Feed block is displayed on the page.');
// Confirm items appear as links.
$items = $this->container->get('entity.manager')->getStorage('aggregator_item')->loadByFeed($feed->id(), 1);
$links = $this->xpath('//a[@href = :href]', [':href' => reset($items)->getLink()]);
$this->assert(isset($links[0]), 'Item link found.');
// Find the expected read_more link.
$href = $feed->url();
$links = $this->xpath('//a[@href = :href]', [':href' => $href]);
$this->assert(isset($links[0]), format_string('Link to href %href found.', ['%href' => $href]));
$cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
$cache_tags = explode(' ', $cache_tags_header);
$this->assertTrue(in_array('aggregator_feed:' . $feed->id(), $cache_tags));
// Visit that page.
$this->drupalGet($feed->urlInfo()->getInternalPath());
$correct_titles = $this->xpath('//h1[normalize-space(text())=:title]', [':title' => $feed->label()]);
$this->assertFalse(empty($correct_titles), 'Aggregator feed page is available and has the correct title.');
$cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
$this->assertTrue(in_array('aggregator_feed:' . $feed->id(), $cache_tags));
$this->assertTrue(in_array('aggregator_feed_view', $cache_tags));
$this->assertTrue(in_array('aggregator_item_view', $cache_tags));
// Set the number of news items to 0 to test that the block does not show
// up.
$block->getPlugin()->setConfigurationValue('block_count', 0);
$block->save();
// Check that the block is no longer displayed.
$this->drupalGet('test-page');
$this->assertNoText($block->label(), 'Feed block is not displayed on the page when number of items is set to 0.');
}
/**
* Creates a feed and checks that feed's page.
*/
public function testFeedPage() {
// Increase the number of items published in the rss.xml feed so we have
// enough articles to test paging.
$view = View::load('frontpage');
$display = &$view->getDisplay('feed_1');
$display['display_options']['pager']['options']['items_per_page'] = 30;
$view->save();
// Create a feed with 30 items.
$this->createSampleNodes(30);
$feed = $this->createFeed();
$this->updateFeedItems($feed, 30);
// Check for presence of an aggregator pager.
$this->drupalGet('aggregator');
$elements = $this->xpath("//ul[contains(@class, :class)]", [':class' => 'pager__items']);
$this->assertTrue(!empty($elements), 'Individual source page contains a pager.');
// Check for sources page title.
$this->drupalGet('aggregator/sources');
$titles = $this->xpath('//h1[normalize-space(text())=:title]', [':title' => 'Sources']);
$this->assertTrue(!empty($titles), 'Source page contains correct title.');
// Find the expected read_more link on the sources page.
$href = $feed->url();
$links = $this->xpath('//a[@href = :href]', [':href' => $href]);
$this->assertTrue(isset($links[0]), SafeMarkup::format('Link to href %href found.', ['%href' => $href]));
$cache_tags_header = $this->drupalGetHeader('X-Drupal-Cache-Tags');
$cache_tags = explode(' ', $cache_tags_header);
$this->assertTrue(in_array('aggregator_feed:' . $feed->id(), $cache_tags));
// Check the rss aggregator page as anonymous user.
$this->drupalLogout();
$this->drupalGet('aggregator/rss');
$this->assertResponse(403);
// Check the rss aggregator page as admin.
$this->drupalLogin($this->adminUser);
$this->drupalGet('aggregator/rss');
$this->assertResponse(200);
$this->assertEqual($this->drupalGetHeader('Content-type'), 'application/rss+xml; charset=utf-8');
// Check the opml aggregator page.
$this->drupalGet('aggregator/opml');
$content = $this->getSession()->getPage()->getContent();
// We can't use Mink xpath queries here because it only supports HTML pages,
// but we are dealing with XML here.
$xml = simplexml_load_string($content);
$attributes = $xml->xpath('//outline[1]')[0]->attributes();
$this->assertEquals('rss', $attributes->type);
$this->assertEquals($feed->label(), $attributes->text);
$this->assertEquals($feed->getUrl(), $attributes->xmlUrl);
// Check for the presence of a pager.
$this->drupalGet('aggregator/sources/' . $feed->id());
$elements = $this->xpath("//ul[contains(@class, :class)]", [':class' => 'pager__items']);
$this->assertTrue(!empty($elements), 'Individual source page contains a pager.');
$cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
$this->assertTrue(in_array('aggregator_feed:' . $feed->id(), $cache_tags));
$this->assertTrue(in_array('aggregator_feed_view', $cache_tags));
$this->assertTrue(in_array('aggregator_item_view', $cache_tags));
}
}
@@ -0,0 +1,379 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
use Drupal\aggregator\Entity\Feed;
use Drupal\Component\Utility\Html;
use Drupal\Tests\BrowserTestBase;
use Drupal\aggregator\FeedInterface;
/**
* Defines a base class for testing the Aggregator module.
*/
abstract class AggregatorTestBase extends BrowserTestBase {
/**
* A user with permission to administer feeds and create content.
*
* @var \Drupal\user\Entity\User
*/
protected $adminUser;
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'node', 'aggregator', 'aggregator_test', 'views'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create an Article node type.
if ($this->profile != 'standard') {
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
}
$this->adminUser = $this->drupalCreateUser(['access administration pages', 'administer news feeds', 'access news feeds', 'create article content']);
$this->drupalLogin($this->adminUser);
$this->drupalPlaceBlock('local_tasks_block');
}
/**
* Creates an aggregator feed.
*
* This method simulates the form submission on path aggregator/sources/add.
*
* @param string $feed_url
* (optional) If given, feed will be created with this URL, otherwise
* /rss.xml will be used. Defaults to NULL.
* @param array $edit
* Array with additional form fields.
*
* @return \Drupal\aggregator\FeedInterface
* Full feed object if possible.
*
* @see getFeedEditArray()
*/
public function createFeed($feed_url = NULL, array $edit = []) {
$edit = $this->getFeedEditArray($feed_url, $edit);
$this->drupalPostForm('aggregator/sources/add', $edit, t('Save'));
$this->assertText(t('The feed @name has been added.', ['@name' => $edit['title[0][value]']]), format_string('The feed @name has been added.', ['@name' => $edit['title[0][value]']]));
// Verify that the creation message contains a link to a feed.
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', [':href' => 'aggregator/sources/']);
$this->assert(isset($view_link), 'The message area contains a link to a feed');
$fid = db_query("SELECT fid FROM {aggregator_feed} WHERE title = :title AND url = :url", [':title' => $edit['title[0][value]'], ':url' => $edit['url[0][value]']])->fetchField();
$this->assertTrue(!empty($fid), 'The feed found in database.');
return Feed::load($fid);
}
/**
* Deletes an aggregator feed.
*
* @param \Drupal\aggregator\FeedInterface $feed
* Feed object representing the feed.
*/
public function deleteFeed(FeedInterface $feed) {
$this->drupalPostForm('aggregator/sources/' . $feed->id() . '/delete', [], t('Delete'));
$this->assertRaw(t('The feed %title has been deleted.', ['%title' => $feed->label()]), 'Feed deleted successfully.');
}
/**
* Returns a randomly generated feed edit array.
*
* @param string $feed_url
* (optional) If given, feed will be created with this URL, otherwise
* /rss.xml will be used. Defaults to NULL.
* @param array $edit
* Array with additional form fields.
*
* @return array
* A feed array.
*/
public function getFeedEditArray($feed_url = NULL, array $edit = []) {
$feed_name = $this->randomMachineName(10);
if (!$feed_url) {
$feed_url = \Drupal::url('view.frontpage.feed_1', [], [
'query' => ['feed' => $feed_name],
'absolute' => TRUE,
]);
}
$edit += [
'title[0][value]' => $feed_name,
'url[0][value]' => $feed_url,
'refresh' => '900',
];
return $edit;
}
/**
* Returns a randomly generated feed edit object.
*
* @param string $feed_url
* (optional) If given, feed will be created with this URL, otherwise
* /rss.xml will be used. Defaults to NULL.
* @param array $values
* (optional) Default values to initialize object properties with.
*
* @return \Drupal\aggregator\FeedInterface
* A feed object.
*/
public function getFeedEditObject($feed_url = NULL, array $values = []) {
$feed_name = $this->randomMachineName(10);
if (!$feed_url) {
$feed_url = \Drupal::url('view.frontpage.feed_1', [
'query' => ['feed' => $feed_name],
'absolute' => TRUE,
]);
}
$values += [
'title' => $feed_name,
'url' => $feed_url,
'refresh' => '900',
];
return Feed::create($values);
}
/**
* Returns the count of the randomly created feed array.
*
* @return int
* Number of feed items on default feed created by createFeed().
*/
public function getDefaultFeedItemCount() {
// Our tests are based off of rss.xml, so let's find out how many elements should be related.
$feed_count = db_query_range('SELECT COUNT(DISTINCT nid) FROM {node_field_data} n WHERE n.promote = 1 AND n.status = 1', 0, $this->config('system.rss')->get('items.limit'))->fetchField();
return $feed_count > 10 ? 10 : $feed_count;
}
/**
* Updates the feed items.
*
* This method simulates a click to
* admin/config/services/aggregator/update/$fid.
*
* @param \Drupal\aggregator\FeedInterface $feed
* Feed object representing the feed.
* @param int|null $expected_count
* Expected number of feed items. If omitted no check will happen.
*/
public function updateFeedItems(FeedInterface $feed, $expected_count = NULL) {
// First, let's ensure we can get to the rss xml.
$this->drupalGet($feed->getUrl());
$this->assertResponse(200, format_string(':url is reachable.', [':url' => $feed->getUrl()]));
// Attempt to access the update link directly without an access token.
$this->drupalGet('admin/config/services/aggregator/update/' . $feed->id());
$this->assertResponse(403);
// Refresh the feed (simulated link click).
$this->drupalGet('admin/config/services/aggregator');
$this->clickLink('Update items');
// Ensure we have the right number of items.
$result = db_query('SELECT iid FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()]);
$feed->items = [];
foreach ($result as $item) {
$feed->items[] = $item->iid;
}
if ($expected_count !== NULL) {
$feed->item_count = count($feed->items);
$this->assertEqual($expected_count, $feed->item_count, format_string('Total items in feed equal to the total items in database (@val1 != @val2)', ['@val1' => $expected_count, '@val2' => $feed->item_count]));
}
}
/**
* Confirms an item removal from a feed.
*
* @param \Drupal\aggregator\FeedInterface $feed
* Feed object representing the feed.
*/
public function deleteFeedItems(FeedInterface $feed) {
$this->drupalPostForm('admin/config/services/aggregator/delete/' . $feed->id(), [], t('Delete items'));
$this->assertRaw(t('The news items from %title have been deleted.', ['%title' => $feed->label()]), 'Feed items deleted.');
}
/**
* Adds and deletes feed items and ensure that the count is zero.
*
* @param \Drupal\aggregator\FeedInterface $feed
* Feed object representing the feed.
* @param int $expected_count
* Expected number of feed items.
*/
public function updateAndDelete(FeedInterface $feed, $expected_count) {
$this->updateFeedItems($feed, $expected_count);
$count = db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField();
$this->assertTrue($count);
$this->deleteFeedItems($feed);
$count = db_query('SELECT COUNT(*) FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField();
$this->assertTrue($count == 0);
}
/**
* Checks whether the feed name and URL are unique.
*
* @param string $feed_name
* String containing the feed name to check.
* @param string $feed_url
* String containing the feed url to check.
*
* @return bool
* TRUE if feed is unique.
*/
public function uniqueFeed($feed_name, $feed_url) {
$result = db_query("SELECT COUNT(*) FROM {aggregator_feed} WHERE title = :title AND url = :url", [':title' => $feed_name, ':url' => $feed_url])->fetchField();
return (1 == $result);
}
/**
* Creates a valid OPML file from an array of feeds.
*
* @param array $feeds
* An array of feeds.
*
* @return string
* Path to valid OPML file.
*/
public function getValidOpml(array $feeds) {
// Properly escape URLs so that XML parsers don't choke on them.
foreach ($feeds as &$feed) {
$feed['url[0][value]'] = Html::escape($feed['url[0][value]']);
}
/**
* Does not have an XML declaration, must pass the parser.
*/
$opml = <<<EOF
<opml version="1.0">
<head></head>
<body>
<!-- First feed to be imported. -->
<outline text="{$feeds[0]['title[0][value]']}" xmlurl="{$feeds[0]['url[0][value]']}" />
<!-- Second feed. Test string delimitation and attribute order. -->
<outline xmlurl='{$feeds[1]['url[0][value]']}' text='{$feeds[1]['title[0][value]']}'/>
<!-- Test for duplicate URL and title. -->
<outline xmlurl="{$feeds[0]['url[0][value]']}" text="Duplicate URL"/>
<outline xmlurl="http://duplicate.title" text="{$feeds[1]['title[0][value]']}"/>
<!-- Test that feeds are only added with required attributes. -->
<outline text="{$feeds[2]['title[0][value]']}" />
<outline xmlurl="{$feeds[2]['url[0][value]']}" />
</body>
</opml>
EOF;
$path = 'public://valid-opml.xml';
// Add the UTF-8 byte order mark.
return file_unmanaged_save_data(chr(239) . chr(187) . chr(191) . $opml, $path);
}
/**
* Creates an invalid OPML file.
*
* @return string
* Path to invalid OPML file.
*/
public function getInvalidOpml() {
$opml = <<<EOF
<opml>
<invalid>
</opml>
EOF;
$path = 'public://invalid-opml.xml';
return file_unmanaged_save_data($opml, $path);
}
/**
* Creates a valid but empty OPML file.
*
* @return string
* Path to empty OPML file.
*/
public function getEmptyOpml() {
$opml = <<<EOF
<?xml version="1.0" encoding="utf-8"?>
<opml version="1.0">
<head></head>
<body>
<outline text="Sample text" />
<outline text="Sample text" url="Sample URL" />
</body>
</opml>
EOF;
$path = 'public://empty-opml.xml';
return file_unmanaged_save_data($opml, $path);
}
/**
* Returns a example RSS091 feed.
*
* @return string
* Path to the feed.
*/
public function getRSS091Sample() {
return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/modules/aggregator_test/aggregator_test_rss091.xml';
}
/**
* Returns a example Atom feed.
*
* @return string
* Path to the feed.
*/
public function getAtomSample() {
// The content of this sample ATOM feed is based directly off of the
// example provided in RFC 4287.
return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/modules/aggregator_test/aggregator_test_atom.xml';
}
/**
* Returns a example feed.
*
* @return string
* Path to the feed.
*/
public function getHtmlEntitiesSample() {
return $GLOBALS['base_url'] . '/' . drupal_get_path('module', 'aggregator') . '/tests/modules/aggregator_test/aggregator_test_title_entities.xml';
}
/**
* Creates sample article nodes.
*
* @param int $count
* (optional) The number of nodes to generate. Defaults to five.
*/
public function createSampleNodes($count = 5) {
// Post $count article nodes.
for ($i = 0; $i < $count; $i++) {
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName();
$edit['body[0][value]'] = $this->randomMachineName();
$this->drupalPostForm('node/add/article', $edit, t('Save'));
}
}
/**
* Enable the plugins coming with aggregator_test module.
*/
public function enableTestPlugins() {
$this->config('aggregator.settings')
->set('fetcher', 'aggregator_test_fetcher')
->set('parser', 'aggregator_test_parser')
->set('processors', [
'aggregator_test_processor' => 'aggregator_test_processor',
'aggregator' => 'aggregator',
])
->save();
}
}
@@ -0,0 +1,40 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
/**
* Delete feed items from a feed.
*
* @group aggregator
*/
class DeleteFeedItemTest extends AggregatorTestBase {
/**
* Tests running "delete items" from 'admin/config/services/aggregator' page.
*/
public function testDeleteFeedItem() {
// Create a bunch of test feeds.
$feed_urls = [];
// No last-modified, no etag.
$feed_urls[] = \Drupal::url('aggregator_test.feed', [], ['absolute' => TRUE]);
// Last-modified, but no etag.
$feed_urls[] = \Drupal::url('aggregator_test.feed', ['use_last_modified' => 1], ['absolute' => TRUE]);
// No Last-modified, but etag.
$feed_urls[] = \Drupal::url('aggregator_test.feed', ['use_last_modified' => 0, 'use_etag' => 1], ['absolute' => TRUE]);
// Last-modified and etag.
$feed_urls[] = \Drupal::url('aggregator_test.feed', ['use_last_modified' => 1, 'use_etag' => 1], ['absolute' => TRUE]);
foreach ($feed_urls as $feed_url) {
$feed = $this->createFeed($feed_url);
// Update and delete items two times in a row to make sure that removal
// resets all 'modified' information (modified, etag, hash) and allows for
// immediate update. There's 8 items in the feed, but one has an empty
// title and is skipped.
$this->updateAndDelete($feed, 7);
$this->updateAndDelete($feed, 7);
$this->updateAndDelete($feed, 7);
// Delete feed.
$this->deleteFeed($feed);
}
}
}
@@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
/**
* Delete feed test.
*
* @group aggregator
*/
class DeleteFeedTest extends AggregatorTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block'];
/**
* Deletes a feed and ensures that all of its services are deleted.
*/
public function testDeleteFeed() {
$feed1 = $this->createFeed();
$feed2 = $this->createFeed();
// Place a block for both feeds.
$block = $this->drupalPlaceBlock('aggregator_feed_block');
$block->getPlugin()->setConfigurationValue('feed', $feed1->id());
$block->save();
$block2 = $this->drupalPlaceBlock('aggregator_feed_block');
$block2->getPlugin()->setConfigurationValue('feed', $feed2->id());
$block2->save();
// Delete feed.
$this->deleteFeed($feed1);
$this->assertText($feed2->label());
$block_storage = $this->container->get('entity.manager')->getStorage('block');
$this->assertNull($block_storage->load($block->id()), 'Block for the deleted feed was deleted.');
$this->assertEqual($block2->id(), $block_storage->load($block2->id())->id(), 'Block for not deleted feed still exists.');
// Check feed source.
$this->drupalGet('aggregator/sources/' . $feed1->id());
$this->assertResponse(404, 'Deleted feed source does not exists.');
// Check database for feed.
$result = db_query("SELECT COUNT(*) FROM {aggregator_feed} WHERE title = :title AND url = :url", [':title' => $feed1->label(), ':url' => $feed1->getUrl()])->fetchField();
$this->assertFalse($result, 'Feed not found in database');
}
}
@@ -0,0 +1,73 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
/**
* Tests the display of a feed on the Aggregator list page.
*
* @group aggregator
*/
class FeedAdminDisplayTest extends AggregatorTestBase {
/**
* Tests the "Next update" and "Last update" fields.
*/
public function testFeedUpdateFields() {
// Create scheduled feed.
$scheduled_feed = $this->createFeed(NULL, ['refresh' => '900']);
$this->drupalGet('admin/config/services/aggregator');
$this->assertResponse(200, 'Aggregator feed overview page exists.');
// The scheduled feed shows that it has not been updated yet and is
// scheduled.
$this->assertText('never', 'The scheduled feed has not been updated yet. Last update shows "never".');
$this->assertText('imminently', 'The scheduled feed has not been updated yet. Next update shows "imminently".');
$this->assertNoText('ago', 'The scheduled feed has not been updated yet. Last update does not show "x x ago".');
$this->assertNoText('left', 'The scheduled feed has not been updated yet. Next update does not show "x x left".');
$this->updateFeedItems($scheduled_feed);
$this->drupalGet('admin/config/services/aggregator');
// After the update, an interval should be displayed on both last updated
// and next update.
$this->assertNoText('never', 'The scheduled feed has been updated. Last updated changed.');
$this->assertNoText('imminently', 'The scheduled feed has been updated. Next update changed.');
$this->assertText('ago', 'The scheduled feed been updated. Last update shows "x x ago".');
$this->assertText('left', 'The scheduled feed has been updated. Next update shows "x x left".');
// Delete scheduled feed.
$this->deleteFeed($scheduled_feed);
// Create non-scheduled feed.
$non_scheduled_feed = $this->createFeed(NULL, ['refresh' => '0']);
$this->drupalGet('admin/config/services/aggregator');
// The non scheduled feed shows that it has not been updated yet.
$this->assertText('never', 'The non scheduled feed has not been updated yet. Last update shows "never".');
$this->assertNoText('imminently', 'The non scheduled feed does not show "imminently" as next update.');
$this->assertNoText('ago', 'The non scheduled feed has not been updated. It does not show "x x ago" as last update.');
$this->assertNoText('left', 'The feed is not scheduled. It does not show a timeframe "x x left" for next update.');
$this->updateFeedItems($non_scheduled_feed);
$this->drupalGet('admin/config/services/aggregator');
// After the feed update, we still need to see "never" as next update label.
// Last update will show an interval.
$this->assertNoText('imminently', 'The updated non scheduled feed does not show "imminently" as next update.');
$this->assertText('never', 'The updated non scheduled feed still shows "never" as next update.');
$this->assertText('ago', 'The non scheduled feed has been updated. It shows "x x ago" as last update.');
$this->assertNoText('left', 'The feed is not scheduled. It does not show a timeframe "x x left" for next update.');
}
/**
* {@inheritdoc}
*/
public function randomMachineName($length = 8) {
$value = parent::randomMachineName($length);
// See expected values in testFeedUpdateFields().
$value = str_replace(['never', 'imminently', 'ago', 'left'], 'x', $value);
return $value;
}
}
@@ -0,0 +1,52 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
use Drupal\aggregator\Entity\Feed;
use Drupal\system\Tests\Entity\EntityWithUriCacheTagsTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Tests the Feed entity's cache tags.
*
* @group aggregator
*/
class FeedCacheTagsTest extends EntityWithUriCacheTagsTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['aggregator'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Give anonymous users permission to access feeds, so that we can verify
// the cache tags of cached versions of feeds.
$user_role = Role::load(RoleInterface::ANONYMOUS_ID);
$user_role->grantPermission('access news feeds');
$user_role->save();
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Llama" feed.
$feed = Feed::create([
'title' => 'Llama',
'url' => 'https://www.drupal.org/',
'refresh' => 900,
'checked' => 1389919932,
'description' => 'Drupal.org',
]);
$feed->save();
return $feed;
}
}
@@ -0,0 +1,44 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
/**
* Tests the fetcher plugins functionality and discoverability.
*
* @group aggregator
*
* @see \Drupal\aggregator_test\Plugin\aggregator\fetcher\TestFetcher.
*/
class FeedFetcherPluginTest extends AggregatorTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Enable test plugins.
$this->enableTestPlugins();
// Create some nodes.
$this->createSampleNodes();
}
/**
* Test fetching functionality.
*/
public function testfetch() {
// Create feed with local url.
$feed = $this->createFeed();
$this->updateFeedItems($feed);
$this->assertFalse(empty($feed->items));
// Delete items and restore checked property to 0.
$this->deleteFeedItems($feed);
// Change its name and try again.
$feed->setTitle('Do not fetch');
$feed->save();
$this->updateFeedItems($feed);
// Fetch should fail due to feed name.
$this->assertTrue(empty($feed->items));
}
}
@@ -0,0 +1,88 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\Traits\Core\CronRunTrait;
/**
* Tests aggregator feeds in multiple languages.
*
* @group aggregator
*/
class FeedLanguageTest extends AggregatorTestBase {
use CronRunTrait;
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['language'];
/**
* List of langcodes.
*
* @var string[]
*/
protected $langcodes = [];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Create test languages.
$this->langcodes = [ConfigurableLanguage::load('en')];
for ($i = 1; $i < 3; ++$i) {
$language = ConfigurableLanguage::create([
'id' => 'l' . $i,
'label' => $this->randomString(),
]);
$language->save();
$this->langcodes[$i] = $language->id();
}
}
/**
* Tests creation of feeds with a language.
*/
public function testFeedLanguage() {
$admin_user = $this->drupalCreateUser(['administer languages', 'access administration pages', 'administer news feeds', 'access news feeds', 'create article content']);
$this->drupalLogin($admin_user);
// Enable language selection for feeds.
$edit['entity_types[aggregator_feed]'] = TRUE;
$edit['settings[aggregator_feed][aggregator_feed][settings][language][language_alterable]'] = TRUE;
$this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
/** @var \Drupal\aggregator\FeedInterface[] $feeds */
$feeds = [];
// Create feeds.
$feeds[1] = $this->createFeed(NULL, ['langcode[0][value]' => $this->langcodes[1]]);
$feeds[2] = $this->createFeed(NULL, ['langcode[0][value]' => $this->langcodes[2]]);
// Make sure that the language has been assigned.
$this->assertEqual($feeds[1]->language()->getId(), $this->langcodes[1]);
$this->assertEqual($feeds[2]->language()->getId(), $this->langcodes[2]);
// Create example nodes to create feed items from and then update the feeds.
$this->createSampleNodes();
$this->cronRun();
// Loop over the created feed items and verify that their language matches
// the one from the feed.
foreach ($feeds as $feed) {
/** @var \Drupal\aggregator\ItemInterface[] $items */
$items = entity_load_multiple_by_properties('aggregator_item', ['fid' => $feed->id()]);
$this->assertTrue(count($items) > 0, 'Feed items were created.');
foreach ($items as $item) {
$this->assertEqual($item->language()->getId(), $feed->language()->getId());
}
}
}
}
@@ -0,0 +1,111 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
use Drupal\Core\Url;
use Drupal\aggregator\Entity\Feed;
/**
* Tests the built-in feed parser with valid feed samples.
*
* @group aggregator
*/
class FeedParserTest extends AggregatorTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Do not delete old aggregator items during these tests, since our sample
// feeds have hardcoded dates in them (which may be expired when this test
// is run).
$this->config('aggregator.settings')->set('items.expire', AGGREGATOR_CLEAR_NEVER)->save();
}
/**
* Tests a feed that uses the RSS 0.91 format.
*/
public function testRSS091Sample() {
$feed = $this->createFeed($this->getRSS091Sample());
$feed->refreshItems();
$this->drupalGet('aggregator/sources/' . $feed->id());
$this->assertResponse(200, format_string('Feed %name exists.', ['%name' => $feed->label()]));
$this->assertText('First example feed item title');
$this->assertLinkByHref('http://example.com/example-turns-one');
$this->assertText('First example feed item description.');
$this->assertRaw('<img src="http://example.com/images/druplicon.png"');
// Several additional items that include elements over 255 characters.
$this->assertRaw("Second example feed item title.");
$this->assertText('Long link feed item title');
$this->assertText('Long link feed item description');
$this->assertLinkByHref('http://example.com/tomorrow/and/tomorrow/and/tomorrow/creeps/in/this/petty/pace/from/day/to/day/to/the/last/syllable/of/recorded/time/and/all/our/yesterdays/have/lighted/fools/the/way/to/dusty/death/out/out/brief/candle/life/is/but/a/walking/shadow/a/poor/player/that/struts/and/frets/his/hour/upon/the/stage/and/is/heard/no/more/it/is/a/tale/told/by/an/idiot/full/of/sound/and/fury/signifying/nothing');
$this->assertText('Long author feed item title');
$this->assertText('Long author feed item description');
$this->assertLinkByHref('http://example.com/long/author');
}
/**
* Tests a feed that uses the Atom format.
*/
public function testAtomSample() {
$feed = $this->createFeed($this->getAtomSample());
$feed->refreshItems();
$this->drupalGet('aggregator/sources/' . $feed->id());
$this->assertResponse(200, format_string('Feed %name exists.', ['%name' => $feed->label()]));
$this->assertText('Atom-Powered Robots Run Amok');
$this->assertLinkByHref('http://example.org/2003/12/13/atom03');
$this->assertText('Some text.');
$this->assertEqual('urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a', db_query('SELECT guid FROM {aggregator_item} WHERE link = :link', [':link' => 'http://example.org/2003/12/13/atom03'])->fetchField(), 'Atom entry id element is parsed correctly.');
// Check for second feed entry.
$this->assertText('We tried to stop them, but we failed.');
$this->assertLinkByHref('http://example.org/2003/12/14/atom03');
$this->assertText('Some other text.');
$db_guid = db_query('SELECT guid FROM {aggregator_item} WHERE link = :link', [
':link' => 'http://example.org/2003/12/14/atom03',
])->fetchField();
$this->assertEqual('urn:uuid:1225c695-cfb8-4ebb-bbbb-80da344efa6a', $db_guid, 'Atom entry id element is parsed correctly.');
}
/**
* Tests a feed that uses HTML entities in item titles.
*/
public function testHtmlEntitiesSample() {
$feed = $this->createFeed($this->getHtmlEntitiesSample());
$feed->refreshItems();
$this->drupalGet('aggregator/sources/' . $feed->id());
$this->assertResponse(200, format_string('Feed %name exists.', ['%name' => $feed->label()]));
$this->assertRaw("Quote&quot; Amp&amp;");
}
/**
* Tests that a redirected feed is tracked to its target.
*/
public function testRedirectFeed() {
$redirect_url = Url::fromRoute('aggregator_test.redirect')->setAbsolute()->toString();
$feed = Feed::create(['url' => $redirect_url, 'title' => $this->randomMachineName()]);
$feed->save();
$feed->refreshItems();
// Make sure that the feed URL was updated correctly.
$this->assertEqual($feed->getUrl(), \Drupal::url('aggregator_test.feed', [], ['absolute' => TRUE]));
}
/**
* Tests error handling when an invalid feed is added.
*/
public function testInvalidFeed() {
// Simulate a typo in the URL to force a curl exception.
$invalid_url = 'http:/www.drupal.org';
$feed = Feed::create(['url' => $invalid_url, 'title' => $this->randomMachineName()]);
$feed->save();
// Update the feed. Use the UI to be able to check the message easily.
$this->drupalGet('admin/config/services/aggregator');
$this->clickLink(t('Update items'));
$this->assertRaw(t('The feed from %title seems to be broken because of error', ['%title' => $feed->label()]));
}
}
@@ -0,0 +1,67 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
use Drupal\aggregator\Entity\Feed;
use Drupal\aggregator\Entity\Item;
/**
* Tests the processor plugins functionality and discoverability.
*
* @group aggregator
*
* @see \Drupal\aggregator_test\Plugin\aggregator\processor\TestProcessor.
*/
class FeedProcessorPluginTest extends AggregatorTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Enable test plugins.
$this->enableTestPlugins();
// Create some nodes.
$this->createSampleNodes();
}
/**
* Test processing functionality.
*/
public function testProcess() {
$feed = $this->createFeed();
$this->updateFeedItems($feed);
foreach ($feed->items as $iid) {
$item = Item::load($iid);
$this->assertTrue(strpos($item->label(), 'testProcessor') === 0);
}
}
/**
* Test deleting functionality.
*/
public function testDelete() {
$feed = $this->createFeed();
$description = $feed->description->value ?: '';
$this->updateAndDelete($feed, NULL);
// Make sure the feed title is changed.
$entities = entity_load_multiple_by_properties('aggregator_feed', ['description' => $description]);
$this->assertTrue(empty($entities));
}
/**
* Test post-processing functionality.
*/
public function testPostProcess() {
$feed = $this->createFeed(NULL, ['refresh' => 1800]);
$this->updateFeedItems($feed);
$feed_id = $feed->id();
// Reset entity cache manually.
\Drupal::entityManager()->getStorage('aggregator_feed')->resetCache([$feed_id]);
// Reload the feed to get new values.
$feed = Feed::load($feed_id);
// Make sure its refresh rate doubled.
$this->assertEqual($feed->getRefreshRate(), 3600);
}
}
@@ -0,0 +1,124 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
/**
* Tests OPML import.
*
* @group aggregator
*/
class ImportOpmlTest extends AggregatorTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['block', 'help'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$admin_user = $this->drupalCreateUser(['administer news feeds', 'access news feeds', 'create article content', 'administer blocks']);
$this->drupalLogin($admin_user);
}
/**
* Opens OPML import form.
*/
public function openImportForm() {
// Enable the help block.
$this->drupalPlaceBlock('help_block', ['region' => 'help']);
$this->drupalGet('admin/config/services/aggregator/add/opml');
$this->assertText('A single OPML document may contain many feeds.', 'Found OPML help text.');
$this->assertField('files[upload]', 'Found file upload field.');
$this->assertField('remote', 'Found Remote URL field.');
$this->assertField('refresh', '', 'Found Refresh field.');
}
/**
* Submits form filled with invalid fields.
*/
public function validateImportFormFields() {
$before = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
$edit = [];
$this->drupalPostForm('admin/config/services/aggregator/add/opml', $edit, t('Import'));
$this->assertRaw(t('<em>Either</em> upload a file or enter a URL.'), 'Error if no fields are filled.');
$path = $this->getEmptyOpml();
$edit = [
'files[upload]' => $path,
'remote' => file_create_url($path),
];
$this->drupalPostForm('admin/config/services/aggregator/add/opml', $edit, t('Import'));
$this->assertRaw(t('<em>Either</em> upload a file or enter a URL.'), 'Error if both fields are filled.');
$edit = ['remote' => 'invalidUrl://empty'];
$this->drupalPostForm('admin/config/services/aggregator/add/opml', $edit, t('Import'));
$this->assertText(t('The URL invalidUrl://empty is not valid.'), 'Error if the URL is invalid.');
$after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
$this->assertEqual($before, $after, 'No feeds were added during the three last form submissions.');
}
/**
* Submits form with invalid, empty, and valid OPML files.
*/
protected function submitImportForm() {
$before = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
$form['files[upload]'] = $this->getInvalidOpml();
$this->drupalPostForm('admin/config/services/aggregator/add/opml', $form, t('Import'));
$this->assertText(t('No new feed has been added.'), 'Attempting to upload invalid XML.');
$edit = ['remote' => file_create_url($this->getEmptyOpml())];
$this->drupalPostForm('admin/config/services/aggregator/add/opml', $edit, t('Import'));
$this->assertText(t('No new feed has been added.'), 'Attempting to load empty OPML from remote URL.');
$after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
$this->assertEqual($before, $after, 'No feeds were added during the two last form submissions.');
db_delete('aggregator_feed')->execute();
$feeds[0] = $this->getFeedEditArray();
$feeds[1] = $this->getFeedEditArray();
$feeds[2] = $this->getFeedEditArray();
$edit = [
'files[upload]' => $this->getValidOpml($feeds),
'refresh' => '900',
];
$this->drupalPostForm('admin/config/services/aggregator/add/opml', $edit, t('Import'));
$this->assertRaw(t('A feed with the URL %url already exists.', ['%url' => $feeds[0]['url[0][value]']]), 'Verifying that a duplicate URL was identified');
$this->assertRaw(t('A feed named %title already exists.', ['%title' => $feeds[1]['title[0][value]']]), 'Verifying that a duplicate title was identified');
$after = db_query('SELECT COUNT(*) FROM {aggregator_feed}')->fetchField();
$this->assertEqual($after, 2, 'Verifying that two distinct feeds were added.');
$feeds_from_db = db_query("SELECT title, url, refresh FROM {aggregator_feed}");
$refresh = TRUE;
foreach ($feeds_from_db as $feed) {
$title[$feed->url] = $feed->title;
$url[$feed->title] = $feed->url;
$refresh = $refresh && $feed->refresh == 900;
}
$this->assertEqual($title[$feeds[0]['url[0][value]']], $feeds[0]['title[0][value]'], 'First feed was added correctly.');
$this->assertEqual($url[$feeds[1]['title[0][value]']], $feeds[1]['url[0][value]'], 'Second feed was added correctly.');
$this->assertTrue($refresh, 'Refresh times are correct.');
}
/**
* Tests the import of an OPML file.
*/
public function testOpmlImport() {
$this->openImportForm();
$this->validateImportFormFields();
$this->submitImportForm();
}
}
@@ -0,0 +1,83 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
use Drupal\aggregator\Entity\Feed;
use Drupal\aggregator\Entity\Item;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\system\Tests\Entity\EntityCacheTagsTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
/**
* Tests the Item entity's cache tags.
*
* @group aggregator
*/
class ItemCacheTagsTest extends EntityCacheTagsTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['aggregator'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Give anonymous users permission to access feeds, so that we can verify
// the cache tags of cached versions of feed items.
$user_role = Role::load(RoleInterface::ANONYMOUS_ID);
$user_role->grantPermission('access news feeds');
$user_role->save();
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Camelids" feed.
$feed = Feed::create([
'title' => 'Camelids',
'url' => 'https://groups.drupal.org/not_used/167169',
'refresh' => 900,
'checked' => 1389919932,
'description' => 'Drupal Core Group feed',
]);
$feed->save();
// Create a "Llama" aggregator feed item.
$item = Item::create([
'fid' => $feed->id(),
'title' => t('Llama'),
'path' => 'https://www.drupal.org/',
]);
$item->save();
return $item;
}
/**
* Tests that when creating a feed item, the feed tag is invalidated.
*/
public function testEntityCreation() {
// Create a cache entry that is tagged with a feed cache tag.
\Drupal::cache('render')->set('foo', 'bar', CacheBackendInterface::CACHE_PERMANENT, $this->entity->getCacheTags());
// Verify a cache hit.
$this->verifyRenderCache('foo', ['aggregator_feed:1']);
// Now create a feed item in that feed.
Item::create([
'fid' => $this->entity->getFeedId(),
'title' => t('Llama 2'),
'path' => 'https://groups.drupal.org/',
])->save();
// Verify a cache miss.
$this->assertFalse(\Drupal::cache('render')->get('foo'), 'Creating a new feed item invalidates the cache tag of the feed.');
}
}
@@ -0,0 +1,74 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
use Drupal\aggregator\Entity\Feed;
/**
* Update feed items from a feed.
*
* @group aggregator
*/
class UpdateFeedItemTest extends AggregatorTestBase {
/**
* Tests running "update items" from 'admin/config/services/aggregator' page.
*/
public function testUpdateFeedItem() {
$this->createSampleNodes();
// Create a feed and test updating feed items if possible.
$feed = $this->createFeed();
if (!empty($feed)) {
$this->updateFeedItems($feed, $this->getDefaultFeedItemCount());
$this->deleteFeedItems($feed);
}
// Delete feed.
$this->deleteFeed($feed);
// Test updating feed items without valid timestamp information.
$edit = [
'title[0][value]' => "Feed without publish timestamp",
'url[0][value]' => $this->getRSS091Sample(),
];
$this->drupalGet($edit['url[0][value]']);
$this->assertResponse(200);
$this->drupalPostForm('aggregator/sources/add', $edit, t('Save'));
$this->assertText(t('The feed @name has been added.', ['@name' => $edit['title[0][value]']]), format_string('The feed @name has been added.', ['@name' => $edit['title[0][value]']]));
// Verify that the creation message contains a link to a feed.
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', [':href' => 'aggregator/sources/']);
$this->assert(isset($view_link), 'The message area contains a link to a feed');
$fid = db_query("SELECT fid FROM {aggregator_feed} WHERE url = :url", [':url' => $edit['url[0][value]']])->fetchField();
$feed = Feed::load($fid);
$feed->refreshItems();
$before = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField();
// Sleep for 3 second.
sleep(3);
db_update('aggregator_feed')
->condition('fid', $feed->id())
->fields([
'checked' => 0,
'hash' => '',
'etag' => '',
'modified' => 0,
])
->execute();
$feed->refreshItems();
$after = db_query('SELECT timestamp FROM {aggregator_item} WHERE fid = :fid', [':fid' => $feed->id()])->fetchField();
$this->assertTrue($before === $after, format_string('Publish timestamp of feed item was not updated (@before === @after)', ['@before' => $before, '@after' => $after]));
// Make sure updating items works even after uninstalling a module
// that provides the selected plugins.
$this->enableTestPlugins();
$this->container->get('module_installer')->uninstall(['aggregator_test']);
$this->updateFeedItems($feed);
$this->assertResponse(200);
}
}
@@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\aggregator\Functional;
/**
* Update feed test.
*
* @group aggregator
*/
class UpdateFeedTest extends AggregatorTestBase {
/**
* Creates a feed and attempts to update it.
*/
public function testUpdateFeed() {
$remaining_fields = ['title[0][value]', 'url[0][value]', ''];
foreach ($remaining_fields as $same_field) {
$feed = $this->createFeed();
// Get new feed data array and modify newly created feed.
$edit = $this->getFeedEditArray();
// Change refresh value.
$edit['refresh'] = 1800;
if (isset($feed->{$same_field}->value)) {
$edit[$same_field] = $feed->{$same_field}->value;
}
$this->drupalPostForm('aggregator/sources/' . $feed->id() . '/configure', $edit, t('Save'));
$this->assertText(t('The feed @name has been updated.', ['@name' => $edit['title[0][value]']]), format_string('The feed %name has been updated.', ['%name' => $edit['title[0][value]']]));
// Verify that the creation message contains a link to a feed.
$view_link = $this->xpath('//div[@class="messages"]//a[contains(@href, :href)]', [':href' => 'aggregator/sources/']);
$this->assert(isset($view_link), 'The message area contains a link to a feed');
// Check feed data.
$this->assertUrl($feed->url('canonical', ['absolute' => TRUE]));
$this->assertTrue($this->uniqueFeed($edit['title[0][value]'], $edit['url[0][value]']), 'The feed is unique.');
// Check feed source.
$this->drupalGet('aggregator/sources/' . $feed->id());
$this->assertResponse(200, 'Feed source exists.');
$this->assertText($edit['title[0][value]'], 'Page title');
// Set correct title so deleteFeed() will work.
$feed->title = $edit['title[0][value]'];
// Delete feed.
$this->deleteFeed($feed);
}
}
}
@@ -17,7 +17,7 @@ class FeedValidationTest extends EntityKernelTestBase {
*
* @var array
*/
public static $modules = array('aggregator', 'options');
public static $modules = ['aggregator', 'options'];
/**
* {@inheritdoc}
@@ -2,7 +2,7 @@
namespace Drupal\Tests\aggregator\Kernel\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
use Drupal\Tests\SchemaCheckTestTrait;
use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
/**
@@ -34,7 +34,7 @@ class MigrateAggregatorConfigsTest extends MigrateDrupal6TestBase {
$config = $this->config('aggregator.settings');
$this->assertIdentical('aggregator', $config->get('fetcher'));
$this->assertIdentical('aggregator', $config->get('parser'));
$this->assertIdentical(array('aggregator'), $config->get('processors'));
$this->assertIdentical(['aggregator'], $config->get('processors'));
$this->assertIdentical(600, $config->get('items.teaser_length'));
$this->assertIdentical('<a> <b> <br /> <dd> <dl> <dt> <em> <i> <li> <ol> <p> <strong> <u> <ul>', $config->get('items.allowed_html'));
$this->assertIdentical(9676800, $config->get('items.expire'));
@@ -0,0 +1,60 @@
<?php
namespace Drupal\Tests\aggregator\Kernel\Plugin\migrate\source;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests D6 aggregator feed source plugin.
*
* @covers \Drupal\aggregator\Plugin\migrate\source\AggregatorFeed
* @group aggregator
*/
class AggregatorFeedTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['aggregator', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
$tests[0]['database']['aggregator_feed'] = [
[
'fid' => 1,
'title' => 'feed title 1',
'url' => 'http://example.com/feed.rss',
'refresh' => 900,
'checked' => 0,
'link' => 'http://example.com',
'description' => 'A vague description',
'image' => '',
'etag' => '',
'modified' => 0,
'block' => 5,
],
[
'fid' => 2,
'title' => 'feed title 2',
'url' => 'http://example.net/news.rss',
'refresh' => 1800,
'checked' => 0,
'link' => 'http://example.net',
'description' => 'An even more vague description',
'image' => '',
'etag' => '',
'modified' => 0,
'block' => 5,
],
];
// The expected results are identical to the source data.
$tests[0]['expected_results'] = $tests[0]['database']['aggregator_feed'];
return $tests;
}
}
@@ -0,0 +1,44 @@
<?php
namespace Drupal\Tests\aggregator\Kernel\Plugin\migrate\source;
use Drupal\Tests\migrate\Kernel\MigrateSqlSourceTestBase;
/**
* Tests aggregator item source plugin.
*
* @covers \Drupal\aggregator\Plugin\migrate\source\AggregatorItem
* @group aggregator
*/
class AggregatorItemTest extends MigrateSqlSourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['aggregator', 'migrate_drupal'];
/**
* {@inheritdoc}
*/
public function providerSource() {
$tests = [];
$tests[0]['database']['aggregator_item'] = [
[
'iid' => 1,
'fid' => 1,
'title' => 'This (three) weeks in Drupal Core - January 10th 2014',
'link' => 'https://groups.drupal.org/node/395218',
'author' => 'larowlan',
'description' => "<h2 id='new'>What's new with Drupal 8?</h2>",
'timestamp' => 1389297196,
'guid' => '395218 at https://groups.drupal.org',
],
];
// The expected results are identical to the source data.
$tests[0]['expected_results'] = $tests[0]['database']['aggregator_item'];
return $tests;
}
}
@@ -21,14 +21,14 @@ class IntegrationTest extends ViewsKernelTestBase {
*
* @var array
*/
public static $modules = array('aggregator', 'aggregator_test_views', 'system', 'field', 'options', 'user');
public static $modules = ['aggregator', 'aggregator_test_views', 'system', 'field', 'options', 'user'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = array('test_aggregator_items');
public static $testViews = ['test_aggregator_items'];
/**
* The entity storage for aggregator items.
@@ -53,7 +53,7 @@ class IntegrationTest extends ViewsKernelTestBase {
$this->installEntitySchema('aggregator_item');
$this->installEntitySchema('aggregator_feed');
ViewTestData::createTestViews(get_class($this), array('aggregator_test_views'));
ViewTestData::createTestViews(get_class($this), ['aggregator_test_views']);
$this->itemStorage = $this->container->get('entity.manager')->getStorage('aggregator_item');
$this->feedStorage = $this->container->get('entity.manager')->getStorage('aggregator_feed');
@@ -66,19 +66,19 @@ class IntegrationTest extends ViewsKernelTestBase {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$feed = $this->feedStorage->create(array(
$feed = $this->feedStorage->create([
'title' => $this->randomMachineName(),
'url' => 'https://www.drupal.org/',
'refresh' => 900,
'checked' => 123543535,
'description' => $this->randomMachineName(),
));
]);
$feed->save();
$items = array();
$expected = array();
$items = [];
$expected = [];
for ($i = 0; $i < 10; $i++) {
$values = array();
$values = [];
$values['fid'] = $feed->id();
$values['timestamp'] = mt_rand(REQUEST_TIME - 10, REQUEST_TIME + 10);
$values['title'] = $this->randomMachineName();
@@ -99,13 +99,13 @@ class IntegrationTest extends ViewsKernelTestBase {
$view = Views::getView('test_aggregator_items');
$this->executeView($view);
$column_map = array(
$column_map = [
'iid' => 'iid',
'title' => 'title',
'aggregator_item_timestamp' => 'timestamp',
'description' => 'description',
'aggregator_item_author' => 'author',
);
];
$this->assertIdenticalResultset($view, $expected, $column_map);
// Ensure that the rendering of the linked title works as expected.
@@ -15,7 +15,7 @@ class AggregatorLocalTasksTest extends LocalTaskIntegrationTestBase {
* {@inheritdoc}
*/
protected function setUp() {
$this->directoryList = array('aggregator' => 'core/modules/aggregator');
$this->directoryList = ['aggregator' => 'core/modules/aggregator'];
parent::setUp();
}
@@ -25,19 +25,19 @@ class AggregatorLocalTasksTest extends LocalTaskIntegrationTestBase {
* @dataProvider getAggregatorAdminRoutes
*/
public function testAggregatorAdminLocalTasks($route) {
$this->assertLocalTasks($route, array(
0 => array('aggregator.admin_overview', 'aggregator.admin_settings'),
));
$this->assertLocalTasks($route, [
0 => ['aggregator.admin_overview', 'aggregator.admin_settings'],
]);
}
/**
* Provides a list of routes to test.
*/
public function getAggregatorAdminRoutes() {
return array(
array('aggregator.admin_overview'),
array('aggregator.admin_settings'),
);
return [
['aggregator.admin_overview'],
['aggregator.admin_settings'],
];
}
/**
@@ -46,9 +46,9 @@ class AggregatorLocalTasksTest extends LocalTaskIntegrationTestBase {
* @dataProvider getAggregatorSourceRoutes
*/
public function testAggregatorSourceLocalTasks($route) {
$this->assertLocalTasks($route, array(
0 => array('entity.aggregator_feed.canonical', 'entity.aggregator_feed.edit_form', 'entity.aggregator_feed.delete_form'),
));
$this->assertLocalTasks($route, [
0 => ['entity.aggregator_feed.canonical', 'entity.aggregator_feed.edit_form', 'entity.aggregator_feed.delete_form'],
]);
;
}
@@ -56,10 +56,10 @@ class AggregatorLocalTasksTest extends LocalTaskIntegrationTestBase {
* Provides a list of source routes to test.
*/
public function getAggregatorSourceRoutes() {
return array(
array('entity.aggregator_feed.canonical'),
array('entity.aggregator_feed.edit_form'),
);
return [
['entity.aggregator_feed.canonical'],
['entity.aggregator_feed.edit_form'],
];
}
}
@@ -1,6 +1,6 @@
<?php
namespace Drupal\Tests\aggregator\Unit\Plugin {
namespace Drupal\Tests\aggregator\Unit\Plugin;
use Drupal\aggregator\Form\SettingsForm;
use Drupal\Core\Form\FormState;
@@ -39,20 +39,20 @@ class AggregatorPluginSettingsBaseTest extends UnitTestCase {
*/
protected function setUp() {
$this->configFactory = $this->getConfigFactoryStub(
array(
'aggregator.settings' => array(
'processors' => array('aggregator_test'),
),
'aggregator_test.settings' => array(),
)
[
'aggregator.settings' => [
'processors' => ['aggregator_test'],
],
'aggregator_test.settings' => [],
]
);
foreach (array('fetcher', 'parser', 'processor') as $type) {
foreach (['fetcher', 'parser', 'processor'] as $type) {
$this->managers[$type] = $this->getMockBuilder('Drupal\aggregator\Plugin\AggregatorPluginManager')
->disableOriginalConstructor()
->getMock();
$this->managers[$type]->expects($this->once())
->method('getDefinitions')
->will($this->returnValue(array('aggregator_test' => array('title' => '', 'description' => ''))));
->will($this->returnValue(['aggregator_test' => ['title' => '', 'description' => '']]));
}
$this->settingsForm = new SettingsForm(
@@ -79,8 +79,8 @@ class AggregatorPluginSettingsBaseTest extends UnitTestCase {
$test_processor = $this->getMock(
'Drupal\aggregator_test\Plugin\aggregator\processor\TestProcessor',
array('buildConfigurationForm', 'validateConfigurationForm', 'submitConfigurationForm'),
array(array(), 'aggregator_test', array('description' => ''), $this->configFactory)
['buildConfigurationForm', 'validateConfigurationForm', 'submitConfigurationForm'],
[[], 'aggregator_test', ['description' => ''], $this->configFactory]
);
$test_processor->expects($this->at(0))
->method('buildConfigurationForm')
@@ -98,18 +98,16 @@ class AggregatorPluginSettingsBaseTest extends UnitTestCase {
->with($this->equalTo('aggregator_test'))
->will($this->returnValue($test_processor));
$form = $this->settingsForm->buildForm(array(), $form_state);
$form = $this->settingsForm->buildForm([], $form_state);
$this->settingsForm->validateForm($form, $form_state);
$this->settingsForm->submitForm($form, $form_state);
}
}
}
// @todo Delete after https://www.drupal.org/node/2278383 is in.
namespace Drupal\Core\Form;
namespace {
// @todo Delete after https://www.drupal.org/node/1858196 is in.
if (!function_exists('drupal_set_message')) {
function drupal_set_message() {}
}
if (!function_exists('drupal_set_message')) {
function drupal_set_message() {}
}
@@ -6,8 +6,8 @@ package: Core
# core: 8.x
configure: system.cron_settings
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233790
datestamp: 1502903957
@@ -34,14 +34,8 @@ function automated_cron_help($route_name, RouteMatchInterface $route_match) {
function automated_cron_form_system_cron_settings_alter(&$form, &$form_state) {
$automated_cron_settings = \Drupal::config('automated_cron.settings');
// Add automated cron settings.
$form['automated_cron'] = [
'#title' => t('Cron settings'),
'#type' => 'details',
'#open' => TRUE,
];
$options = [3600, 10800, 21600, 43200, 86400, 604800];
$form['automated_cron']['interval'] = [
$form['cron']['interval'] = [
'#type' => 'select',
'#title' => t('Run cron every'),
'#description' => t('More information about setting up scheduled tasks can be found by <a href="@url">reading the cron tutorial on drupal.org</a>.', ['@url' => 'https://www.drupal.org/cron']),
@@ -49,13 +43,6 @@ function automated_cron_form_system_cron_settings_alter(&$form, &$form_state) {
'#options' => [0 => t('Never')] + array_map([\Drupal::service('date.formatter'), 'formatInterval'], array_combine($options, $options)),
];
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => t('Save configuration'),
'#button_type' => 'primary',
];
// Add submit callback.
$form['#submit'][] = 'automated_cron_settings_submit';
@@ -70,5 +57,4 @@ function automated_cron_settings_submit(array $form, FormStateInterface $form_st
\Drupal::configFactory()->getEditable('automated_cron.settings')
->set('interval', $form_state->getValue('interval'))
->save();
drupal_set_message(t('The configuration options have been saved.'));
}
+3 -3
View File
@@ -6,8 +6,8 @@ package: Core
# core: 8.x
configure: ban.admin_page
# Information added by Drupal.org packaging script on 2016-08-03
version: '8.1.8'
# Information added by Drupal.org packaging script on 2017-08-16
version: '8.3.7'
core: '8.x'
project: 'drupal'
datestamp: 1470233791
datestamp: 1502903957
+12 -12
View File
@@ -9,27 +9,27 @@
* Implements hook_schema().
*/
function ban_schema() {
$schema['ban_ip'] = array(
$schema['ban_ip'] = [
'description' => 'Stores banned IP addresses.',
'fields' => array(
'iid' => array(
'fields' => [
'iid' => [
'description' => 'Primary Key: unique ID for IP addresses.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'ip' => array(
],
'ip' => [
'description' => 'IP address',
'type' => 'varchar_ascii',
'length' => 40,
'not null' => TRUE,
'default' => '',
),
),
'indexes' => array(
'ip' => array('ip'),
),
'primary key' => array('iid'),
);
],
],
'indexes' => [
'ip' => ['ip'],
],
'primary key' => ['iid'],
];
return $schema;
}
+2 -2
View File
@@ -15,11 +15,11 @@ function ban_help($route_name, RouteMatchInterface $route_match) {
case 'help.page.ban':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The Ban module allows administrators to ban visits to their site from individual IP addresses. For more information, see the <a href=":url">online documentation for the Ban module</a>.', array(':url' => 'https://www.drupal.org/documentation/modules/ban')) . '</p>';
$output .= '<p>' . t('The Ban module allows administrators to ban visits to their site from individual IP addresses. For more information, see the <a href=":url">online documentation for the Ban module</a>.', [':url' => 'https://www.drupal.org/documentation/modules/ban']) . '</p>';
$output .= '<h3>' . t('Uses') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Banning IP addresses') . '</dt>';
$output .= '<dd>' . t('Administrators can enter IP addresses to ban on the <a href=":bans">IP address bans</a> page.', array(':bans' => \Drupal::url('ban.admin_page'))) . '</dd>';
$output .= '<dd>' . t('Administrators can enter IP addresses to ban on the <a href=":bans">IP address bans</a> page.', [':bans' => \Drupal::url('ban.admin_page')]) . '</dd>';
$output .= '</dl>';
return $output;
+4 -4
View File
@@ -30,7 +30,7 @@ class BanIpManager implements BanIpManagerInterface {
* {@inheritdoc}
*/
public function isBanned($ip) {
return (bool) $this->connection->query("SELECT * FROM {ban_ip} WHERE ip = :ip", array(':ip' => $ip))->fetchField();
return (bool) $this->connection->query("SELECT * FROM {ban_ip} WHERE ip = :ip", [':ip' => $ip])->fetchField();
}
/**
@@ -45,8 +45,8 @@ class BanIpManager implements BanIpManagerInterface {
*/
public function banIp($ip) {
$this->connection->merge('ban_ip')
->key(array('ip' => $ip))
->fields(array('ip' => $ip))
->key(['ip' => $ip])
->fields(['ip' => $ip])
->execute();
}
@@ -63,7 +63,7 @@ class BanIpManager implements BanIpManagerInterface {
* {@inheritdoc}
*/
public function findById($ban_id) {
return $this->connection->query("SELECT ip FROM {ban_ip} WHERE iid = :iid", array(':iid' => $ban_id))->fetchField();
return $this->connection->query("SELECT ip FROM {ban_ip} WHERE iid = :iid", [':iid' => $ban_id])->fetchField();
}
}
+18 -18
View File
@@ -52,47 +52,47 @@ class BanAdmin extends FormBase {
* address form field.
*/
public function buildForm(array $form, FormStateInterface $form_state, $default_ip = '') {
$rows = array();
$header = array($this->t('banned IP addresses'), $this->t('Operations'));
$rows = [];
$header = [$this->t('banned IP addresses'), $this->t('Operations')];
$result = $this->ipManager->findAll();
foreach ($result as $ip) {
$row = array();
$row = [];
$row[] = $ip->ip;
$links = array();
$links['delete'] = array(
$links = [];
$links['delete'] = [
'title' => $this->t('Delete'),
'url' => Url::fromRoute('ban.delete', ['ban_id' => $ip->iid]),
);
$row[] = array(
'data' => array(
];
$row[] = [
'data' => [
'#type' => 'operations',
'#links' => $links,
),
);
],
];
$rows[] = $row;
}
$form['ip'] = array(
$form['ip'] = [
'#title' => $this->t('IP address'),
'#type' => 'textfield',
'#size' => 48,
'#maxlength' => 40,
'#default_value' => $default_ip,
'#description' => $this->t('Enter a valid IP address.'),
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
];
$form['actions'] = ['#type' => 'actions'];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Add'),
);
];
$form['ban_ip_banning_table'] = array(
$form['ban_ip_banning_table'] = [
'#type' => 'table',
'#header' => $header,
'#rows' => $rows,
'#empty' => $this->t('No blocked IP addresses available.'),
'#weight' => 120,
);
];
return $form;
}
@@ -118,7 +118,7 @@ class BanAdmin extends FormBase {
public function submitForm(array &$form, FormStateInterface $form_state) {
$ip = trim($form_state->getValue('ip'));
$this->ipManager->banIp($ip);
drupal_set_message($this->t('The IP address %ip has been banned.', array('%ip' => $ip)));
drupal_set_message($this->t('The IP address %ip has been banned.', ['%ip' => $ip]));
$form_state->setRedirect('ban.admin_page');
}
+3 -3
View File
@@ -58,7 +58,7 @@ class BanDelete extends ConfirmFormBase {
* {@inheritdoc}
*/
public function getQuestion() {
return $this->t('Are you sure you want to unblock %ip?', array('%ip' => $this->banIp));
return $this->t('Are you sure you want to unblock %ip?', ['%ip' => $this->banIp]);
}
/**
@@ -93,8 +93,8 @@ class BanDelete extends ConfirmFormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$this->ipManager->unbanIp($this->banIp);
$this->logger('user')->notice('Deleted %ip', array('%ip' => $this->banIp));
drupal_set_message($this->t('The IP address %ip was deleted.', array('%ip' => $this->banIp)));
$this->logger('user')->notice('Deleted %ip', ['%ip' => $this->banIp]);
drupal_set_message($this->t('The IP address %ip was deleted.', ['%ip' => $this->banIp]));
$form_state->setRedirectUrl($this->getCancelUrl());
}
@@ -76,7 +76,7 @@ class BlockedIP extends DestinationBase implements ContainerFactoryPluginInterfa
/**
* {@inheritdoc}
*/
public function import(Row $row, array $old_destination_id_values = array()) {
public function import(Row $row, array $old_destination_id_values = []) {
$this->banManager->banIp($row->getDestinationProperty('ip'));
}
@@ -18,55 +18,55 @@ class IpAddressBlockingTest extends BrowserTestBase {
*
* @var array
*/
public static $modules = array('ban');
public static $modules = ['ban'];
/**
* Tests various user input to confirm correct validation and saving of data.
*/
function testIPAddressValidation() {
public function testIPAddressValidation() {
// Create user.
$admin_user = $this->drupalCreateUser(array('ban IP addresses'));
$admin_user = $this->drupalCreateUser(['ban IP addresses']);
$this->drupalLogin($admin_user);
$this->drupalGet('admin/config/people/ban');
// Ban a valid IP address.
$edit = array();
$edit = [];
$edit['ip'] = '1.2.3.3';
$this->drupalPostForm('admin/config/people/ban', $edit, t('Add'));
$ip = db_query("SELECT iid from {ban_ip} WHERE ip = :ip", array(':ip' => $edit['ip']))->fetchField();
$ip = db_query("SELECT iid from {ban_ip} WHERE ip = :ip", [':ip' => $edit['ip']])->fetchField();
$this->assertTrue($ip, 'IP address found in database.');
$this->assertRaw(t('The IP address %ip has been banned.', array('%ip' => $edit['ip'])), 'IP address was banned.');
$this->assertRaw(t('The IP address %ip has been banned.', ['%ip' => $edit['ip']]), 'IP address was banned.');
// Try to block an IP address that's already blocked.
$edit = array();
$edit = [];
$edit['ip'] = '1.2.3.3';
$this->drupalPostForm('admin/config/people/ban', $edit, t('Add'));
$this->assertText(t('This IP address is already banned.'));
// Try to block a reserved IP address.
$edit = array();
$edit = [];
$edit['ip'] = '255.255.255.255';
$this->drupalPostForm('admin/config/people/ban', $edit, t('Add'));
$this->assertText(t('Enter a valid IP address.'));
// Try to block a reserved IP address.
$edit = array();
$edit = [];
$edit['ip'] = 'test.example.com';
$this->drupalPostForm('admin/config/people/ban', $edit, t('Add'));
$this->assertText(t('Enter a valid IP address.'));
// Submit an empty form.
$edit = array();
$edit = [];
$edit['ip'] = '';
$this->drupalPostForm('admin/config/people/ban', $edit, t('Add'));
$this->assertText(t('Enter a valid IP address.'));
// Pass an IP address as a URL parameter and submit it.
$submit_ip = '1.2.3.4';
$this->drupalPostForm('admin/config/people/ban/' . $submit_ip, array(), t('Add'));
$ip = db_query("SELECT iid from {ban_ip} WHERE ip = :ip", array(':ip' => $submit_ip))->fetchField();
$this->drupalPostForm('admin/config/people/ban/' . $submit_ip, [], t('Add'));
$ip = db_query("SELECT iid from {ban_ip} WHERE ip = :ip", [':ip' => $submit_ip])->fetchField();
$this->assertTrue($ip, 'IP address found in database');
$this->assertRaw(t('The IP address %ip has been banned.', array('%ip' => $submit_ip)), 'IP address was banned.');
$this->assertRaw(t('The IP address %ip has been banned.', ['%ip' => $submit_ip]), 'IP address was banned.');
// Submit your own IP address. This fails, although it works when testing
// manually.
@@ -85,7 +85,7 @@ class IpAddressBlockingTest extends BrowserTestBase {
$banIp->banIp($ip);
$banIp->banIp($ip);
$query = db_select('ban_ip', 'bip');
$query->fields('bip', array('iid'));
$query->fields('bip', ['iid']);
$query->condition('bip.ip', $ip);
$ip_count = $query->execute()->fetchAll();
$this->assertEqual(1, count($ip_count));
@@ -93,7 +93,7 @@ class IpAddressBlockingTest extends BrowserTestBase {
$banIp->banIp($ip);
$banIp->banIp($ip);
$query = db_select('ban_ip', 'bip');
$query->fields('bip', array('iid'));
$query->fields('bip', ['iid']);
$query->condition('bip.ip', $ip);
$ip_count = $query->execute()->fetchAll();
$this->assertEqual(1, count($ip_count));

Some files were not shown because too many files have changed in this diff Show More