Update to Drupal 8.0.0 beta 14. For more information, see https://drupal.org/node/2544542

This commit is contained in:
Pantheon Automation 2015-08-27 12:03:05 -07:00 committed by Greg Anderson
parent 3b2511d96d
commit 81ccda77eb
2155 changed files with 54307 additions and 46870 deletions

View file

@ -1,14 +0,0 @@
protocols:
- http
- https
- ftp
- news
- nntp
- tel
- telnet
- mailto
- irc
- ssh
- sftp
- webcal
- rtsp

View file

@ -126,17 +126,6 @@ system.diff:
type: integer
label: 'Number of trailing lines in a diff'
system.filter:
type: config_object
label: 'Filter settings'
mapping:
protocols:
type: sequence
label: 'Allowed protocols'
sequence:
type: string
label: 'Protocol'
system.logging:
type: config_object
label: 'Logging settings'

View file

@ -114,7 +114,7 @@ abbr.ajax-changed {
}
/* Inline error messages. */
.form-error-message:before {
.form-item--error-message:before {
content: '';
display: inline-block;
height: 14px;
@ -164,6 +164,19 @@ abbr.ajax-changed {
padding: 1px 20px 1px 0;
}
/**
* Feed Icon.
* Markup generated by feed-icon.html.twig.
*/
.feed-icon {
background: url(../../../misc/feed.png) no-repeat;
overflow: hidden;
text-indent: -9999px;
display: block;
width: 16px;
height: 16px;
}
/**
* Markup generated by pager.html.twig.
*/

View file

@ -3,7 +3,7 @@
* Module page behaviors.
*/
(function ($, Drupal) {
(function ($, Drupal, debounce) {
"use strict";
@ -15,7 +15,7 @@
*
* Text search input: input.table-filter-text
* Target table: input.table-filter-text[data-table]
* Source text: .table-filter-text-source
* Source text: .table-filter-text-source, .module-name, .module-description
*
* @type {Drupal~behavior}
*/
@ -30,17 +30,19 @@
function hidePackageDetails(index, element) {
var $packDetails = $(element);
var $visibleRows = $packDetails.find('table:not(.sticky-header)').find('tbody tr:visible');
var $visibleRows = $packDetails.find('tbody tr:visible');
$packDetails.toggle($visibleRows.length > 0);
}
function filterModuleList(e) {
var query = $(e.target).val().toLowerCase();
var query = $(e.target).val();
// Case insensitive expression to find query at the beginning of a word.
var re = new RegExp('\\b' + query, 'i');
function showModuleRow(index, row) {
var $row = $(row);
var $sources = $row.find('.table-filter-text-source');
var textMatch = $sources.text().toLowerCase().indexOf(query) !== -1;
var $sources = $row.find('.table-filter-text-source, .module-name, .module-description');
var textMatch = $sources.text().search(re) !== -1;
$row.closest('tr').toggle(textMatch);
}
// Search over all rows and packages.
@ -59,6 +61,13 @@
// Hide the package <details> if they don't have any visible rows.
// Note that we first show() all <details> to be able to use ':visible'.
$details.attr('open', true).each(hidePackageDetails);
Drupal.announce(
Drupal.t(
'!modules modules are available in the modified list.',
{'!modules': $rowsAndDetails.find('tbody tr:visible').length}
)
);
}
else if (searching) {
searching = false;
@ -71,14 +80,24 @@
}
}
function preventEnterKey(event) {
if (event.which === 13) {
event.preventDefault();
event.stopPropagation();
}
}
if ($table.length) {
$rowsAndDetails = $table.find('tr, details');
$rows = $table.find('tbody tr');
$details = $rowsAndDetails.filter('.package-listing');
$input.on('keyup', filterModuleList);
$input.on({
keyup: debounce(filterModuleList, 200),
keydown: preventEnterKey
});
}
}
};
}(jQuery, Drupal));
}(jQuery, Drupal, Drupal.debounce));

View file

@ -0,0 +1,21 @@
id: d6_date_formats
label: Drupal 6 date format configuration
migration_tags:
- Drupal 6
source:
plugin: variable_multirow
variables:
- date_format_long
- date_format_medium
- date_format_short
process:
id:
plugin: static_map
source: name
map:
date_format_long: long
date_format_short: short
date_format_medium: medium
pattern: value
destination:
plugin: entity:date_format

View file

@ -0,0 +1,12 @@
id: d6_menu
label: Drupal 6 menus
migration_tags:
- Drupal 6
source:
plugin: d6_menu
process:
id: menu_name
label: title
description: description
destination:
plugin: entity:menu

View file

@ -0,0 +1,16 @@
id: d6_system_cron
label: Drupal 6 cron settings
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- cron_threshold_warning
- cron_threshold_error
- cron_last
process:
'threshold/requirements_warning': cron_threshold_warning
'threshold/requirements_error': cron_threshold_error
destination:
plugin: config
config_name: system.cron

View file

@ -0,0 +1,20 @@
id: d6_system_file
label: Drupal 6 file system configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- file_directory_temp
- allow_insecure_uploads
process:
'path/temporary': file_directory_temp
allow_insecure_uploads:
plugin: static_map
source: allow_insecure_uploads
map:
0: FALSE
1: TRUE
destination:
plugin: config
config_name: system.file

View file

@ -0,0 +1,13 @@
id: d6_system_image
label: Drupal 6 image toolkit configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- image_toolkit
process:
toolkit: image_toolkit
destination:
plugin: config
config_name: system.image

View file

@ -0,0 +1,13 @@
id: d6_system_image_gd
label: Drupal 6 image quality configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- image_jpeg_quality
process:
jpeg_quality: image_jpeg_quality
destination:
plugin: config
config_name: system.image.gd

View file

@ -0,0 +1,21 @@
id: d6_system_logging
label: Drupal 6 system logging
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- error_level
process:
error_level:
plugin: static_map
source: error_level
default_value: all
map:
0: hide
1: some
2: all
3: verbose
destination:
plugin: config
config_name: system.logging

View file

@ -0,0 +1,13 @@
id: d6_system_maintenance
label: Drupal 6 maintenance page configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- site_offline_message
process:
message: site_offline_message
destination:
plugin: config
config_name: system.maintenance

View file

@ -0,0 +1,20 @@
id: d6_system_performance
label: Drupal 6 performance configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- preprocess_css
- preprocess_js
- cache_lifetime
- cache
- page_compression
process:
'css/preprocess': preprocess_css
'js/preprocess': preprocess_js
'cache/page/max_age': cache_lifetime
'response/gzip': page_compression
destination:
plugin: config
config_name: system.performance

View file

@ -0,0 +1,15 @@
id: d6_system_rss
label: Drupal 6 RSS configuration
migration_tags:
- Drupal 6
source:
plugin: variable
variables:
- feed_default_items
- feed_item_length
process:
'items/limit': feed_default_items
'items/view_mode': feed_item_length
destination:
plugin: config
config_name: system.rss

View file

@ -0,0 +1,41 @@
id: d6_system_site
label: Drupal 6 site configuration
migration_tags:
- Drupal 6
source:
plugin: variable
constants:
slash: '/'
variables:
- site_name
- site_mail
- site_slogan
- site_frontpage
- site_403
- site_404
- drupal_weight_select_max
- admin_compact_mode
process:
name: site_name
mail: site_mail
slogan: site_slogan
'page/front':
plugin: concat
source:
- constants/slash
- site_frontpage
'page/403':
plugin: concat
source:
- constants/slash
- site_403
'page/404':
plugin: concat
source:
- constants/slash
- site_404
weight_select_max: drupal_weight_select_max
admin_compact_mode: admin_compact_mode
destination:
plugin: config
config_name: system.site

View file

@ -220,7 +220,7 @@ class DbUpdateController extends ControllerBase {
$info[] = $this->t("<strong>Back up your code</strong>. Hint: when backing up module code, do not leave that backup in the 'modules' or 'sites/*/modules' directories as this may confuse Drupal's auto-discovery mechanism.");
$info[] = $this->t('Put your site into <a href="@url">maintenance mode</a>.', array(
'@url' => $this->url('system.site_maintenance_mode'),
'@url' => Url::fromRoute('system.site_maintenance_mode')->toString(TRUE)->getGeneratedUrl(),
));
$info[] = $this->t('<strong>Back up your database</strong>. This process will change your database values and in case of emergency you may need to revert to a backup.');
$info[] = $this->t('Install your new files in the appropriate location, as described in the handbook.');
@ -388,7 +388,7 @@ class DbUpdateController extends ControllerBase {
$dblog_exists = $this->moduleHandler->moduleExists('dblog');
if ($dblog_exists && $this->account->hasPermission('access site reports')) {
$log_message = $this->t('All errors have been <a href="@url">logged</a>.', array(
'@url' => $this->url('dblog.overview'),
'@url' => Url::fromRoute('dblog.overview')->toString(TRUE)->getGeneratedUrl(),
));
}
else {
@ -396,7 +396,7 @@ class DbUpdateController extends ControllerBase {
}
if (!empty($_SESSION['update_success'])) {
$message = '<p>' . $this->t('Updates were attempted. If you see no failures below, you may proceed happily back to your <a href="@url">site</a>. Otherwise, you may need to update your database manually.', array('@url' => $this->url('<front>'))) . ' ' . $log_message . '</p>';
$message = '<p>' . $this->t('Updates were attempted. If you see no failures below, you may proceed happily back to your <a href="@url">site</a>. Otherwise, you may need to update your database manually.', array('@url' => Url::fromRoute('<front>')->toString(TRUE)->getGeneratedUrl())) . ' ' . $log_message . '</p>';
}
else {
$last = reset($_SESSION['updates_remaining']);
@ -497,7 +497,7 @@ class DbUpdateController extends ControllerBase {
*/
public function requirements($severity, array $requirements) {
$options = $severity == REQUIREMENT_WARNING ? array('continue' => 1) : array();
$try_again_url = $this->url('system.db_update', $options);
$try_again_url = Url::fromRoute('system.db_update', $options)->toString(TRUE)->getGeneratedUrl();
$build['status_report'] = array(
'#theme' => 'status_report',
@ -556,13 +556,6 @@ class DbUpdateController extends ControllerBase {
$operations = array();
// First of all perform entity definition updates, which will update
// storage schema if needed, so that module update functions work with
// the correct entity schema.
if ($this->entityDefinitionUpdateManager->needsUpdates()) {
$operations[] = array('update_entity_definitions', array('system', '0 - Update entity definitions'));
}
// Resolve any update dependencies to determine the actual updates that will
// be run and the order they will be run in.
$start = $this->getModuleUpdates();
@ -578,7 +571,7 @@ class DbUpdateController extends ControllerBase {
}
// Determine updates to be performed.
foreach ($updates as $update) {
foreach ($updates as $function => $update) {
if ($update['allowed']) {
// Set the installed version of each module so updates will start at the
// correct place. (The updates are already sorted, so we can simply base
@ -587,11 +580,20 @@ class DbUpdateController extends ControllerBase {
drupal_set_installed_schema_version($update['module'], $update['number'] - 1);
unset($start[$update['module']]);
}
// Add this update function to the batch.
$function = $update['module'] . '_update_' . $update['number'];
$operations[] = array('update_do_one', array($update['module'], $update['number'], $dependency_map[$function]));
}
}
// Lastly, perform entity definition updates, which will update storage
// schema if needed. If module update functions need to work with specific
// entity schema they should call the entity update service for the specific
// update themselves.
// @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyEntityUpdate()
// @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface::applyFieldUpdate()
if ($this->entityDefinitionUpdateManager->needsUpdates()) {
$operations[] = array('update_entity_definitions', array());
}
$batch['operations'] = $operations;
$batch += array(
'title' => $this->t('Updating'),

View file

@ -1,186 +0,0 @@
<?php
/**
* @file
* Contains \Drupal\system\Controller\FormAjaxController.
*/
namespace Drupal\system\Controller;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Form\FormAjaxResponseBuilderInterface;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form\FormBuilderInterface;
use Drupal\Core\Render\MainContent\MainContentRendererInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\system\FileAjaxForm;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
/**
* Defines a controller to respond to form Ajax requests.
*/
class FormAjaxController implements ContainerInjectionInterface {
/**
* A logger instance.
*
* @var \Psr\Log\LoggerInterface
*/
protected $logger;
/**
* The form builder.
*
* @var \Drupal\Core\Form\FormBuilderInterface|\Drupal\Core\Form\FormCacheInterface
*/
protected $formBuilder;
/**
* The renderer.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* The main content to AJAX Response renderer.
*
* @var \Drupal\Core\Render\MainContent\MainContentRendererInterface
*/
protected $ajaxRenderer;
/**
* The current route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* The form AJAX response builder.
*
* @var \Drupal\Core\Form\FormAjaxResponseBuilderInterface
*/
protected $formAjaxResponseBuilder;
/**
* Constructs a FormAjaxController object.
*
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
* @param \Drupal\Core\Form\FormBuilderInterface $form_builder
* The form builder.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer.
* @param \Drupal\Core\Render\MainContent\MainContentRendererInterface $ajax_renderer
* The main content to AJAX Response renderer.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
* @param \Drupal\Core\Form\FormAjaxResponseBuilderInterface $form_ajax_response_builder
* The form AJAX response builder.
*/
public function __construct(LoggerInterface $logger, FormBuilderInterface $form_builder, RendererInterface $renderer, MainContentRendererInterface $ajax_renderer, RouteMatchInterface $route_match, FormAjaxResponseBuilderInterface $form_ajax_response_builder) {
$this->logger = $logger;
$this->formBuilder = $form_builder;
$this->renderer = $renderer;
$this->ajaxRenderer = $ajax_renderer;
$this->routeMatch = $route_match;
$this->formAjaxResponseBuilder = $form_ajax_response_builder;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('logger.factory')->get('ajax'),
$container->get('form_builder'),
$container->get('renderer'),
$container->get('main_content_renderer.ajax'),
$container->get('current_route_match'),
$container->get('form_ajax_response_builder')
);
}
/**
* Processes an Ajax form submission.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
*
* @return mixed
* Whatever is returned by the triggering element's #ajax['callback']
* function. One of:
* - A render array containing the new or updated content to return to the
* browser. This is commonly an element within the rebuilt form.
* - A \Drupal\Core\Ajax\AjaxResponse object containing commands for the
* browser to process.
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface
*/
public function content(Request $request) {
$ajax_form = $this->getForm($request);
$form = $ajax_form->getForm();
$form_state = $ajax_form->getFormState();
$commands = $ajax_form->getCommands();
$this->formBuilder->processForm($form['#form_id'], $form, $form_state);
return $this->formAjaxResponseBuilder->buildResponse($request, $form, $form_state, $commands);
}
/**
* Gets a form submitted via #ajax during an Ajax callback.
*
* This will load a form from the form cache used during Ajax operations. It
* pulls the form info from the request body.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request object.
*
* @return \Drupal\system\FileAjaxForm
* A wrapper object containing the $form, $form_state, $form_id,
* $form_build_id and an initial list of Ajax $commands.
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface
*/
protected function getForm(Request $request) {
$form_state = new FormState();
$form_build_id = $request->request->get('form_build_id');
// Get the form from the cache.
$form = $this->formBuilder->getCache($form_build_id, $form_state);
if (!$form) {
// If $form cannot be loaded from the cache, the form_build_id must be
// invalid, which means that someone performed a POST request onto
// system/ajax without actually viewing the concerned form in the browser.
// This is likely a hacking attempt as it never happens under normal
// circumstances.
$this->logger->warning('Invalid form POST data.');
throw new BadRequestHttpException();
}
// Since some of the submit handlers are run, redirects need to be disabled.
$form_state->disableRedirect();
// When a form is rebuilt after Ajax processing, its #build_id and #action
// should not change.
// @see \Drupal\Core\Form\FormBuilderInterface::rebuildForm()
$form_state->addRebuildInfo('copy', [
'#build_id' => TRUE,
'#action' => TRUE,
]);
// The form needs to be processed; prepare for that by setting a few
// internal variables.
$form_state->setUserInput($request->request->all());
$form_id = $form['#form_id'];
return new FileAjaxForm($form, $form_state, $form_id, $form['#build_id'], []);
}
}

View file

@ -241,9 +241,10 @@ class SystemController extends ControllerBase {
if (empty($theme->status)) {
// Ensure this theme is compatible with this version of core.
$theme->incompatible_core = !isset($theme->info['core']) || ($theme->info['core'] != \DRUPAL::CORE_COMPATIBILITY);
// Require the 'content' region to make sure the main page
// content has a common place in all themes.
$theme->incompatible_core = !isset($theme->info['core']) || ($theme->info['core'] != \DRUPAL::CORE_COMPATIBILITY) || !isset($theme->info['regions']['content']);
$theme->incompatible_region = !isset($theme->info['regions']['content']);
$theme->incompatible_php = version_compare(phpversion(), $theme->info['php']) < 0;
// Confirmed that the base theme is available.
$theme->incompatible_base = isset($theme->info['base theme']) && !isset($themes[$theme->info['base theme']]);

View file

@ -59,7 +59,6 @@ class DateFormatListBuilder extends ConfigEntityListBuilder {
* {@inheritdoc}
*/
public function buildHeader() {
$header['id'] = t('Machine name');
$header['label'] = t('Name');
$header['pattern'] = t('Pattern');
return $header + parent::buildHeader();
@ -69,12 +68,6 @@ class DateFormatListBuilder extends ConfigEntityListBuilder {
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
if ($entity->isLocked()) {
$row['id'] = $this->t('@entity_id (locked)', array('@entity_id' => $entity->id()));
}
else {
$row['id'] = $entity->id();
}
$row['label'] = $this->getLabel($entity);
$row['pattern'] = $this->dateFormatter->format(REQUEST_TIME, $entity->id());
return $row + parent::buildRow($entity);

View file

@ -126,8 +126,8 @@ abstract class DateFormatFormBase extends EntityForm {
/**
* {@inheritdoc}
*/
public function validate(array $form, FormStateInterface $form_state) {
parent::validate($form, $form_state);
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
// The machine name field should already check to see if the requested
// machine name is available. Regardless of machine_name or human readable

View file

@ -150,16 +150,13 @@ class ModulesListConfirmForm extends ConfirmFormBase {
$account = $this->currentUser()->id();
$this->keyValueExpirable->delete($account);
// Gets list of modules prior to install process.
$before = $this->moduleHandler->getModuleList();
// Install the given modules.
if (!empty($this->modules['install'])) {
// Don't catch the exception that this can throw for missing dependencies:
// the form doesn't allow modules with unmet dependencies, so the only way
// this can happen is if the filesystem changed between form display and
// submit, in which case the user has bigger problems.
try {
// Install the given modules.
$this->moduleInstaller->install(array_keys($this->modules['install']));
}
catch (PreExistingConfigException $e) {
@ -184,12 +181,12 @@ class ModulesListConfirmForm extends ConfirmFormBase {
);
return;
}
}
// Gets module list after install process, flushes caches and displays a
// message if there are changes.
if ($before != $this->moduleHandler->getModuleList()) {
drupal_set_message($this->t('The configuration options have been saved.'));
$module_names = array_values($this->modules['install']);
drupal_set_message($this->formatPlural(count($module_names), 'Module %name has been enabled.', '@count modules have been enabled: %names.', array(
'%name' => $module_names[0],
'%names' => implode(', ', $module_names),
)));
}
$form_state->setRedirectUrl($this->getCancelUrl());

View file

@ -24,6 +24,7 @@ use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\user\PermissionHandlerInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
@ -94,6 +95,13 @@ class ModulesListForm extends FormBase {
*/
protected $moduleInstaller;
/**
* The permission handler.
*
* @var \Drupal\user\PermissionHandlerInterface
*/
protected $permissionHandler;
/**
* {@inheritdoc}
*/
@ -107,7 +115,8 @@ class ModulesListForm extends FormBase {
$container->get('current_route_match'),
$container->get('title_resolver'),
$container->get('router.route_provider'),
$container->get('plugin.manager.menu.link')
$container->get('plugin.manager.menu.link'),
$container->get('user.permissions')
);
}
@ -132,8 +141,10 @@ class ModulesListForm extends FormBase {
* The route provider.
* @param \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager
* The menu link manager.
* @param \Drupal\user\PermissionHandlerInterface $permission_handler
* The permission handler.
*/
public function __construct(ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, KeyValueStoreExpirableInterface $key_value_expirable, AccessManagerInterface $access_manager, AccountInterface $current_user, RouteMatchInterface $route_match, TitleResolverInterface $title_resolver, RouteProviderInterface $route_provider, MenuLinkManagerInterface $menu_link_manager) {
public function __construct(ModuleHandlerInterface $module_handler, ModuleInstallerInterface $module_installer, KeyValueStoreExpirableInterface $key_value_expirable, AccessManagerInterface $access_manager, AccountInterface $current_user, RouteMatchInterface $route_match, TitleResolverInterface $title_resolver, RouteProviderInterface $route_provider, MenuLinkManagerInterface $menu_link_manager, PermissionHandlerInterface $permission_handler) {
$this->moduleHandler = $module_handler;
$this->moduleInstaller = $module_installer;
$this->keyValueExpirable = $key_value_expirable;
@ -143,6 +154,7 @@ class ModulesListForm extends FormBase {
$this->titleResolver = $title_resolver;
$this->routeProvider = $route_provider;
$this->menuLinkManager = $menu_link_manager;
$this->permissionHandler = $permission_handler;
}
/**
@ -171,14 +183,15 @@ class ModulesListForm extends FormBase {
$form['filters']['text'] = array(
'#type' => 'search',
'#title' => $this->t('Search'),
'#title' => $this->t('Filter modules'),
'#title_display' => 'invisible',
'#size' => 30,
'#placeholder' => $this->t('Enter module name'),
'#placeholder' => $this->t('Filter by name or description'),
'#description' => $this->t('Enter a part of the module name or description'),
'#attributes' => array(
'class' => array('table-filter-text'),
'data-table' => '#system-modules',
'autocomplete' => 'off',
'title' => $this->t('Enter a part of the module name or description to filter by.'),
),
);
@ -226,7 +239,7 @@ class ModulesListForm extends FormBase {
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Save configuration'),
'#value' => $this->t('Install'),
'#button_type' => 'primary',
);
@ -270,7 +283,7 @@ class ModulesListForm extends FormBase {
// Generate link for module's permission, if the user has access to it.
$row['links']['permissions'] = array();
if ($module->status && \Drupal::currentUser()->hasPermission('administer permissions') && in_array($module->getName(), $this->moduleHandler->getImplementations('permission'))) {
if ($module->status && $this->currentUser->hasPermission('administer permissions') && $this->permissionHandler->moduleProvidesPermissions($module->getName())) {
$row['links']['permissions'] = array(
'#type' => 'link',
'#title' => $this->t('Permissions'),
@ -503,13 +516,15 @@ class ModulesListForm extends FormBase {
return;
}
// Gets list of modules prior to install process.
$before = $this->moduleHandler->getModuleList();
// There seem to be no dependencies that would need approval.
// Install the given modules.
if (!empty($modules['install'])) {
try {
$this->moduleInstaller->install(array_keys($modules['install']));
$module_names = array_values($modules['install']);
drupal_set_message($this->formatPlural(count($module_names), 'Module %name has been enabled.', '@count modules have been enabled: %names.', array(
'%name' => $module_names[0],
'%names' => implode(', ', $module_names),
)));
}
catch (PreExistingConfigException $e) {
$config_objects = $e->flattenConfigObjects($e->getConfigObjects());
@ -534,12 +549,6 @@ class ModulesListForm extends FormBase {
return;
}
}
// Gets module list after install process, flushes caches and displays a
// message if there are changes.
if ($before != $this->moduleHandler->getModuleList()) {
drupal_set_message(t('The configuration options have been saved.'));
}
}
}

View file

@ -84,7 +84,7 @@ class ModulesUninstallForm extends FormBase {
// Get a list of all available modules.
$modules = system_rebuild_module_data();
$uninstallable = array_filter($modules, function ($module) use ($modules) {
return empty($modules[$module->getName()]->info['required']) && drupal_get_installed_schema_version($module->getName()) > SCHEMA_UNINSTALLED;
return empty($modules[$module->getName()]->info['required']) && $module->status;
});
// Include system.admin.inc so we can use the sort callbacks.
@ -99,14 +99,15 @@ class ModulesUninstallForm extends FormBase {
$form['filters']['text'] = array(
'#type' => 'search',
'#title' => $this->t('Search'),
'#title' => $this->t('Filter modules'),
'#title_display' => 'invisible',
'#size' => 30,
'#placeholder' => $this->t('Enter module name'),
'#placeholder' => $this->t('Filter by name or description'),
'#description' => $this->t('Enter a part of the module name or description'),
'#attributes' => array(
'class' => array('table-filter-text'),
'data-table' => '#system-modules-uninstall',
'autocomplete' => 'off',
'title' => $this->t('Enter a part of the module name or description to filter by.'),
),
);

View file

@ -67,7 +67,9 @@ class MachineNameController implements ContainerInjectionInterface {
$transliterated = Unicode::strtolower($transliterated);
}
if(isset($replace_pattern) && isset($replace)) {
$transliterated = preg_replace('@' . $replace_pattern . '@', $replace, $transliterated);
// Quote the pattern delimiter and remove null characters to avoid the e
// or other modifiers being injected.
$transliterated = preg_replace('@' . strtr($replace_pattern, ['@' => '\@', chr(0) => '']) . '@', $replace, $transliterated);
}
return new JsonResponse($transliterated);
}

View file

@ -7,6 +7,7 @@
namespace Drupal\system\Plugin\Block;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Config\ConfigFactoryInterface;
@ -173,7 +174,7 @@ class SystemBrandingBlock extends BlockBase implements ContainerFactoryPluginInt
);
$build['site_slogan'] = array(
'#markup' => Xss::filterAdmin($site_config->get('slogan')),
'#markup' => $site_config->get('slogan'),
'#access' => $this->configuration['use_site_slogan'],
);

View file

@ -123,4 +123,13 @@ class CurrentThemeCondition extends ConditionPluginBase implements ContainerFact
return $this->t('The current theme is @theme', array('@theme' => $this->configuration['theme']));
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
$contexts = parent::getCacheContexts();
$contexts[] = 'theme';
return $contexts;
}
}

View file

@ -112,8 +112,8 @@ class RequestPath extends ConditionPluginBase implements ContainerFactoryPluginI
'#title' => $this->t('Pages'),
'#default_value' => $this->configuration['pages'],
'#description' => $this->t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %user for the current user's page and %user-wildcard for every user page. %front is the front page.", array(
'%user' => 'user',
'%user-wildcard' => 'user/*',
'%user' => '/user',
'%user-wildcard' => '/user/*',
'%front' => '<front>',
)),
);
@ -159,4 +159,15 @@ class RequestPath extends ConditionPluginBase implements ContainerFactoryPluginI
return $this->pathMatcher->matchPath($path_alias, $pages) || (($path != $path_alias) && $this->pathMatcher->matchPath($path, $pages));
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
$contexts = parent::getCacheContexts();
// @todo Add a url.path cache context in
// https://www.drupal.org/node/2521978.
$contexts[] = 'url';
return $contexts;
}
}

View file

@ -9,8 +9,12 @@ namespace Drupal\system\Plugin\ImageToolkit;
use Drupal\Component\Utility\Color;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\ImageToolkit\ImageToolkitBase;
use Drupal\Core\ImageToolkit\ImageToolkitOperationManagerInterface;
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
@ -53,6 +57,36 @@ class GDToolkit extends ImageToolkitBase {
*/
protected $preLoadInfo = NULL;
/**
* The StreamWrapper manager.
*
* @var \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface
*/
protected $streamWrapperManager;
/**
* Constructs a TestToolkit object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\ImageToolkit\ImageToolkitOperationManagerInterface $operation_manager
* The toolkit operation manager.
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
* The StreamWrapper manager.
*/
public function __construct(array $configuration, $plugin_id, array $plugin_definition, ImageToolkitOperationManagerInterface $operation_manager, LoggerInterface $logger, ConfigFactoryInterface $config_factory, StreamWrapperManagerInterface $stream_wrapper_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $operation_manager, $logger, $config_factory);
$this->streamWrapperManager = $stream_wrapper_manager;
}
/**
* {@inheritdoc}
*/
@ -63,7 +97,8 @@ class GDToolkit extends ImageToolkitBase {
$plugin_definition,
$container->get('image.toolkit.operation.manager'),
$container->get('logger.channel.image'),
$container->get('config.factory')
$container->get('config.factory'),
$container->get('stream_wrapper_manager')
);
}
@ -172,7 +207,7 @@ class GDToolkit extends ImageToolkitBase {
// Work around lack of stream wrapper support in imagejpeg() and imagepng().
if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
// If destination is not local, save image to temporary local file.
$local_wrappers = file_get_stream_wrappers(StreamWrapperInterface::LOCAL);
$local_wrappers = $this->streamWrapperManager->getWrappers(StreamWrapperInterface::LOCAL);
if (!isset($local_wrappers[$scheme])) {
$permanent_destination = $destination;
$destination = drupal_tempnam('temporary://', 'gd_');

View file

@ -7,8 +7,6 @@
namespace Drupal\system\Plugin\ImageToolkit\Operation\gd;
use Drupal\Component\Utility\SafeMarkup;
/**
* Defines GD2 convert operation.
*
@ -38,7 +36,7 @@ class Convert extends GDImageToolkitOperationBase {
*/
protected function validateArguments(array $arguments) {
if (!in_array($arguments['extension'], $this->getToolkit()->getSupportedExtensions())) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid extension (@value) specified for the image 'convert' operation", array('@value' => $arguments['extension'])));
throw new \InvalidArgumentException("Invalid extension ({$arguments['extension']}) specified for the image 'convert' operation");
}
return $arguments;
}

View file

@ -8,7 +8,6 @@
namespace Drupal\system\Plugin\ImageToolkit\Operation\gd;
use Drupal\Component\Utility\Color;
use Drupal\Component\Utility\SafeMarkup;
/**
* Defines GD2 create_new image operation.
@ -53,7 +52,7 @@ class CreateNew extends GDImageToolkitOperationBase {
protected function validateArguments(array $arguments) {
// Assure extension is supported.
if (!in_array($arguments['extension'], $this->getToolkit()->getSupportedExtensions())) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid extension (@value) specified for the image 'convert' operation", array('@value' => $arguments['extension'])));
throw new \InvalidArgumentException("Invalid extension ('{$arguments['extension']}') specified for the image 'convert' operation");
}
// Assure integers for width and height.
@ -62,15 +61,15 @@ class CreateNew extends GDImageToolkitOperationBase {
// Fail when width or height are 0 or negative.
if ($arguments['width'] <= 0) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid width (@value) specified for the image 'create_new' operation", array('@value' => $arguments['width'])));
throw new \InvalidArgumentException("Invalid width ('{$arguments['width']}') specified for the image 'create_new' operation");
}
if ($arguments['height'] <= 0) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid height (@value) specified for the image 'create_new' operation", array('@value' => $arguments['height'])));
throw new \InvalidArgumentException("Invalid height ({$arguments['height']}) specified for the image 'create_new' operation");
}
// Assure transparent color is a valid hex string.
if ($arguments['transparent_color'] && !Color::validateHex($arguments['transparent_color'])) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid transparent color (@value) specified for the image 'create_new' operation", array('@value' => $arguments['transparent_color'])));
throw new \InvalidArgumentException("Invalid transparent color ({$arguments['transparent_color']}) specified for the image 'create_new' operation");
}
return $arguments;

View file

@ -7,8 +7,6 @@
namespace Drupal\system\Plugin\ImageToolkit\Operation\gd;
use Drupal\Component\Utility\SafeMarkup;
/**
* Defines GD2 Crop operation.
*
@ -67,10 +65,10 @@ class Crop extends GDImageToolkitOperationBase {
// Fail when width or height are 0 or negative.
if ($arguments['width'] <= 0) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid width (@value) specified for the image 'crop' operation", array('@value' => $arguments['width'])));
throw new \InvalidArgumentException("Invalid width ('{$arguments['width']}') specified for the image 'crop' operation");
}
if ($arguments['height'] <= 0) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid height (@value) specified for the image 'crop' operation", array('@value' => $arguments['height'])));
throw new \InvalidArgumentException("Invalid height ('{$arguments['height']}') specified for the image 'crop' operation");
}
return $arguments;

View file

@ -7,8 +7,6 @@
namespace Drupal\system\Plugin\ImageToolkit\Operation\gd;
use Drupal\Component\Utility\SafeMarkup;
/**
* Defines GD2 resize operation.
*
@ -46,10 +44,10 @@ class Resize extends GDImageToolkitOperationBase {
// Fail when width or height are 0 or negative.
if ($arguments['width'] <= 0) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid width (@value) specified for the image 'resize' operation", array('@value' => $arguments['width'])));
throw new \InvalidArgumentException("Invalid width ('{$arguments['width']}') specified for the image 'resize' operation");
}
if ($arguments['height'] <= 0) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid height (@value) specified for the image 'resize' operation", array('@value' => $arguments['height'])));
throw new \InvalidArgumentException("Invalid height ('{$arguments['height']}') specified for the image 'resize' operation");
}
return $arguments;

View file

@ -7,8 +7,6 @@
namespace Drupal\system\Plugin\ImageToolkit\Operation\gd;
use Drupal\Component\Utility\SafeMarkup;
/**
* Defines GD2 Scale operation.
*
@ -73,10 +71,10 @@ class Scale extends Resize {
// Fail when width or height are 0 or negative.
if ($arguments['width'] <= 0) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid width (@value) specified for the image 'scale' operation", array('@value' => $arguments['width'])));
throw new \InvalidArgumentException("Invalid width ('{$arguments['width']}') specified for the image 'scale' operation");
}
if ($arguments['height'] <= 0) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid height (@value) specified for the image 'scale' operation", array('@value' => $arguments['height'])));
throw new \InvalidArgumentException("Invalid height ('{$arguments['height']}') specified for the image 'scale' operation");
}
return $arguments;

View file

@ -7,8 +7,6 @@
namespace Drupal\system\Plugin\ImageToolkit\Operation\gd;
use Drupal\Component\Utility\SafeMarkup;
/**
* Defines GD2 Scale and crop operation.
*
@ -54,10 +52,10 @@ class ScaleAndCrop extends GDImageToolkitOperationBase {
// Fail when width or height are 0 or negative.
if ($arguments['width'] <= 0) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid width (@value) specified for the image 'scale_and_crop' operation", array('@value' => $arguments['width'])));
throw new \InvalidArgumentException("Invalid width ('{$arguments['width']}') specified for the image 'scale_and_crop' operation");
}
if ($arguments['height'] <= 0) {
throw new \InvalidArgumentException(SafeMarkup::format("Invalid height (@value) specified for the image 'scale_and_crop' operation", array('@value' => $arguments['height'])));
throw new \InvalidArgumentException("Invalid height ('{$arguments['height']}') specified for the image 'scale_and_crop' operation");
}
return $arguments;

View file

@ -0,0 +1,43 @@
<?php
/**
* @file
* Contains \Drupal\system\Plugin\migrate\process\d6\SystemUpdate7000.
*/
namespace Drupal\system\Plugin\migrate\process\d6;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
/**
* Rename blog and forum permissions to be consistent with other content types.
*
* @MigrateProcessPlugin(
* id = "system_update_7000"
* )
*/
class SystemUpdate7000 extends ProcessPluginBase {
/**
* {@inheritdoc}
*
* Rename blog and forum permissions to be consistent with other content types.
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
$value = preg_replace('/(?<=^|,\ )create\ blog\ entries(?=,|$)/', 'create blog content', $value);
$value = preg_replace('/(?<=^|,\ )edit\ own\ blog\ entries(?=,|$)/', 'edit own blog content', $value);
$value = preg_replace('/(?<=^|,\ )edit\ any\ blog\ entry(?=,|$)/', 'edit any blog content', $value);
$value = preg_replace('/(?<=^|,\ )delete\ own\ blog\ entries(?=,|$)/', 'delete own blog content', $value);
$value = preg_replace('/(?<=^|,\ )delete\ any\ blog\ entry(?=,|$)/', 'delete any blog content', $value);
$value = preg_replace('/(?<=^|,\ )create\ forum\ topics(?=,|$)/', 'create forum content', $value);
$value = preg_replace('/(?<=^|,\ )delete\ any\ forum\ topic(?=,|$)/', 'delete any forum content', $value);
$value = preg_replace('/(?<=^|,\ )delete\ own\ forum\ topics(?=,|$)/', 'delete own forum content', $value);
$value = preg_replace('/(?<=^|,\ )edit\ any\ forum\ topic(?=,|$)/', 'edit any forum content', $value);
$value = preg_replace('/(?<=^|,\ )edit\ own\ forum\ topics(?=,|$)/', 'edit own forum content', $value);
return $value;
}
}

View file

@ -0,0 +1,50 @@
<?php
/**
* @file
* Contains \Drupal\system\Plugin\migrate\source\d6\Menu.
*/
namespace Drupal\system\Plugin\migrate\source\d6;
use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
/**
* Drupal 6 menu source from database.
*
* @MigrateSource(
* id = "d6_menu",
* source_provider = "menu"
* )
*/
class Menu extends DrupalSqlBase {
/**
* {@inheritdoc}
*/
public function query() {
$query = $this->select('menu_custom', 'm')
->fields('m', array('menu_name', 'title', 'description'));
return $query;
}
/**
* {@inheritdoc}
*/
public function fields() {
return array(
'menu_name' => $this->t('The menu name. Primary key.'),
'title' => $this->t('The human-readable name of the menu.'),
'description' => $this->t('A description of the menu'),
);
}
/**
* {@inheritdoc}
*/
public function getIds() {
$ids['menu_name']['type'] = 'string';
return $ids;
}
}

View file

@ -95,7 +95,7 @@ class SystemManager {
/**
* Checks for requirement severity.
*
* @return boolean
* @return bool
* Returns the status of the system.
*/
public function checkRequirements() {

View file

@ -37,15 +37,6 @@ class AjaxFormCacheTest extends AjaxTestBase {
// The number of cache entries should not have changed.
$this->assertEqual(0, count($key_value_expirable->getAll()));
// Visit a form that is explicitly cached, 3 times.
$cached_form_url = Url::fromRoute('ajax_forms_test.cached_form');
$this->drupalGet($cached_form_url);
$this->drupalGet($cached_form_url);
$this->drupalGet($cached_form_url);
// The number of cache entries should be exactly 3.
$this->assertEqual(3, count($key_value_expirable->getAll()));
}
/**

View file

@ -35,7 +35,7 @@ class AjaxFormPageCacheTest extends AjaxTestBase {
}
/**
* Create a simple form, then POST to system/ajax to change to it.
* Create a simple form, then submit the form via AJAX to change to it.
*/
public function testSimpleAJAXFormValue() {
$this->drupalGet('ajax_forms_test_get_form');

View file

@ -94,6 +94,10 @@ class DialogTest extends AjaxTestBase {
$this->drupalGet('ajax-test/dialog-contents');
$this->assertRaw($dialog_contents, 'Non-JS modal dialog page present.');
// Check that requesting a modal dialog with XMLHttpRequest goes to a page.
$this->drupalGetXHR('ajax-test/dialog-contents');
$this->assertRaw($dialog_contents, 'Modal dialog page on XMLHttpRequest present.');
// Emulate going to the JS version of the page and check the JSON response.
$ajax_result = $this->drupalGetAjax('ajax-test/dialog-contents', array('query' => array(MainContentViewSubscriber::WRAPPER_FORMAT => 'drupal_modal')));
$this->assertEqual($modal_expected_response, $ajax_result[3], 'Modal dialog JSON response matches.');

View file

@ -0,0 +1,32 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Bootstrap\ErrorContainer.
*/
namespace Drupal\system\Tests\Bootstrap;
use Drupal\Core\DependencyInjection\Container;
/**
* Container base class which triggers an error.
*/
class ErrorContainer extends Container {
/**
* {@inheritdoc}
*/
public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) {
if ($id === 'http_kernel') {
// Enforce a recoverable error.
$callable = function(ErrorContainer $container) {
};
$callable(1);
}
else {
return parent::get($id, $invalidBehavior);
}
}
}

View file

@ -0,0 +1,29 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Bootstrap\ExceptionContainer.
*/
namespace Drupal\system\Tests\Bootstrap;
use Drupal\Core\DependencyInjection\Container;
/**
* Base container which throws an exception.
*/
class ExceptionContainer extends Container {
/**
* {@inheritdoc}
*/
public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) {
if ($id === 'http_kernel') {
throw new \Exception('Thrown exception during Container::get');
}
else {
return parent::get($id, $invalidBehavior);
}
}
}

View file

@ -7,6 +7,7 @@
namespace Drupal\system\Tests\Cache;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Url;
/**
@ -89,8 +90,8 @@ trait AssertPageCacheContextsAndTagsTrait {
*/
protected function debugCacheTags(array $actual_tags, array $expected_tags) {
if ($actual_tags !== $expected_tags) {
debug('Missing cache tags: ' . implode(',', array_diff($expected_tags, $actual_tags)));
debug('Unwanted cache tags: ' . implode(',', array_diff($actual_tags, $expected_tags)));
debug('Unwanted cache tags in response: ' . implode(',', array_diff($actual_tags, $expected_tags)));
debug('Missing cache tags in response: ' . implode(',', array_diff($expected_tags, $actual_tags)));
}
}
@ -99,8 +100,14 @@ trait AssertPageCacheContextsAndTagsTrait {
*
* @param string[] $expected_tags
* The expected tags.
* @param bool $include_default_tags
* (optional) Whether the default cache tags should be included.
*/
protected function assertCacheTags(array $expected_tags) {
protected function assertCacheTags(array $expected_tags, $include_default_tags = TRUE) {
// The anonymous role cache tag is only added if the user is anonymous.
if ($include_default_tags && \Drupal::currentUser()->isAnonymous()) {
$expected_tags = Cache::mergeTags($expected_tags, ['config:user.role.anonymous']);
}
$actual_tags = $this->getCacheHeaderValues('X-Drupal-Cache-Tags');
sort($expected_tags);
sort($actual_tags);
@ -115,18 +122,30 @@ trait AssertPageCacheContextsAndTagsTrait {
* The expected cache contexts.
* @param string $message
* (optional) A verbose message to output.
* @param bool $include_default_contexts
* (optional) Whether the default contexts should automatically be included.
*
* @return
* TRUE if the assertion succeeded, FALSE otherwise.
*/
protected function assertCacheContexts(array $expected_contexts, $message = NULL) {
protected function assertCacheContexts(array $expected_contexts, $message = NULL, $include_default_contexts = TRUE) {
if ($include_default_contexts) {
$default_contexts = ['languages:language_interface', 'theme'];
// Add the user.permission context to the list of default contexts except
// when user is already there.
if (!in_array('user', $expected_contexts)) {
$default_contexts[] = 'user.permissions';
}
$expected_contexts = Cache::mergeContexts($expected_contexts, $default_contexts);
}
$actual_contexts = $this->getCacheHeaderValues('X-Drupal-Cache-Contexts');
sort($expected_contexts);
sort($actual_contexts);
$return = $this->assertIdentical($actual_contexts, $expected_contexts, $message);
if (!$return) {
debug('Missing cache contexts: ' . implode(',', array_diff($actual_contexts, $expected_contexts)));
debug('Unwanted cache contexts: ' . implode(',', array_diff($expected_contexts, $actual_contexts)));
debug('Unwanted cache contexts in response: ' . implode(',', array_diff($actual_contexts, $expected_contexts)));
debug('Missing cache contexts in response: ' . implode(',', array_diff($expected_contexts, $actual_contexts)));
}
return $return;
}

View file

@ -0,0 +1,124 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Cache\CacheContextOptimizationTest.
*/
namespace Drupal\system\Tests\Cache;
use Drupal\simpletest\KernelTestBase;
use Drupal\simpletest\UserCreationTrait;
use Drupal\user\Entity\Role;
/**
* Tests the cache context optimization.
*
* @group Render
*/
class CacheContextOptimizationTest extends KernelTestBase {
use UserCreationTrait;
/**
* Modules to enable.
*
* @var string[]
*/
public static $modules = ['user', 'system'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installEntitySchema('user');
$this->installConfig(['user']);
$this->installSchema('system', ['sequences']);
}
/**
* Ensures that 'user.permissions' cache context is able to define cache tags.
*/
public function testUserPermissionCacheContextOptimization() {
$user1 = $this->createUser();
$this->assertEqual($user1->id(), 1);
$authenticated_user = $this->createUser(['administer permissions']);
$role = $authenticated_user->getRoles()[1];
$test_element = [
'#cache' => [
'keys' => ['test'],
'contexts' => ['user', 'user.permissions'],
],
];
\Drupal::service('account_switcher')->switchTo($authenticated_user);
$element = $test_element;
$element['#markup'] = 'content for authenticated users';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'content for authenticated users');
// Verify that the render caching is working so that other tests can be
// trusted.
$element = $test_element;
$element['#markup'] = 'this should not be visible';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'content for authenticated users');
// Even though the cache contexts have been optimized to only include 'user'
// cache context, the element should have been changed because
// 'user.permissions' cache context defined a cache tags for permission
// changes, which should have bubbled up for the element when it was
// optimized away.
Role::load($role)
->revokePermission('administer permissions')
->save();
$element = $test_element;
$element['#markup'] = 'this should be visible';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'this should be visible');
}
/**
* Ensures that 'user.roles' still works when it is optimized away.
*/
public function testUserRolesCacheContextOptimization() {
$root_user = $this->createUser();
$this->assertEqual($root_user->id(), 1);
$authenticated_user = $this->createUser(['administer permissions']);
$role = $authenticated_user->getRoles()[1];
$test_element = [
'#cache' => [
'keys' => ['test'],
'contexts' => ['user', 'user.roles'],
],
];
\Drupal::service('account_switcher')->switchTo($authenticated_user);
$element = $test_element;
$element['#markup'] = 'content for authenticated users';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'content for authenticated users');
// Verify that the render caching is working so that other tests can be
// trusted.
$element = $test_element;
$element['#markup'] = 'this should not be visible';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'content for authenticated users');
// Even though the cache contexts have been optimized to only include 'user'
// cache context, the element should have been changed because 'user.roles'
// cache context defined a cache tag for user entity changes, which should
// have bubbled up for the element when it was optimized away.
$authenticated_user->removeRole($role);
$authenticated_user->save();
$element = $test_element;
$element['#markup'] = 'this should be visible';
$output = \Drupal::service('renderer')->renderRoot($element);
$this->assertEqual($output, 'this should be visible');
}
}

View file

@ -91,7 +91,6 @@ class AddFeedTest extends WebTestBase {
'#title' => '<>&"\'',
);
$text = \Drupal::service('renderer')->renderRoot($variables);
preg_match('/title="(.*?)"/', $text, $matches);
$this->assertEqual($matches[1], 'Subscribe to &amp;&quot;&#039;', 'feed_icon template escapes reserved HTML characters.');
$this->assertEqual(trim(strip_tags($text)), 'Subscribe to &lt;&gt;&amp;&quot;&#039;', 'feed_icon template escapes reserved HTML characters.');
}
}

View file

@ -215,7 +215,6 @@ class AttachedAssetsTest extends KernelTestBase {
// Test whether the settings for core/drupalSettings are available.
$this->assertTrue(isset($parsed_settings['path']['baseUrl']), 'drupalSettings.path.baseUrl is present.');
$this->assertTrue(isset($parsed_settings['path']['scriptPath']), 'drupalSettings.path.scriptPath is present.');
$this->assertIdentical($parsed_settings['path']['pathPrefix'], 'yarhar', 'drupalSettings.path.pathPrefix is present and has the correct (overridden) value.');
$this->assertIdentical($parsed_settings['path']['currentPath'], '', 'drupalSettings.path.currentPath is present and has the correct value.');
$this->assertIdentical($parsed_settings['path']['currentPathIsAdmin'], FALSE, 'drupalSettings.path.currentPathIsAdmin is present and has the correct value.');
@ -287,8 +286,9 @@ class AttachedAssetsTest extends KernelTestBase {
$js = $this->assetResolver->getJsAssets($assets, FALSE)[1];
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array);
$this->assertTrue(strpos($rendered_js, 'core/assets/vendor/backbone/backbone-min.js?v=1.1.2') > 0 && strpos($rendered_js, 'core/assets/vendor/domready/ready.min.js?v=1.0.8') > 0 , 'JavaScript version identifiers correctly appended to URLs');
$this->assertTrue(strpos($rendered_js, 'core/assets/vendor/backbone/backbone-min.js?v=1.2.1') > 0 && strpos($rendered_js, 'core/assets/vendor/domready/ready.min.js?v=1.0.8') > 0 , 'JavaScript version identifiers correctly appended to URLs');
}
/**

View file

@ -0,0 +1,116 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Common\EarlyRenderingControllerTest.
*/
namespace Drupal\system\Tests\Common;
use Drupal\Core\Render\Element;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
/**
* Verifies that bubbleable metadata of early rendering is not lost.
*
* @group Common
*/
class EarlyRenderingControllerTest extends WebTestBase {
/**
* {@inheritdoc}
*/
protected $dumpHeaders = TRUE;
/**
* {@inheritdoc}
*/
public static $modules = ['system', 'early_rendering_controller_test'];
/**
* Tests theme preprocess functions being able to attach assets.
*/
function testEarlyRendering() {
// Render array: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.render_array'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.render_array.early'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertCacheTag('foo');
// AjaxResponse: non-early & early.
// @todo Add cache tags assertion when AjaxResponse is made cacheable in
// https://www.drupal.org/node/956186.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.ajax_response'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.ajax_response.early'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
// Basic Response object: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response.early'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertNoCacheTag('foo');
// Response object with attachments: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response-with-attachments'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.response-with-attachments.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\AttachmentsTestResponse.');
// Cacheable Response object: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-response'));
$this->assertResponse(200);
$this->assertRaw('Hello world!');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-response.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\CacheableTestResponse.');
// Basic domain object: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object'));
$this->assertResponse(200);
$this->assertRaw('TestDomainObject');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object.early'));
$this->assertResponse(200);
$this->assertRaw('TestDomainObject');
$this->assertNoCacheTag('foo');
// Basic domain object with attachments: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object-with-attachments'));
$this->assertResponse(200);
$this->assertRaw('AttachmentsTestDomainObject');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.domain-object-with-attachments.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\AttachmentsTestDomainObject.');
// Cacheable Response object: non-early & early.
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-domain-object'));
$this->assertResponse(200);
$this->assertRaw('CacheableTestDomainObject');
$this->assertNoCacheTag('foo');
$this->drupalGet(Url::fromRoute('early_rendering_controller_test.cacheable-domain-object.early'));
$this->assertResponse(500);
$this->assertRaw('The controller result claims to be providing relevant cache metadata, but leaked metadata was detected. Please ensure you are not rendering content too early. Returned object class: Drupal\early_rendering_controller_test\CacheableTestDomainObject.');
// The exceptions are expected. Do not interpret them as a test failure.
// Not using File API; a potential error must trigger a PHP warning.
unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log');
}
}

View file

@ -57,7 +57,8 @@ class NoJavaScriptAnonymousTest extends WebTestBase {
*/
protected function assertNoJavaScriptExceptHtml5Shiv() {
// Ensure drupalSettings is not set.
$this->assertNoRaw('var drupalSettings = {', 'drupalSettings is not set.');
$settings = $this->getDrupalSettings();
$this->assertTrue(empty($settings), 'drupalSettings is not set.');
// Ensure the HTML5 shiv exists.
$this->assertRaw('html5shiv/html5shiv.min.js', 'HTML5 shiv JavaScript exists.');

View file

@ -43,7 +43,7 @@ class RenderElementTypesTest extends KernelTestBase {
* Assertion message.
*/
protected function assertElements(array $elements, $expected_html, $message) {
$actual_html = \Drupal::service('renderer')->renderRoot($elements);
$actual_html = (string) \Drupal::service('renderer')->renderRoot($elements);
$out = '<table><tr>';
$out .= '<td valign="top"><pre>' . SafeMarkup::checkPlain($expected_html) . '</pre></td>';

View file

@ -10,6 +10,7 @@ namespace Drupal\system\Tests\Common;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Language\Language;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Url;
use Drupal\simpletest\WebTestBase;
@ -43,21 +44,21 @@ class UrlTest extends WebTestBase {
}
/**
* Tests that #type=link bubbles outbound route/path processors' cacheability.
* Tests that #type=link bubbles outbound route/path processors' metadata.
*/
function testLinkCacheability() {
function testLinkBubbleableMetadata() {
$cases = [
['Regular link', 'internal:/user', [], ['contexts' => [], 'tags' => [], 'max-age' => Cache::PERMANENT]],
['Regular link, absolute', 'internal:/user', ['absolute' => TRUE], ['contexts' => ['url.site'], 'tags' => [], 'max-age' => Cache::PERMANENT]],
['Route processor link', 'route:system.run_cron', [], ['contexts' => [], 'tags' => [], 'max-age' => 0]],
['Route processor link, absolute', 'route:system.run_cron', ['absolute' => TRUE], ['contexts' => ['url.site'], 'tags' => [], 'max-age' => 0]],
['Path processor link', 'internal:/user/1', [], ['contexts' => [], 'tags' => ['user:1'], 'max-age' => Cache::PERMANENT]],
['Path processor link, absolute', 'internal:/user/1', ['absolute' => TRUE], ['contexts' => ['url.site'], 'tags' => ['user:1'], 'max-age' => Cache::PERMANENT]],
['Regular link', 'internal:/user', [], ['contexts' => [], 'tags' => [], 'max-age' => Cache::PERMANENT], []],
['Regular link, absolute', 'internal:/user', ['absolute' => TRUE], ['contexts' => ['url.site'], 'tags' => [], 'max-age' => Cache::PERMANENT], []],
['Route processor link', 'route:system.run_cron', [], ['contexts' => ['session'], 'tags' => [], 'max-age' => Cache::PERMANENT], ['placeholders' => []]],
['Route processor link, absolute', 'route:system.run_cron', ['absolute' => TRUE], ['contexts' => ['url.site', 'session'], 'tags' => [], 'max-age' => Cache::PERMANENT], ['placeholders' => []]],
['Path processor link', 'internal:/user/1', [], ['contexts' => [], 'tags' => ['user:1'], 'max-age' => Cache::PERMANENT], []],
['Path processor link, absolute', 'internal:/user/1', ['absolute' => TRUE], ['contexts' => ['url.site'], 'tags' => ['user:1'], 'max-age' => Cache::PERMANENT], []],
];
foreach ($cases as $case) {
list($title, $uri, $options, $expected_cacheability) = $case;
$expected_cacheability['contexts'] = Cache::mergeContexts($expected_cacheability['contexts'], ['languages:language_interface', 'theme']);
list($title, $uri, $options, $expected_cacheability, $expected_attachments) = $case;
$expected_cacheability['contexts'] = Cache::mergeContexts($expected_cacheability['contexts'], ['languages:language_interface', 'theme', 'user.permissions']);
$link = [
'#type' => 'link',
'#title' => $title,
@ -67,6 +68,7 @@ class UrlTest extends WebTestBase {
\Drupal::service('renderer')->renderRoot($link);
$this->pass($title);
$this->assertEqual($expected_cacheability, $link['#cache']);
$this->assertEqual($expected_attachments, $link['#attached']);
}
}
@ -168,9 +170,11 @@ class UrlTest extends WebTestBase {
$l = \Drupal::l('foo', Url::fromUri('https://www.drupal.org'));
// Test a renderable array passed to _l().
$renderable_text = array('#markup' => 'foo');
$l_renderable_text = \Drupal::l($renderable_text, Url::fromUri('https://www.drupal.org'));
$this->assertEqual($l_renderable_text, $l);
$renderer->executeInRenderContext(new RenderContext(), function() use ($renderer, $l) {
$renderable_text = array('#markup' => 'foo');
$l_renderable_text = \Drupal::l($renderable_text, Url::fromUri('https://www.drupal.org'));
$this->assertEqual($l_renderable_text, $l);
});
// Test a themed link with plain text 'text'.
$type_link_plain_array = array(

View file

@ -138,4 +138,23 @@ class ConnectionTest extends DatabaseTestBase {
}
}
/**
* Test the escapeTable(), escapeField() and escapeAlias() methods with all possible reserved words in PostgreSQL.
*/
public function testPostgresqlReservedWords() {
if (Database::getConnection()->databaseType() !== 'pgsql') {
return;
}
$db = Database::getConnection('default', 'default');
$stmt = $db->query("SELECT word FROM pg_get_keywords() WHERE catcode IN ('R', 'T')");
$stmt->execute();
foreach ($stmt->fetchAllAssoc('word') as $word => $row) {
$expected = '"' . $word . '"';
$this->assertIdentical($db->escapeTable($word), $expected, format_string('The reserved word %word was correctly escaped when used as a table name.', array('%word' => $word)));
$this->assertIdentical($db->escapeField($word), $expected, format_string('The reserved word %word was correctly escaped when used as a column name.', array('%word' => $word)));
$this->assertIdentical($db->escapeAlias($word), $expected, format_string('The reserved word %word was correctly escaped when used as an alias.', array('%word' => $word)));
}
}
}

View file

@ -30,6 +30,7 @@ abstract class DatabaseTestBase extends KernelTestBase {
'test_task',
'test_null',
'test_serialized',
'test_special_columns',
));
self::addSampleData();
}
@ -138,5 +139,12 @@ abstract class DatabaseTestBase extends KernelTestBase {
'priority' => 3,
))
->execute();
db_insert('test_special_columns')
->fields(array(
'id' => 1,
'offset' => 'Offset value 1',
))
->execute();
}
}

View file

@ -70,4 +70,19 @@ class DeleteTruncateTest extends DatabaseTestBase {
$num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
$this->assertEqual(0, $num_records_after, 'Truncate really deletes everything.');
}
/**
* Confirms that we can delete a single special column name record successfully.
*/
function testSpecialColumnDelete() {
$num_records_before = db_query('SELECT COUNT(*) FROM {test_special_columns}')->fetchField();
$num_deleted = db_delete('test_special_columns')
->condition('id', 1)
->execute();
$this->assertIdentical($num_deleted, 1, 'Deleted 1 special column record.');
$num_records_after = db_query('SELECT COUNT(*) FROM {test_special_columns}')->fetchField();
$this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.');
}
}

View file

@ -182,4 +182,17 @@ class InsertTest extends DatabaseTestBase {
$this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
}
/**
* Tests that we can INSERT INTO a special named column.
*/
function testSpecialColumnInsert() {
$id = db_insert('test_special_columns')
->fields(array(
'id' => 2,
'offset' => 'Offset value 2',
))
->execute();
$saved_value = db_query('SELECT "offset" FROM {test_special_columns} WHERE id = :id', array(':id' => 2))->fetchField();
$this->assertIdentical($saved_value, 'Offset value 2', 'Can retrieve special column name value after inserting.');
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* @file
* Definition of Drupal\system\Tests\Database\LargeQueryTest.
*/
namespace Drupal\system\Tests\Database;
use Drupal\Component\Utility\Environment;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\DatabaseException;
/**
* Tests handling of large queries.
*
* @group Database
*/
class LargeQueryTest extends DatabaseTestBase {
/**
* Tests truncation of messages when max_allowed_packet exception occurs.
*/
function testMaxAllowedPacketQueryTruncating() {
// This test only makes sense if we are running on a MySQL database.
// Test if we are.
$database = Database::getConnectionInfo('default');
if ($database['default']['driver'] == 'mysql') {
// The max_allowed_packet value is configured per database instance.
// Retrieve the max_allowed_packet value from the current instance and
// check if PHP is configured with sufficient allowed memory to be able
// to generate a query larger than max_allowed_packet.
$max_allowed_packet = db_query('SELECT @@global.max_allowed_packet')->fetchField();
if (Environment::checkMemoryLimit($max_allowed_packet + (16 * 1024 * 1024))) {
$long_name = str_repeat('a', $max_allowed_packet + 1);
try {
db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $long_name));
$this->fail("An exception should be thrown for queries larger than 'max_allowed_packet'");
} catch (DatabaseException $e) {
// Close and re-open the connection. Otherwise we will run into error
// 2006 "MySQL server had gone away" afterwards.
Database::closeConnection();
Database::getConnection();
$this->assertEqual($e->getPrevious()->errorInfo[1], 1153, "Got a packet bigger than 'max_allowed_packet' bytes exception thrown.");
// Use strlen() to count the bytes exactly, not the unicode chars.
$this->assertTrue(strlen($e->getMessage()) <= $max_allowed_packet, "'max_allowed_packet' exception message truncated.");
}
}
else {
$this->verbose('The configured max_allowed_packet exceeds the php memory limit. Therefore the test is skipped.');
}
}
else {
$this->verbose('The test requires MySQL. Therefore the test is skipped.');
}
}
}

View file

@ -145,4 +145,18 @@ class UpdateTest extends DatabaseTestBase {
$saved_name= db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 42))->fetchField();
$this->assertIdentical($saved_name, 'John', 'Updated primary key successfully.');
}
/**
* Confirm that we can update values in a column with special name.
*/
function testSpecialColumnUpdate() {
$num_updated = db_update('test_special_columns')
->fields(array('offset' => 'New offset value'))
->condition('id', 1)
->execute();
$this->assertIdentical($num_updated, 1, 'Updated 1 special column record.');
$saved_value = db_query('SELECT "offset" FROM {test_special_columns} WHERE id = :id', array(':id' => 1))->fetchField();
$this->assertIdentical($saved_value, 'New offset value', 'Updated special column name value successfully.');
}
}

View file

@ -35,4 +35,26 @@ class ContainerRebuildWebTest extends WebTestBase {
$this->assertHeader('container_rebuild_indicator', 'new-identifier');
}
/**
* Tests container invalidation.
*/
public function testContainerInvalidation() {
// Ensure that parameter is not set.
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_test_parameter', FALSE);
// Ensure that after setting the parameter, without a container rebuild the
// parameter is still not set.
$this->writeSettings(['settings' => ['container_rebuild_test_parameter' => (object) ['value' => 'rebuild_me_please', 'required' => TRUE]]]);
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_test_parameter', FALSE);
// Ensure that after container invalidation the parameter is set.
\Drupal::service('kernel')->invalidateContainer();
$this->drupalGet('<front>');
$this->assertHeader('container_rebuild_test_parameter', 'rebuild_me_please');
}
}

View file

@ -0,0 +1,70 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Entity\ContentEntityCloneTest.
*/
namespace Drupal\system\Tests\Entity;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests proper cloning of content entities.
*
* @group Entity
*/
class ContentEntityCloneTest extends EntityUnitTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['language', 'entity_test'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Enable an additional language.
ConfigurableLanguage::createFromLangcode('de')->save();
$this->installEntitySchema('entity_test_mul');
}
/**
* Tests if entity references on fields are still correct after cloning.
*/
public function testFieldEntityReferenceAfterClone() {
$user = $this->createUser();
// Create a test entity.
$entity = EntityTestMul::create([
'name' => $this->randomString(),
'user_id' => $user->id(),
'language' => 'en',
]);
$clone = clone $entity->addTranslation('de');
$this->assertEqual($entity->getTranslationLanguages(), $clone->getTranslationLanguages(), 'The entity and its clone have the same translation languages.');
$default_langcode = $entity->getUntranslated()->language()->getId();
foreach (array_keys($clone->getTranslationLanguages()) as $langcode) {
$translation = $clone->getTranslation($langcode);
foreach ($translation->getFields() as $field_name => $field) {
if ($field->getFieldDefinition()->isTranslatable()) {
$args = ['%field_name' => $field_name, '%langcode' => $langcode];
$this->assertEqual($langcode, $field->getEntity()->language()->getId(), format_string('Translatable field %field_name on translation %langcode has correct entity reference in translation %langcode after cloning.', $args));
}
else {
$args = ['%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode];
$this->assertEqual($default_langcode, $field->getEntity()->language()->getId(), format_string('Non translatable field %field_name on translation %langcode has correct entity reference in the default translation %default_langcode after cloning.', $args));
}
}
}
}
}

View file

@ -132,6 +132,8 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
/**
* Returns the access cache contexts for the tested entity.
*
* Only list cache contexts that aren't part of the required cache contexts.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to be tested, as created by createEntity().
*
@ -141,12 +143,14 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
* @see \Drupal\Core\Entity\EntityAccessControlHandlerInterface
*/
protected function getAccessCacheContextsForEntity(EntityInterface $entity) {
return ['user.permissions'];
return [];
}
/**
* Returns the additional (non-standard) cache contexts for the tested entity.
*
* Only list cache contexts that aren't part of the required cache contexts.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity to be tested, as created by createEntity().
*
@ -331,16 +335,18 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$nonempty_entity_listing_url = Url::fromRoute('entity.entity_test.collection_labels_alphabetically', ['entity_type_id' => $entity_type]);
// The default cache contexts for rendered entities.
$default_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme'];
$default_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
$entity_cache_contexts = $default_cache_contexts;
// Cache tags present on every rendered page.
$page_cache_tags = Cache::mergeTags(
['rendered'],
// If the block module is used, the Block page display variant is used,
// which adds the block config entity type's list cache tags.
\Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list']: []
);
// 'user.permissions' is a required cache context, and responses that vary
// by this cache context when requested by anonymous users automatically
// also get this cache tag, to ensure correct invalidation.
$page_cache_tags = Cache::mergeTags(['rendered'], ['config:user.role.anonymous']);
// If the block module is used, the Block page display variant is used,
// which adds the block config entity type's list cache tags.
$page_cache_tags = Cache::mergeTags($page_cache_tags, \Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list']: []);
$page_cache_tags_referencing_entity = in_array('user.permissions', $this->getAccessCacheContextsForEntity($this->referencingEntity)) ? ['config:user.role.anonymous'] : [];
$view_cache_tag = array();
@ -349,47 +355,50 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
->getCacheTags();
}
$context_metadata = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($entity_cache_contexts);
$cache_context_tags = $context_metadata->getCacheTags();
// Generate the cache tags for the (non) referencing entities.
$referencing_entity_cache_tags = Cache::mergeTags(
$this->referencingEntity->getCacheTags(),
\Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(),
// Includes the main entity's cache tags, since this entity references it.
$this->entity->getCacheTags(),
$this->getAdditionalCacheTagsForEntity($this->entity),
$view_cache_tag,
['rendered']
);
$non_referencing_entity_cache_tags = Cache::mergeTags(
$this->nonReferencingEntity->getCacheTags(),
\Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(),
['rendered']
);
$referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags());
// Includes the main entity's cache tags, since this entity references it.
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $this->entity->getCacheTags());
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity));
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $view_cache_tag);
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $cache_context_tags);
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']);
$non_referencing_entity_cache_tags = Cache::mergeTags($this->nonReferencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags());
$non_referencing_entity_cache_tags = Cache::mergeTags($non_referencing_entity_cache_tags, ['rendered']);
// Generate the cache tags for all two possible entity listing paths.
// 1. list cache tag only (listing query has no match)
// 2. list cache tag plus entity cache tag (listing query has a match)
$empty_entity_listing_cache_tags = Cache::mergeTags(
$this->entity->getEntityType()->getListCacheTags(),
$page_cache_tags
);
$nonempty_entity_listing_cache_tags = Cache::mergeTags(
$this->entity->getEntityType()->getListCacheTags(),
$this->entity->getCacheTags(),
$this->getAdditionalCacheTagsForEntityListing($this->entity),
$page_cache_tags
);
$empty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $page_cache_tags);
$nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->entity->getCacheTags());
$nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $this->getAdditionalCacheTagsForEntityListing($this->entity));
$nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags);
$this->pass("Test referencing entity.", 'Debug');
$this->verifyPageCache($referencing_entity_url, 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags.
$this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags, $page_cache_tags_referencing_entity));
$expected_tags = Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags);
$expected_tags = Cache::mergeTags($expected_tags, $page_cache_tags_referencing_entity);
$this->verifyPageCache($referencing_entity_url, 'HIT', $expected_tags);
// Also verify the existence of an entity render cache entry.
$cache_keys = ['entity_view', 'entity_test', $this->referencingEntity->id(), 'full'];
$cid = $this->createCacheId($cache_keys, $entity_cache_contexts);
$access_cache_contexts = $this->getAccessCacheContextsForEntity($this->entity);
$additional_cache_contexts = $this->getAdditionalCacheContextsForEntity($this->referencingEntity);
$redirected_cid = NULL;
if (count($access_cache_contexts)) {
$redirected_cid = $this->createCacheId($cache_keys, Cache::mergeContexts($entity_cache_contexts, $this->getAdditionalCacheContextsForEntity($this->referencingEntity), $access_cache_contexts));
if (count($access_cache_contexts) || count($additional_cache_contexts)) {
$cache_contexts = Cache::mergeContexts($entity_cache_contexts, $additional_cache_contexts);
$cache_contexts = Cache::mergeContexts($cache_contexts, $access_cache_contexts);
$redirected_cid = $this->createCacheId($cache_keys, $cache_contexts);
$context_metadata = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts);
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $context_metadata->getCacheTags());
}
$this->verifyRenderCache($cid, $referencing_entity_cache_tags, $redirected_cid);
@ -406,9 +415,11 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$this->pass("Test listing of referencing entities.", 'Debug');
// Prime the page cache for the listing of referencing entities.
$this->verifyPageCache($listing_url, 'MISS');
// Verify a cache hit, but also the presence of the correct cache tags.
$this->verifyPageCache($listing_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags, $page_cache_tags_referencing_entity));
// Verify a cache hit, but also the presence of the correct cache tags.
$expected_tags = Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags);
$expected_tags = Cache::mergeTags($expected_tags, $page_cache_tags_referencing_entity);
$this->verifyPageCache($listing_url, 'HIT', $expected_tags);
$this->pass("Test empty listing.", 'Debug');
// Prime the page cache for the empty listing.
@ -571,7 +582,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
// a cache miss for every route except the ones for the non-referencing
// entity and the empty entity listing.
$this->pass("Test invalidation of referenced entity's cache tag.", 'Debug');
Cache::invalidateTags($this->entity->getCacheTags());
Cache::invalidateTags($this->entity->getCacheTagsToInvalidate());
$this->verifyPageCache($referencing_entity_url, 'MISS');
$this->verifyPageCache($listing_url, 'MISS');
$this->verifyPageCache($nonempty_entity_listing_url, 'MISS');
@ -627,15 +638,16 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$this->verifyPageCache($non_referencing_entity_url, 'HIT');
// Verify cache hits.
$referencing_entity_cache_tags = Cache::mergeTags(
$this->referencingEntity->getCacheTags(),
\Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags(),
['rendered']
);
$referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags());
$referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']);
$nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing());
$nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags);
$this->verifyPageCache($referencing_entity_url, 'HIT', Cache::mergeTags($referencing_entity_cache_tags, $page_cache_tags));
$this->verifyPageCache($listing_url, 'HIT', $page_cache_tags);
$this->verifyPageCache($empty_entity_listing_url, 'HIT', $empty_entity_listing_cache_tags);
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT', Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing(), $page_cache_tags));
$this->verifyPageCache($nonempty_entity_listing_url, 'HIT', $nonempty_entity_listing_cache_tags);
}
/**
@ -653,7 +665,7 @@ abstract class EntityCacheTagsTestBase extends PageCacheTagsTestBase {
$cid_parts = $keys;
$contexts = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($contexts);
$cid_parts = array_merge($cid_parts, $contexts);
$cid_parts = array_merge($cid_parts, $contexts->getKeys());
return implode(':', $cid_parts);
}

View file

@ -17,6 +17,7 @@ use Drupal\taxonomy\Entity\Term;
use Drupal\node\Entity\Node;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\user\Entity\User;
use Drupal\file\Entity\File;
/**
* Tests the invocation of hooks when creating, inserting, loading, updating or
@ -257,7 +258,7 @@ class EntityCrudHookTest extends EntityUnitTestBase {
));
$GLOBALS['entity_crud_hook_test'] = array();
$file = file_load($file->id());
$file = File::load($file->id());
$this->assertHookMessageOrder(array(
'entity_crud_hook_test_entity_load called for type file',

View file

@ -9,6 +9,7 @@ namespace Drupal\system\Tests\Entity;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Database\IntegrityConstraintViolationException;
use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\EntityTypeEvents;
use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException;
@ -604,4 +605,25 @@ class EntityDefinitionUpdateTest extends EntityUnitTestBase {
}
}
/**
* Tests ::applyEntityUpdate() and ::applyFieldUpdate().
*/
public function testSingleActionCalls() {
// Ensure that the methods return FALSE when called with bogus information.
$this->assertFalse($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'foo'), 'Calling applyEntityUpdate() with a non-existent entity returns FALSE.');
$this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'foo', 'bar'), 'Calling applyFieldUpdate() with a non-existent entity returns FALSE.');
$this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update', 'bar'), 'Calling applyFieldUpdate() with a non-existent field returns FALSE.');
$this->assertFalse($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update'), 'Calling applyEntityUpdate() with an $op that is not applicable to the entity type returns FALSE.');
$this->assertFalse($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_DELETED, 'entity_test_update', 'new_base_field'), 'Calling applyFieldUpdate() with an $op that is not applicable to the field returns FALSE.');
// Create a new base field.
$this->addRevisionableBaseField();
$this->assertTrue($this->entityDefinitionUpdateManager->applyFieldUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_CREATED, 'entity_test_update', 'new_base_field'), 'Calling applyFieldUpdate() correctly returns TRUE.');
$this->assertTrue($this->database->schema()->fieldExists('entity_test_update', 'new_base_field'), "New field 'new_base_field' has been created on the 'entity_test_update' table.");
// Make the entity type revisionable.
$this->updateEntityTypeToRevisionable();
$this->assertTrue($this->entityDefinitionUpdateManager->applyEntityUpdate(EntityDefinitionUpdateManagerInterface::DEFINITION_UPDATED, 'entity_test_update'), 'Calling applyEntityUpdate() correctly returns TRUE.');
$this->assertTrue($this->database->schema()->tableExists('entity_test_update_revision'), "The 'entity_test_update_revision' table has been created.");
}
}

View file

@ -149,4 +149,25 @@ class EntityFormTest extends WebTestBase {
$entities = $entity_storage->loadByProperties(array('name' => $name));
return $entities ? current($entities) : NULL;
}
/**
* Checks that validation handlers works as expected.
*/
public function testValidationHandlers() {
/** @var \Drupal\Core\State\StateInterface $state */
$state = $this->container->get('state');
// Check that from-level validation handlers can be defined and can alter
// the form array.
$state->set('entity_test.form.validate.test', 'form-level');
$this->drupalPostForm('entity_test/add', [], 'Save');
$this->assertTrue($state->get('entity_test.form.validate.result'), 'Form-level validation handlers behave correctly.');
// Check that defining a button-level validation handler causes an exception
// to be thrown.
$state->set('entity_test.form.validate.test', 'button-level');
$this->drupalPostForm('entity_test/add', [], 'Save');
$this->assertEqual($state->get('entity_test.form.save.exception'), 'Drupal\Core\Entity\EntityStorageException: Entity validation was skipped.', 'Button-level validation handlers behave correctly.');
}
}

View file

@ -67,7 +67,7 @@ class EntityListBuilderTest extends WebTestBase {
$build = $list_builder->render();
$this->container->get('renderer')->renderRoot($build);
$this->assertEqual(['entity_test_view_grants', 'languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'url.query_args.pagers:0'], $build['#cache']['contexts']);
$this->assertEqual(['entity_test_view_grants', 'languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'url.query_args.pagers:0', 'user.permissions'], $build['#cache']['contexts']);
}
}

View file

@ -40,6 +40,13 @@ class EntityQueryTest extends EntityUnitTestBase {
*/
protected $factory;
/**
* A list of bundle machine names created for this test.
*
* @var string[]
*/
protected $bundles;
/**
* Field name for the greetings field.
*
@ -133,6 +140,7 @@ class EntityQueryTest extends EntityUnitTestBase {
}
$entity->save();
}
$this->bundles = $bundles;
$this->figures = $figures;
$this->greetings = $greetings;
$this->factory = \Drupal::service('entity.query');
@ -210,6 +218,26 @@ class EntityQueryTest extends EntityUnitTestBase {
// Unit 0 and unit 1, so bits 0 1.
$this->assertResult(3, 7, 11, 15);
// Do the same test but with IN operator.
$query = $this->factory->get('entity_test_mulrev');
$group_blue = $query->andConditionGroup()->condition("$figures.color", array('blue'), 'IN');
$group_red = $query->andConditionGroup()->condition("$figures.color", array('red'), 'IN');
$query
->condition($group_blue)
->condition($group_red)
->sort('id')
->execute();
// Unit 0 and unit 1, so bits 0 1.
$this->assertResult(3, 7, 11, 15);
// An entity might have either red or blue figure.
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition("$figures.color", array('blue', 'red'), 'IN')
->sort('id')
->execute();
// Bit 0 or 1 is on.
$this->assertResult(1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 14, 15);
$this->queryResults = $this->factory->get('entity_test_mulrev')
->exists("$figures.color")
->notExists("$greetings.value")
@ -460,6 +488,34 @@ class EntityQueryTest extends EntityUnitTestBase {
$this->assertFalse($count);
}
/**
* Tests that nested condition groups work as expected.
*/
public function testNestedConditionGroups() {
// Query for all entities of the first bundle that have either a red
// triangle as a figure or the Turkish greeting as a greeting.
$query = $this->factory->get('entity_test_mulrev');
$first_and = $query->andConditionGroup()
->condition($this->figures . '.color', 'red')
->condition($this->figures . '.shape', 'triangle');
$second_and = $query->andConditionGroup()
->condition($this->greetings . '.value', 'merhaba')
->condition($this->greetings . '.format', 'format-tr');
$or = $query->orConditionGroup()
->condition($first_and)
->condition($second_and);
$this->queryResults = $query
->condition($or)
->condition('type', reset($this->bundles))
->sort('id')
->execute();
$this->assertResult(6, 14);
}
protected function assertResult() {
$assert = array();
$expected = func_get_args();
@ -613,6 +669,38 @@ class EntityQueryTest extends EntityUnitTestBase {
)->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, exact match.');
// Check the case insensitive field, IN operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', array($fixtures[0]['lowercase'] . $fixtures[1]['lowercase']), 'IN'
)->execute();
$this->assertIdentical(count($result), 1, 'Case insensitive, lowercase');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', array($fixtures[0]['uppercase'] . $fixtures[1]['uppercase']), 'IN'
)->execute();
$this->assertIdentical(count($result), 1, 'Case insensitive, uppercase');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', array($fixtures[0]['uppercase'] . $fixtures[1]['lowercase']), 'IN'
)->execute();
$this->assertIdentical(count($result), 1, 'Case insensitive, mixed');
// Check the case sensitive field, IN operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', array($fixtures[0]['lowercase'] . $fixtures[1]['lowercase']), 'IN'
)->execute();
$this->assertIdentical(count($result), 0, 'Case sensitive, lowercase');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', array($fixtures[0]['uppercase'] . $fixtures[1]['uppercase']), 'IN'
)->execute();
$this->assertIdentical(count($result), 0, 'Case sensitive, uppercase');
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_cs', array($fixtures[0]['uppercase'] . $fixtures[1]['lowercase']), 'IN'
)->execute();
$this->assertIdentical(count($result), 1, 'Case sensitive, mixed');
// Check the case insensitive field, STARTS_WITH operator.
$result = \Drupal::entityQuery('entity_test_mulrev')->condition(
'field_ci', $fixtures[0]['lowercase'], 'STARTS_WITH'
@ -762,4 +850,20 @@ class EntityQueryTest extends EntityUnitTestBase {
$this->assertEqual($result, [16 => '14']);
}
/**
* Test against SQL inject of condition field. This covers a
* database driver's EntityQuery\Condition class.
*/
public function testInjectionInCondition() {
try {
$this->queryResults = $this->factory->get('entity_test_mulrev')
->condition('1 ; -- ', array(0, 1), 'IN')
->sort('id')
->execute();
$this->fail('SQL Injection attempt in Entity Query condition in operator should result in an exception.');
}
catch (\Exception $e) {
$this->pass('SQL Injection attempt in Entity Query condition in operator should result in an exception.');
}
}
}

View file

@ -612,10 +612,10 @@ class EntityTranslationTest extends EntityLanguageTestBase {
// Get an view builder.
$controller = $this->entityManager->getViewBuilder($entity_type);
$entity2_build = $controller->view($entity2);
$entity2_output = $renderer->renderRoot($entity2_build);
$entity2_output = (string) $renderer->renderRoot($entity2_build);
$translation = $this->entityManager->getTranslationFromContext($entity2, $default_langcode);
$translation_build = $controller->view($translation);
$translation_output = $renderer->renderRoot($translation_build);
$translation_output = (string) $renderer->renderRoot($translation_build);
$this->assertIdentical($entity2_output, $translation_output, 'When the entity has no translation no fallback is applied.');
// Checks that entity translations are rendered properly.
@ -769,4 +769,33 @@ class EntityTranslationTest extends EntityLanguageTestBase {
}
}
/**
* Tests if entity references are correct after adding a new translation.
*/
public function testFieldEntityReference() {
$entity_type = 'entity_test_mul';
$controller = $this->entityManager->getStorage($entity_type);
/** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
$entity = $controller->create();
foreach ($this->langcodes as $langcode) {
$entity->addTranslation($langcode);
}
$default_langcode = $entity->getUntranslated()->language()->getId();
foreach (array_keys($entity->getTranslationLanguages()) as $langcode) {
$translation = $entity->getTranslation($langcode);
foreach ($translation->getFields() as $field_name => $field) {
if ($field->getFieldDefinition()->isTranslatable()) {
$args = ['%field_name' => $field_name, '%langcode' => $langcode];
$this->assertEqual($langcode, $field->getEntity()->language()->getId(), format_string('Translatable field %field_name on translation %langcode has correct entity reference in translation %langcode.', $args));
}
else {
$args = ['%field_name' => $field_name, '%langcode' => $langcode, '%default_langcode' => $default_langcode];
$this->assertEqual($default_langcode, $field->getEntity()->language()->getId(), format_string('Non translatable field %field_name on translation %langcode has correct entity reference in the default translation %default_langcode.', $args));
}
}
}
}
}

View file

@ -85,7 +85,14 @@ class EntityValidationTest extends EntityUnitTestBase {
*/
public function testValidation() {
// Ensure that the constraint manager is marked as cached cleared.
$plugin_cache_clearer = \Drupal::service('plugin.cache_clearer');
// Use the protected property on the cache_clearer first to check whether
// the constraint manager is added there.
// Ensure that the proxy class is initialized, which has the necessary
// method calls attached.
\Drupal::service('plugin.cache_clearer');
$plugin_cache_clearer = \Drupal::service('drupal.proxy_original_service.plugin.cache_clearer');
$get_cached_discoveries = function () {
return $this->cachedDiscoveries;
};

View file

@ -65,7 +65,7 @@ class EntityViewBuilderTest extends EntityUnitTestBase {
// Get a fully built entity view render array.
$entity_test->save();
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme']));
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
$cid = implode(':', $cid_parts);
$bin = $build['#cache']['bin'];
@ -117,7 +117,7 @@ class EntityViewBuilderTest extends EntityUnitTestBase {
// Get a fully built entity view render array for the referenced entity.
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test_reference, 'full');
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme']));
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
$cid_reference = implode(':', $cid_parts);
$bin_reference = $build['#cache']['bin'];
@ -136,7 +136,7 @@ class EntityViewBuilderTest extends EntityUnitTestBase {
// Get a fully built entity view render array.
$build = $this->container->get('entity.manager')->getViewBuilder('entity_test')->view($entity_test, 'full');
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme']));
$cid_parts = array_merge($build['#cache']['keys'], $cache_contexts_manager->convertTokensToKeys(['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'])->getKeys());
$cid = implode(':', $cid_parts);
$bin = $build['#cache']['bin'];

View file

@ -34,9 +34,7 @@ class EntityViewControllerTest extends WebTestBase {
parent::setUp();
// Create some dummy entity_test entities.
for ($i = 0; $i < 2; $i++) {
$random_label = $this->randomMachineName();
$data = array('bundle' => 'entity_test', 'name' => $random_label);
$entity_test = $this->container->get('entity.manager')->getStorage('entity_test')->create($data);
$entity_test = $this->createTestEntity('entity_test');
$entity_test->save();
$this->entities[] = $entity_test;
}
@ -116,4 +114,31 @@ class EntityViewControllerTest extends WebTestBase {
$this->assertTrue($xpath, 'The field item attributes from both modules have been found in the rendered output of the field.');
}
/**
* Tests that a view builder can successfully override the view builder.
*/
public function testEntityViewControllerViewBuilder() {
$entity_test = $this->createTestEntity('entity_test_view_builder');
$entity_test->save();
$this->drupalGet('entity_test_view_builder/' . $entity_test->id());
$this->assertText($entity_test->label());
}
/**
* Creates an entity for testing.
*
* @param string $entity_type
* The entity type.
*
* @return \Drupal\Core\Entity\EntityInterface
* The created entity.
*/
protected function createTestEntity($entity_type) {
$data = array(
'bundle' => $entity_type,
'name' => $this->randomMachineName(),
);
return $this->container->get('entity.manager')->getStorage($entity_type)->create($data);
}
}

View file

@ -32,7 +32,7 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
$view_mode = $this->selectViewMode($entity_type);
// The default cache contexts for rendered entities.
$entity_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme'];
$entity_cache_contexts = ['languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'user.permissions'];
// Generate the standardized entity cache tags.
$cache_tag = $this->entity->getCacheTags();
@ -56,7 +56,9 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
if (count($additional_cache_contexts)) {
$redirected_cid = $this->createCacheId($cache_keys, Cache::mergeContexts($entity_cache_contexts, $additional_cache_contexts));
}
$expected_cache_tags = Cache::mergeTags($cache_tag, $view_cache_tag, $this->getAdditionalCacheTagsForEntity($this->entity), array($render_cache_tag));
$expected_cache_tags = Cache::mergeTags($cache_tag, $view_cache_tag);
$expected_cache_tags = Cache::mergeTags($expected_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity));
$expected_cache_tags = Cache::mergeTags($expected_cache_tags, array($render_cache_tag));
$this->verifyRenderCache($cid, $expected_cache_tags, $redirected_cid);
}
@ -121,7 +123,7 @@ abstract class EntityWithUriCacheTagsTestBase extends EntityCacheTagsTestBase {
// Verify that after invalidating the entity's cache tag directly, there is
// a cache miss.
$this->pass("Test invalidation of entity's cache tag.", 'Debug');
Cache::invalidateTags($this->entity->getCacheTags());
Cache::invalidateTags($this->entity->getCacheTagsToInvalidate());
$this->verifyPageCache($entity_url, 'MISS');
// Verify a cache hit.

View file

@ -89,8 +89,12 @@ class FieldWidgetConstraintValidatorTest extends KernelTestBase {
\Drupal::formBuilder()->processForm('field_test_entity_form', $form, $form_state);
// Validate the field constraint.
$form_state->getFormObject()->setEntity($entity)->setFormDisplay($display, $form_state);
$form_state->getFormObject()->validate($form, $form_state);
/** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */
$form_object = $form_state->getFormObject();
$form_object
->setEntity($entity)
->setFormDisplay($display, $form_state)
->validateForm($form, $form_state);
return $form_state->getErrors();
}

View file

@ -0,0 +1,82 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Entity\Update\SqlContentEntityStorageSchemaIndexTest.
*/
namespace Drupal\system\Tests\Entity\Update;
use Drupal\system\Tests\Update\UpdatePathTestBase;
/**
* Tests that a newly-added index is properly created during database updates.
*
* @group Entity
*/
class SqlContentEntityStorageSchemaIndexTest extends UpdatePathTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = ['update_order_test'];
/**
* {@inheritdoc}
*/
public function setUp() {
$this->databaseDumpFiles = [
__DIR__ . '/../../../../tests/fixtures/update/drupal-8.bare.standard.php.gz',
];
parent::setUp();
}
/**
* Tests entity and field schema database updates and execution order.
*/
public function testIndex() {
// Enable the hook implementations in the update_order_test module.
\Drupal::state()->set('update_order_test', TRUE);
// The initial Drupal 8 database dump before any updates does not include
// the entity ID in the entity field data table indices that were added in
// https://www.drupal.org/node/2261669.
$this->assertTrue(db_index_exists('node_field_data', 'node__default_langcode'), 'Index node__default_langcode exists prior to running updates.');
$this->assertFalse(db_index_exists('node_field_data', 'node__id__default_langcode__langcode'), 'Index node__id__default_langcode__langcode does not exist prior to running updates.');
$this->assertFalse(db_index_exists('users_field_data', 'user__id__default_langcode__langcode'), 'Index users__id__default_langcode__langcode does not exist prior to running updates.');
// Running database updates should automatically update the entity schemata
// to add the indices from https://www.drupal.org/node/2261669.
$this->runUpdates();
$this->assertFalse(db_index_exists('node_field_data', 'node__default_langcode'), 'Index node__default_langcode properly removed.');
$this->assertTrue(db_index_exists('node_field_data', 'node__id__default_langcode__langcode'), 'Index node__id__default_langcode__langcode properly created on the node_field_data table.');
$this->assertTrue(db_index_exists('users_field_data', 'user__id__default_langcode__langcode'), 'Index users__id__default_langcode__langcode properly created on the user_field_data table.');
// Ensure that hook_update_N() implementations were in the expected order
// relative to the entity and field updates. The expected order is:
// 1. Initial Drupal 8.0.0-beta12 installation with no indices.
// 2. update_order_test_update_8001() is invoked.
// 3. update_order_test_update_8002() is invoked.
// 4. update_order_test_update_8002() explicitly applies the updates for
// the update_order_test_field storage. See update_order_test.module.
// 5. update_order_test_update_8002() explicitly applies the updates for
// the node entity type indices listed above.
// 6. The remaining entity schema updates are applied automatically after
// all update hook implementations have run, which applies the user
// index update.
$this->assertTrue(\Drupal::state()->get('update_order_test_update_8001', FALSE), 'Index node__default_langcode still existed during update_order_test_update_8001(), indicating that it ran before the entity type updates.');
// Node updates were run during update_order_test_update_8002().
$this->assertFalse(\Drupal::state()->get('update_order_test_update_8002_node__default_langcode', TRUE), 'The node__default_langcode index was removed during update_order_test_update_8002().');
$this->assertTrue(\Drupal::state()->get('update_order_test_update_8002_node__id__default_langcode__langcode', FALSE), 'The node__id__default_langcode__langcode index was created during update_order_test_update_8002().');
// Ensure that the base field created by update_order_test_update_8002() is
// created when we expect.
$this->assertFalse(\Drupal::state()->get('update_order_test_update_8002_update_order_test_before', TRUE), 'The update_order_test field was not been created on Node before update_order_test_update_8002().');
$this->assertTrue(\Drupal::state()->get('update_order_test_update_8002_update_order_test_after', FALSE), 'The update_order_test field was created on Node by update_order_test_update_8002().');
// User update were not run during update_order_test_update_8002().
$this->assertFalse(\Drupal::state()->get('update_order_test_update_8002_user__id__default_langcode__langcode', TRUE));
}
}

View file

@ -297,14 +297,14 @@ class ModuleHandlerTest extends KernelTestBase {
public function testModuleStreamWrappers() {
// file_test.module provides (among others) a 'dummy' stream wrapper.
// Verify that it is not registered yet to prevent false positives.
$stream_wrappers = file_get_stream_wrappers();
$stream_wrappers = \Drupal::service('stream_wrapper_manager')->getWrappers();
$this->assertFalse(isset($stream_wrappers['dummy']));
$this->moduleInstaller()->install(['file_test']);
// Verify that the stream wrapper is available even without calling
// file_get_stream_wrappers() again. If the stream wrapper is not available
// file_exists() will raise a notice.
// \Drupal::service('stream_wrapper_manager')->getWrappers() again.
// If the stream wrapper is not available file_exists() will raise a notice.
file_exists('dummy://');
$stream_wrappers = file_get_stream_wrappers();
$stream_wrappers = \Drupal::service('stream_wrapper_manager')->getWrappers();
$this->assertTrue(isset($stream_wrappers['dummy']));
}

View file

@ -0,0 +1,110 @@
<?php
/**
* @file
* Contains Drupal\system\Tests\Field\FieldItemTest.
*/
namespace Drupal\system\Tests\Field;
use Drupal\Component\Utility\Unicode;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\entity_test\Entity\EntityTestMulRev;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\system\Tests\Entity\EntityUnitTestBase;
/**
* Test field item methods.
*
* @group Field
*/
class FieldItemTest extends EntityUnitTestBase {
/**
* @var string
*/
protected $fieldName;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->container->get('state')->set('entity_test.field_test_item', TRUE);
$this->entityManager->clearCachedDefinitions();
$entity_type_id = 'entity_test_mulrev';
$this->installEntitySchema($entity_type_id);
$this->fieldName = Unicode::strtolower($this->randomMachineName());
/** @var \Drupal\field\Entity\FieldStorageConfig $field_storage */
FieldStorageConfig::create([
'field_name' => $this->fieldName,
'type' => 'field_test',
'entity_type' => $entity_type_id,
'cardinality' => 1,
])->save();
FieldConfig::create([
'entity_type' => $entity_type_id,
'field_name' => $this->fieldName,
'bundle' => $entity_type_id,
'label' => 'Test field',
])->save();
$this->entityManager->clearCachedDefinitions();
$definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
$this->assertTrue(!empty($definitions[$this->fieldName]));
}
/**
* Tests the field item save workflow.
*/
public function testSaveWorkflow() {
$entity = EntityTestMulRev::create([
'name' => $this->randomString(),
'field_test_item' => $this->randomString(),
$this->fieldName => $this->randomString(),
]);
// Save a new entity and verify that the initial field value is overwritten
// with a value containing the entity id, which implies a resave. Check that
// the entity data structure and the stored values match.
$this->assertSavedFieldItemValue($entity, "field_test:{$this->fieldName}:1:1");
// Update the entity and verify that the field value is overwritten on
// presave if it is not resaved.
$this->assertSavedFieldItemValue($entity, 'overwritten');
// Flag the field value as needing to be resaved and verify it actually is.
$entity->field_test_item->value = $entity->{$this->fieldName}->value = 'resave';
$this->assertSavedFieldItemValue($entity, "field_test:{$this->fieldName}:1:3");
}
/**
* Checks that the saved field item value matches the expected one.
*
* @param \Drupal\entity_test\Entity\EntityTest $entity
* The test entity.
* @param $expected_value
* The expected field item value.
*
* @return bool
* TRUE if the item value matches expectations, FALSE otherwise.
*/
protected function assertSavedFieldItemValue(EntityTest $entity, $expected_value) {
$entity->setNewRevision(TRUE);
$entity->save();
$base_field_expected_value = str_replace($this->fieldName, 'field_test_item', $expected_value);
$result = $this->assertEqual($entity->field_test_item->value, $base_field_expected_value);
$result = $result && $this->assertEqual($entity->{$this->fieldName}->value, $expected_value);
$entity = $this->reloadEntity($entity);
$result = $result && $this->assertEqual($entity->field_test_item->value, $base_field_expected_value);
$result = $result && $this->assertEqual($entity->{$this->fieldName}->value, $expected_value);
return $result;
}
}

View file

@ -40,7 +40,7 @@ class ReadOnlyStreamWrapperTest extends FileTestBase {
// Generate a read-only stream wrapper instance
$uri = $this->scheme . '://' . $filename;
file_stream_wrapper_get_instance_by_scheme($this->scheme);
\Drupal::service('stream_wrapper_manager')->getViaScheme($this->scheme);
// Attempt to open a file in read/write mode
$handle = @fopen($uri, 'r+');

View file

@ -56,32 +56,32 @@ class StreamWrapperTest extends FileTestBase {
*/
function testGetClassName() {
// Check the dummy scheme.
$this->assertEqual($this->classname, file_stream_wrapper_get_class($this->scheme), 'Got correct class name for dummy scheme.');
$this->assertEqual($this->classname, \Drupal::service('stream_wrapper_manager')->getClass($this->scheme), 'Got correct class name for dummy scheme.');
// Check core's scheme.
$this->assertEqual('Drupal\Core\StreamWrapper\PublicStream', file_stream_wrapper_get_class('public'), 'Got correct class name for public scheme.');
$this->assertEqual('Drupal\Core\StreamWrapper\PublicStream', \Drupal::service('stream_wrapper_manager')->getClass('public'), 'Got correct class name for public scheme.');
}
/**
* Test the file_stream_wrapper_get_instance_by_scheme() function.
* Test the getViaScheme() method.
*/
function testGetInstanceByScheme() {
$instance = file_stream_wrapper_get_instance_by_scheme($this->scheme);
$instance = \Drupal::service('stream_wrapper_manager')->getViaScheme($this->scheme);
$this->assertEqual($this->classname, get_class($instance), 'Got correct class type for dummy scheme.');
$instance = file_stream_wrapper_get_instance_by_scheme('public');
$instance = \Drupal::service('stream_wrapper_manager')->getViaScheme('public');
$this->assertEqual('Drupal\Core\StreamWrapper\PublicStream', get_class($instance), 'Got correct class type for public scheme.');
}
/**
* Test the URI and target functions.
* Test the getViaUri() and getViaScheme() methods and target functions.
*/
function testUriFunctions() {
$config = $this->config('system.file');
$instance = file_stream_wrapper_get_instance_by_uri($this->scheme . '://foo');
$instance = \Drupal::service('stream_wrapper_manager')->getViaUri($this->scheme . '://foo');
$this->assertEqual($this->classname, get_class($instance), 'Got correct class type for dummy URI.');
$instance = file_stream_wrapper_get_instance_by_uri('public://foo');
$instance = \Drupal::service('stream_wrapper_manager')->getViaUri('public://foo');
$this->assertEqual('Drupal\Core\StreamWrapper\PublicStream', get_class($instance), 'Got correct class type for public URI.');
// Test file_uri_target().
@ -94,8 +94,8 @@ class StreamWrapperTest extends FileTestBase {
// Test file_build_uri() and
// Drupal\Core\StreamWrapper\LocalStream::getDirectoryPath().
$this->assertEqual(file_build_uri('foo/bar.txt'), 'public://foo/bar.txt', 'Expected scheme was added.');
$this->assertEqual(file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath(), PublicStream::basePath(), 'Expected default directory path was returned.');
$this->assertEqual(file_stream_wrapper_get_instance_by_scheme('temporary')->getDirectoryPath(), $config->get('path.temporary'), 'Expected temporary directory path was returned.');
$this->assertEqual(\Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath(), PublicStream::basePath(), 'Expected default directory path was returned.');
$this->assertEqual(\Drupal::service('stream_wrapper_manager')->getViaScheme('temporary')->getDirectoryPath(), $config->get('path.temporary'), 'Expected temporary directory path was returned.');
$config->set('default_scheme', 'private')->save();
$this->assertEqual(file_build_uri('foo/bar.txt'), 'private://foo/bar.txt', 'Got a valid URI from foo/bar.txt.');

View file

@ -80,7 +80,7 @@ class UrlRewritingTest extends FileTestBase {
\Drupal::state()->set('file_test.hook_file_url_alter', 'cdn');
$uri = $this->createUri();
$url = file_create_url($uri);
$public_directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath();
$public_directory_path = \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath();
$this->assertEqual(FILE_URL_TEST_CDN_2 . '/' . $public_directory_path . '/' . drupal_basename($uri), $url, 'Correctly generated a CDN URL for a created file.');
// Test alteration of file URLs to use root-relative URLs.
@ -116,7 +116,7 @@ class UrlRewritingTest extends FileTestBase {
// Managed file.
$uri = $this->createUri();
$url = file_create_url($uri);
$public_directory_path = file_stream_wrapper_get_instance_by_scheme('public')->getDirectoryPath();
$public_directory_path = \Drupal::service('stream_wrapper_manager')->getViaScheme('public')->getDirectoryPath();
$this->assertIdentical(base_path() . $public_directory_path . '/' . rawurlencode(drupal_basename($uri)), file_url_transform_relative($url));
}

View file

@ -151,6 +151,9 @@ class ElementTest extends WebTestBase {
$this->drupalLogin($user);
$this->drupalGet('form-test/autocomplete');
// Make sure that the autocomplete library is added.
$this->assertRaw('core/misc/autocomplete.js');
$result = $this->xpath('//input[@id="edit-autocomplete-1" and contains(@data-autocomplete-path, "form-test/autocomplete-1")]');
$this->assertEqual(count($result), 1, 'Ensure that the user does have access to the autocompletion');
$result = $this->xpath('//input[@id="edit-autocomplete-2" and contains(@data-autocomplete-path, "form-test/autocomplete-2/value")]');

View file

@ -0,0 +1,108 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Form\ExternalFormUrlTest.
*/
namespace Drupal\system\Tests\Form;
use Drupal\Core\Form\FormInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\simpletest\KernelTestBase;
use Drupal\user\Entity\User;
use Symfony\Component\HttpFoundation\Request;
/**
* Ensures that form actions can't be tricked into sending to external URLs.
*
* @group system
*/
class ExternalFormUrlTest extends KernelTestBase implements FormInterface {
/**
* {@inheritdoc}
*/
public static $modules = ['user', 'system'];
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'external_form_url_test';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['something'] = [
'#type' => 'textfield',
'#title' => 'What do you think?',
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {}
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', ['key_value_expire', 'sequences']);
$this->installEntitySchema('user');
$test_user = User::create([
'name' => 'foobar',
'mail' => 'foobar@example.com',
]);
$test_user->save();
\Drupal::service('current_user')->setAccount($test_user);
}
/**
* Tests form behaviour.
*/
public function testActionUrlBehavior() {
// Create a new request which has a request uri with multiple leading
// slashes and make it the master request.
$request_stack = \Drupal::service('request_stack');
$original_request = $request_stack->pop();
$request = Request::create($original_request->getSchemeAndHttpHost() . '//example.org');
$request_stack->push($request);
$form = \Drupal::formBuilder()->getForm($this);
$markup = \Drupal::service('renderer')->renderRoot($form);
$this->setRawContent($markup);
$elements = $this->xpath('//form/@action');
$action = (string) $elements[0];
$this->assertEqual($original_request->getSchemeAndHttpHost() . '//example.org', $action);
// Create a new request which has a request uri with a single leading slash
// and make it the master request.
$request_stack = \Drupal::service('request_stack');
$original_request = $request_stack->pop();
$request = Request::create($original_request->getSchemeAndHttpHost() . '/example.org');
$request_stack->push($request);
$form = \Drupal::formBuilder()->getForm($this);
$markup = \Drupal::service('renderer')->renderRoot($form);
$this->setRawContent($markup);
$elements = $this->xpath('//form/@action');
$action = (string) $elements[0];
$this->assertEqual('/example.org', $action);
}
}

View file

@ -53,7 +53,7 @@ class FormStoragePageCacheTest extends WebTestBase {
// Trigger validation error by submitting an empty title.
$edit = ['title' => ''];
$this->drupalPostForm(NULL, $edit, 'Save');
$this->assertText($build_id_initial, 'Old build id on the page');
$this->assertText('No old build id', 'No old build id on the page');
$build_id_first_validation = $this->getFormBuildId();
$this->assertNotEqual($build_id_initial, $build_id_first_validation, 'Build id changes when form validation fails');
@ -74,7 +74,7 @@ class FormStoragePageCacheTest extends WebTestBase {
// Trigger validation error by submitting an empty title.
$edit = ['title' => ''];
$this->drupalPostForm(NULL, $edit, 'Save');
$this->assertText($build_id_initial, 'Old build id is initial build id');
$this->assertText('No old build id', 'No old build id on the page');
$build_id_from_cache_first_validation = $this->getFormBuildId();
$this->assertNotEqual($build_id_initial, $build_id_from_cache_first_validation, 'Build id changes when form validation fails');
$this->assertNotEqual($build_id_first_validation, $build_id_from_cache_first_validation, 'Build id from first user is not reused');
@ -96,10 +96,15 @@ class FormStoragePageCacheTest extends WebTestBase {
$this->assertText('No old build id', 'No old build id on the page');
$build_id_initial = $this->getFormBuildId();
// Trigger rebuild, should regenerate build id.
// Trigger rebuild, should regenerate build id. When a submit handler
// triggers a rebuild, the form is built twice in the same POST request,
// and during the second build, there is an old build ID, but because the
// form is not cached during the initial GET request, it is different from
// that initial build ID.
$edit = ['title' => 'something'];
$this->drupalPostForm(NULL, $edit, 'Rebuild');
$this->assertText($build_id_initial, 'Initial build id as old build id on the page');
$this->assertNoText('No old build id', 'There is no old build id on the page.');
$this->assertNoText($build_id_initial, 'The old build id is not the initial build id.');
$build_id_first_rebuild = $this->getFormBuildId();
$this->assertNotEqual($build_id_initial, $build_id_first_rebuild, 'Build id changes on first rebuild.');

View file

@ -188,7 +188,7 @@ class FormTest extends WebTestBase {
}
// Check the page for error messages.
$errors = $this->xpath('//div[contains(@class, "form-error-message")]//strong');
$errors = $this->xpath('//div[contains(@class, "form-item--error-message")]//strong');
foreach ($errors as $error) {
$expected_key = array_search($error[0], $expected);
// If the error message is not one of the expected messages, fail.

View file

@ -19,7 +19,7 @@ class ModulesListFormWebTest extends WebTestBase {
/**
* {@inheritdoc}
*/
public static $modules = array('system_test');
public static $modules = array('system_test', 'help');
/**
* {@inheritdoc}
@ -33,11 +33,22 @@ class ModulesListFormWebTest extends WebTestBase {
* Tests the module list form.
*/
public function testModuleListForm() {
$this->drupalLogin($this->drupalCreateUser(array('administer modules')));
$this->drupalLogin(
$this->drupalCreateUser(
array('administer modules', 'administer permissions')
)
);
$this->drupalGet('admin/modules');
$this->assertResponse('200');
// Check that system_test's configure link was rendered correctly.
$this->assertFieldByXPath("//a[contains(@href, '/system-test/configure/bar') and @title='Bar.bar']");
// Check that system_test's permissions link was rendered correctly.
$this->assertFieldByXPath("//a[contains(@href, '/admin/people/permissions#module-system_test') and @title='Configure permissions']");
// Check that system_test's help link was rendered correctly.
$this->assertFieldByXPath("//a[contains(@href, '/admin/help/system_test') and @title='Help']");
}
}

View file

@ -73,16 +73,19 @@ class StorageTest extends WebTestBase {
// Use form rebuilding triggered by a submit button.
$this->drupalPostForm(NULL, $edit, 'Continue submit');
// The first one is for the building of the form.
$this->assertText('Form constructions: 2');
// The second one is for the rebuilding of the form.
$this->assertText('Form constructions: 3');
// Reset the form to the values of the storage, using a form rebuild
// triggered by button of type button.
$this->drupalPostForm(NULL, array('title' => 'changed'), 'Reset');
$this->assertFieldByName('title', 'new', 'Values have been reset.');
$this->assertText('Form constructions: 3');
$this->assertText('Form constructions: 4');
$this->drupalPostForm(NULL, $edit, 'Save');
$this->assertText('Form constructions: 3');
$this->assertText('Form constructions: 4');
$this->assertText('Title: new', 'The form storage has stored the values.');
}
@ -129,55 +132,6 @@ class StorageTest extends WebTestBase {
$this->assertText("The thing has been changed.", 'The altered form storage value was updated in cache and taken over.');
}
/**
* Tests a form using form state without using 'storage' to pass data from the
* constructor to a submit handler. The data has to persist even when caching
* gets activated, what may happen when a modules alter the form and adds
* #ajax properties.
*/
function testFormStatePersist() {
// Test the form one time with caching activated and one time without.
$run_options = array(
array(),
array('query' => array('cache' => 1)),
);
foreach ($run_options as $options) {
$this->drupalPostForm('form-test/state-persist', array(), t('Submit'), $options);
// The submit handler outputs the value in $form_state, assert it's there.
$this->assertText('State persisted.');
// Test it again, but first trigger a validation error, then test.
$this->drupalPostForm('form-test/state-persist', array('title' => ''), t('Submit'), $options);
$this->assertText(t('!name field is required.', array('!name' => 'title')));
// Submit the form again triggering no validation error.
$this->drupalPostForm(NULL, array('title' => 'foo'), t('Submit'), $options);
$this->assertText('State persisted.');
// Now post to the rebuilt form and verify it's still there afterwards.
$this->drupalPostForm(NULL, array('title' => 'bar'), t('Submit'), $options);
$this->assertText('State persisted.');
}
}
/**
* Verify that the form build-id remains the same when validation errors
* occur on a mutable form.
*/
public function testMutableForm() {
// Request the form with 'cache' query parameter to enable form caching.
$this->drupalGet('form_test/form-storage', ['query' => ['cache' => 1]]);
$buildIdFields = $this->xpath('//input[@name="form_build_id"]');
$this->assertEqual(count($buildIdFields), 1, 'One form build id field on the page');
$buildId = (string) $buildIdFields[0]['value'];
// Trigger validation error by submitting an empty title.
$edit = ['title' => ''];
$this->drupalPostForm(NULL, $edit, 'Continue submit');
// Verify that the build-id did not change.
$this->assertFieldByName('form_build_id', $buildId, 'Build id remains the same when form validation fails');
}
/**
* Verifies that form build-id is regenerated when loading an immutable form
* from the cache.

View file

@ -276,7 +276,7 @@ class ValidationTest extends WebTestBase {
* - key: The key used for the form element.
*/
protected function assertErrorMessages($messages) {
$element = $this->xpath('//div[@class = "form-error-message"]/strong');
$element = $this->xpath('//div[@class = "form-item--error-message"]/strong');
$this->assertIdentical(count($messages), count($element));
$error_links = [];

View file

@ -125,7 +125,7 @@ ENDPO;
'modules[Core][views][enable]' => TRUE,
'modules[Core][filter][enable]' => TRUE,
);
$this->drupalPostForm('admin/modules', $edit, t('Save configuration'));
$this->drupalPostForm('admin/modules', $edit, t('Install'));
// Verify the strings from the translation are still as expected.
$this->verifyImportedStringsTranslated();

View file

@ -57,6 +57,8 @@ class InstallerTranslationTest extends InstallerTestBase {
$this->assertText('German');
$this->assertNoText('English');
// The current container still has the english as current language, rebuild.
$this->rebuildContainer();
/** @var \Drupal\user\Entity\User $account */
$account = User::load(0);
$this->assertEqual($account->language()->getId(), 'en', 'Anonymous user is English.');
@ -66,7 +68,7 @@ class InstallerTranslationTest extends InstallerTestBase {
$this->assertEqual($account->language()->getId(), 'de', 'New user is German.');
// Ensure that we can enable basic_auth on a non-english site.
$this->drupalPostForm('admin/modules', array('modules[Web services][basic_auth][enable]' => TRUE), t('Save configuration'));
$this->drupalPostForm('admin/modules', array('modules[Web services][basic_auth][enable]' => TRUE), t('Install'));
$this->assertResponse(200);
// Assert that the theme CSS was added to the page.

View file

@ -30,6 +30,8 @@ class LocalActionTest extends WebTestBase {
// Ensure that both menu and route based actions are shown.
$this->assertLocalAction([
[Url::fromRoute('menu_test.local_action4'), 'My dynamic-title action'],
[Url::fromRoute('menu_test.local_action4'), htmlspecialchars("<script>alert('Welcome to the jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')],
[Url::fromRoute('menu_test.local_action4'), htmlspecialchars("<script>alert('Welcome to the derived jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8')],
[Url::fromRoute('menu_test.local_action2'), 'My hook_menu action'],
[Url::fromRoute('menu_test.local_action3'), 'My YAML discovery action'],
[Url::fromRoute('menu_test.local_action5'), 'Title override'],
@ -50,7 +52,11 @@ class LocalActionTest extends WebTestBase {
foreach ($actions as $action) {
/** @var \Drupal\Core\Url $url */
list($url, $title) = $action;
$this->assertEqual((string) $elements[$index], $title);
// SimpleXML gives us the unescaped text, not the actual escaped markup,
// so use a pattern instead to check the raw content.
// This behaviour is a bug in libxml, see
// https://bugs.php.net/bug.php?id=49437.
$this->assertPattern('@<a [^>]*class="[^"]*button-action[^"]*"[^>]*>' . preg_quote($title, '@') . '</@');
$this->assertEqual($elements[$index]['href'], $url->toString());
$index++;
}

View file

@ -47,18 +47,54 @@ class LocalTasksTest extends WebTestBase {
}
}
/**
* Ensures that some local task appears.
*
* @param string $title
* The expected title.
*
* @return bool
* TRUE if the local task exists on the page.
*/
protected function assertLocalTaskAppers($title) {
// SimpleXML gives us the unescaped text, not the actual escaped markup,
// so use a pattern instead to check the raw content.
// This behaviour is a bug in libxml, see
// https://bugs.php.net/bug.php?id=49437.
return $this->assertPattern('@<a [^>]*>' . preg_quote($title, '@') . '</a>@');
}
/**
* Tests the plugin based local tasks.
*/
public function testPluginLocalTask() {
// Verify local tasks defined in the hook.
$this->drupalGet(Url::fromRoute('menu_test.tasks_default'));
$this->assertLocalTasks([
['menu_test.tasks_default', []],
['menu_test.router_test1', ['bar' => 'unsafe']],
['menu_test.router_test1', ['bar' => '1']],
['menu_test.router_test2', ['bar' => '2']],
]);
// Verify that script tags are escaped on output.
$title = htmlspecialchars("Task 1 <script>alert('Welcome to the jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$this->assertLocalTaskAppers($title);
$title = htmlspecialchars("<script>alert('Welcome to the derived jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$this->assertLocalTaskAppers($title);
// Verify that local tasks appear as defined in the router.
$this->drupalGet(Url::fromRoute('menu_test.local_task_test_tasks_view'));
$this->assertLocalTasks([
['menu_test.local_task_test_tasks_view', []],
['menu_test.local_task_test_tasks_edit', []],
['menu_test.local_task_test_tasks_settings', []],
['menu_test.local_task_test_tasks_settings_dynamic', []],
]);
$title = htmlspecialchars("<script>alert('Welcome to the jungle!')</script>", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$this->assertLocalTaskAppers($title);
// Ensure the view tab is active.
$result = $this->xpath('//ul[contains(@class, "tabs")]//li[contains(@class, "active")]/a');
$this->assertEqual(1, count($result), 'There is just a single active tab.');

View file

@ -0,0 +1,45 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Menu\MenuLinkSecurityTest.
*/
namespace Drupal\system\Tests\Menu;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\simpletest\WebTestBase;
/**
* Ensures that menu links don't cause XSS issues.
*
* @group Menu
*/
class MenuLinkSecurityTest extends WebTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['menu_link_content', 'block', 'menu_test'];
/**
* Ensures that a menu link does not cause an XSS issue.
*/
public function testMenuLink() {
$menu_link_content = MenuLinkContent::create([
'title' => '<script>alert("Wild animals")</script>',
'menu_name' => 'tools',
'link' => ['uri' => 'route:<front>'],
]);
$menu_link_content->save();
$this->drupalPlaceBlock('system_menu_block:tools');
$this->drupalGet('<front>');
$this->assertNoRaw('<script>alert("Wild animals")</script>');
$this->assertNoRaw('<script>alert("Even more wild animals")</script>');
$this->assertEscaped('<script>alert("Wild animals")</script>');
$this->assertEscaped('<script>alert("Even more wild animals")</script>');
}
}

View file

@ -72,6 +72,8 @@ class MenuRouterTest extends WebTestBase {
// Confirm local task links are displayed.
$this->assertLink('Local task A');
$this->assertLink('Local task B');
$this->assertNoLink('Local task C');
$this->assertEscaped("<script>alert('Welcome to the jungle!')</script>", ENT_QUOTES, 'UTF-8');
// Confirm correct local task href.
$this->assertLinkByHref(Url::fromRoute('menu_test.router_test1', ['bar' => $machine_name])->toString());
$this->assertLinkByHref(Url::fromRoute('menu_test.router_test2', ['bar' => $machine_name])->toString());

View file

@ -354,7 +354,6 @@ class MenuTreeStorageTest extends KernelTestBase {
'menu_name' => $menu_name,
'route_name' => $route_name,
'route_parameters' => $route_parameters,
'title_arguments' => array(),
'title' => 'test',
'parent' => $parent,
'options' => array(),

View file

@ -0,0 +1,59 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateDateFormatTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\migrate\MigrateExecutable;
use Drupal\Core\Database\Database;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade date formats to core.date_format.*.yml.
*
* @group system
*/
class MigrateDateFormatTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_date_formats');
}
/**
* Tests the Drupal 6 date formats to Drupal 8 migration.
*/
public function testDateFormats() {
$short_date_format = entity_load('date_format', 'short');
$this->assertIdentical('\S\H\O\R\T m/d/Y - H:i', $short_date_format->getPattern());
$medium_date_format = entity_load('date_format', 'medium');
$this->assertIdentical('\M\E\D\I\U\M D, m/d/Y - H:i', $medium_date_format->getPattern());
$long_date_format = entity_load('date_format', 'long');
$this->assertIdentical('\L\O\N\G l, F j, Y - H:i', $long_date_format->getPattern());
// Test that we can re-import using the EntityDateFormat destination.
Database::getConnection('default', 'migrate')
->update('variable')
->fields(array('value' => serialize('\S\H\O\R\T d/m/Y - H:i')))
->condition('name', 'date_format_short')
->execute();
db_truncate(entity_load('migration', 'd6_date_formats')->getIdMap()->mapTableName())->execute();
$migration = entity_load_unchanged('migration', 'd6_date_formats');
$executable = new MigrateExecutable($migration, $this);
$executable->import();
$short_date_format = entity_load('date_format', 'short');
$this->assertIdentical('\S\H\O\R\T d/m/Y - H:i', $short_date_format->getPattern());
}
}

View file

@ -0,0 +1,59 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateMenuTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\migrate\MigrateExecutable;
use Drupal\Core\Database\Database;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
use Drupal\system\Entity\Menu;
/**
* Upgrade menus to system.menu.*.yml.
*
* @group system
*/
class MigrateMenuTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['MenuCustom.php']);
$this->executeMigration('d6_menu');
}
/**
* Tests the Drupal 6 menu to Drupal 8 migration.
*/
public function testMenu() {
$navigation_menu = Menu::load('navigation');
$this->assertIdentical('navigation', $navigation_menu->id());
$this->assertIdentical('Navigation', $navigation_menu->label());
$expected = <<<EOT
The navigation menu is provided by Drupal and is the main interactive menu for any site. It is usually the only menu that contains personalized links for authenticated users, and is often not even visible to anonymous users.
EOT;
$this->assertIdentical($expected, $navigation_menu->getDescription());
// Test that we can re-import using the ConfigEntityBase destination.
Database::getConnection('default', 'migrate')
->update('menu_custom')
->fields(array('title' => 'Home Navigation'))
->condition('menu_name', 'navigation')
->execute();
db_truncate(entity_load('migration', 'd6_menu')->getIdMap()->mapTableName())->execute();
$migration = entity_load_unchanged('migration', 'd6_menu');
$executable = new MigrateExecutable($migration, $this);
$executable->import();
$navigation_menu = entity_load_unchanged('menu', 'navigation');
$this->assertIdentical('Home Navigation', $navigation_menu->label());
}
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateSystemCronTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade cron variable to system.*.yml.
*
* @group system
*/
class MigrateSystemCronTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_cron');
}
/**
* Tests migration of system (cron) variables to system.cron.yml.
*/
public function testSystemCron() {
$config = $this->config('system.cron');
$this->assertIdentical(172800, $config->get('threshold.requirements_warning'));
$this->assertIdentical(1209600, $config->get('threshold.requirements_error'));
}
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateSystemFileTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade variables to system.*.yml.
*
* @group system
*/
class MigrateSystemFileTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_file');
}
/**
* Tests migration of system (file) variables to system.file.yml.
*/
public function testSystemFile() {
$config = \Drupal::configFactory()->getEditable('system.file');
$this->assertIdentical('files/temp', $config->get('path.temporary'));
$this->assertIdentical(TRUE, $config->get('allow_insecure_uploads'));
}
}

View file

@ -0,0 +1,36 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateSystemImageGdTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade image gd variables to system.*.yml.
*
* @group system
*/
class MigrateSystemImageGdTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_image_gd');
}
/**
* Tests migration of system (image GD) variables to system.image.gd.yml.
*/
public function testSystemImageGd() {
$config = $this->config('system.image.gd');
$this->assertIdentical(75, $config->get('jpeg_quality'));
}
}

View file

@ -0,0 +1,36 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateSystemImageTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade image variables to system.*.yml.
*
* @group system
*/
class MigrateSystemImageTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_image');
}
/**
* Tests migration of system (image) variables to system.image.yml.
*/
public function testSystemImage() {
$config = $this->config('system.image');
$this->assertIdentical('gd', $config->get('toolkit'));
}
}

View file

@ -0,0 +1,40 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateSystemLoggingTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\config\Tests\SchemaCheckTestTrait;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade error_level variable to system.logging.yml.
*
* @group system
*/
class MigrateSystemLoggingTest extends MigrateDrupal6TestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_logging');
}
/**
* Tests migration of system error_level variables to system.logging.yml.
*/
public function testSystemLogging() {
$config = $this->config('system.logging');
$this->assertIdentical('some', $config->get('error_level'));
$this->assertConfigSchema(\Drupal::service('config.typed'), 'system.logging', $config->get());
}
}

View file

@ -0,0 +1,36 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateSystemMaintenanceTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade maintenance variables to system.*.yml.
*
* @group system
*/
class MigrateSystemMaintenanceTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_maintenance');
}
/**
* Tests migration of system (maintenance) variables to system.maintenance.yml.
*/
public function testSystemMaintenance() {
$config = $this->config('system.maintenance');
$this->assertIdentical('Drupal is currently under maintenance. We should be back shortly. Thank you for your patience.', $config->get('message'));
}
}

View file

@ -0,0 +1,38 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateSystemPerformanceTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade performance variables to system.*.yml.
*
* @group system
*/
class MigrateSystemPerformanceTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_performance');
}
/**
* Tests migration of system (Performance) variables to system.performance.yml.
*/
public function testSystemPerformance() {
$config = $this->config('system.performance');
$this->assertIdentical(FALSE, $config->get('css.preprocess'));
$this->assertIdentical(FALSE, $config->get('js.preprocess'));
$this->assertIdentical(0, $config->get('cache.page.max_age'));
}
}

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Migrate\d6\MigrateSystemRssTest.
*/
namespace Drupal\system\Tests\Migrate\d6;
use Drupal\migrate_drupal\Tests\d6\MigrateDrupal6TestBase;
/**
* Upgrade rss variable to system.*.yml.
*
* @group system
*/
class MigrateSystemRssTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->loadDumps(['Variable.php']);
$this->executeMigration('d6_system_rss');
}
/**
* Tests migration of system (rss) variables to system.rss.yml.
*/
public function testSystemRss() {
$config = $this->config('system.rss');
$this->assertIdentical(10, $config->get('items.limit'));
$this->assertIdentical('title', $config->get('items.view_mode'));
}
}

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