Update to Drupal 8.0.0-beta15. For more information, see: https://www.drupal.org/node/2563023

This commit is contained in:
Pantheon Automation 2015-09-04 13:20:09 -07:00 committed by Greg Anderson
parent 2720a9ec4b
commit f3791f1da3
1898 changed files with 54300 additions and 11481 deletions

View file

@ -0,0 +1,20 @@
uuid: ee7c230c-337b-4e8f-8600-d65bfd34f171
langcode: en
status: true
dependencies:
theme:
- seven
id: seven_local_actions
theme: seven
region: content
weight: -10
provider: null
plugin: local_actions_block
settings:
id: local_actions_block
label: 'Primary admin actions'
label_display: '0'
cache:
max_age: 0
status: true
visibility: { }

View file

@ -27,4 +27,4 @@ visibility:
page: page
negate: false
context_mapping:
baloney: baloney.spam
baloney: baloney_spam

View file

@ -0,0 +1,49 @@
<?php
/**
* @file
* Partial database to mimic the installation of the block_test module.
*/
use Drupal\Core\Database\Database;
use Symfony\Component\Yaml\Yaml;
$connection = Database::getConnection();
// Set the schema version.
$connection->insert('key_value')
->fields([
'collection' => 'system.schema',
'name' => 'block_test',
'value' => 'i:8000;',
])
->execute();
// Update core.extension.
$extensions = $connection->select('config')
->fields('config', ['data'])
->condition('collection', '')
->condition('name', 'core.extension')
->execute()
->fetchField();
$extensions = unserialize($extensions);
$extensions['module']['block_test'] = 8000;
$connection->update('config')
->fields([
'data' => serialize($extensions),
])
->condition('collection', '')
->condition('name', 'core.extension')
->execute();
// Install the block configuration.
$config = file_get_contents(__DIR__ . '/../../../../block/tests/modules/block_test/config/install/block.block.test_block.yml');
$config = Yaml::parse($config);
$connection->insert('config')
->fields(['data', 'name', 'collection'])
->values([
'name' => 'block.block.test_block',
'data' => serialize($config),
'collection' => '',
])
->execute();

View file

@ -0,0 +1,18 @@
<?php
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
$config = unserialize($connection->query("SELECT data FROM {config} where name = :name", [':name' => 'core.extension'])->fetchField());
$config['module']['update_script_test'] = 0;
$connection->update('config')
->fields(['data' => serialize($config)])
->condition('name', 'core.extension')
->execute();
$connection->insert('key_value')
->fields(['collection' => 'system.schema', 'name' => 'update_script_test', 'value' => serialize(8000)])
->execute();

View file

@ -0,0 +1,60 @@
<?php
/**
* @file
* Contains database additions to drupal-8.bare.standard.php.gz for testing the
* upgrade path of https://www.drupal.org/node/507488.
*/
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
// Structure of a custom block with visibility settings.
$block_configs[] = \Drupal\Component\Serialization\Yaml::decode(file_get_contents(__DIR__ . '/block.block.testfor507488.yml'));
foreach ($block_configs as $block_config) {
$connection->insert('config')
->fields([
'collection',
'name',
'data',
])
->values([
'collection' => '',
'name' => 'block.block.' . $block_config['id'],
'data' => serialize($block_config),
])
->execute();
}
// Update the config entity query "index".
$existing_blocks = $connection->select('key_value')
->fields('key_value', ['value'])
->condition('collection', 'config.entity.key_store.block')
->condition('name', 'theme:seven')
->execute()
->fetchField();
$existing_blocks = unserialize($existing_blocks);
$connection->update('key_value')
->fields([
'value' => serialize(array_merge($existing_blocks, ['block.block.seven_local_actions']))
])
->condition('collection', 'config.entity.key_store.block')
->condition('name', 'theme:seven')
->execute();
// Enable test theme.
$extensions = $connection->select('config')
->fields('config', ['data'])
->condition('name', 'core.extension')
->execute()
->fetchField();
$extensions = unserialize($extensions);
$connection->update('config')
->fields([
'data' => serialize(array_merge_recursive($extensions, ['theme' => ['test_theme' => 0]]))
])
->condition('name', 'core.extension')
->execute();

View file

@ -0,0 +1,54 @@
<?php
/**
* @file
* Partial database to mimic the installation of the update_test_schema module.
*/
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
// Create the table.
$connection->schema()->createTable('update_test_schema_table', array(
'fields' => array(
'a' => array(
'type' => 'int',
'not null' => TRUE,
'size' => 'normal',
),
'b' => array(
'type' => 'blob',
'not null' => FALSE,
'size' => 'normal',
),
),
));
// Set the schema version.
$connection->merge('key_value')
->condition('collection', 'system.schema')
->condition('name', 'update_test_schema')
->fields([
'collection' => 'system.schema',
'name' => 'update_test_schema',
'value' => 'i:8000;',
])
->execute();
// Update core.extension.
$extensions = $connection->select('config')
->fields('config', ['data'])
->condition('collection', '')
->condition('name', 'core.extension')
->execute()
->fetchField();
$extensions = unserialize($extensions);
$extensions['module']['update_test_schema'] = 8000;
$connection->update('config')
->fields([
'data' => serialize($extensions),
])
->condition('collection', '')
->condition('name', 'core.extension')
->execute();

View file

@ -0,0 +1,31 @@
<?php
/**
* @file
* Contains database additions to drupal-8.bare.standard.php.gz for testing the
* upgrade path of https://www.drupal.org/node/2455125.
*/
use Drupal\Core\Database\Database;
$connection = Database::getConnection();
// Structure of a view with timestamp fields.
$views_configs = [];
$views_configs[] = \Drupal\Component\Serialization\Yaml::decode(file_get_contents(__DIR__ . '/drupal-8.views-entity-views-data-2455125.yml'));
foreach ($views_configs as $views_config) {
$connection->insert('config')
->fields(array(
'collection',
'name',
'data',
))
->values(array(
'collection' => '',
'name' => 'views.view.' . $views_config['id'],
'data' => serialize($views_config),
))
->execute();
}

View file

@ -0,0 +1,294 @@
uuid: e693b165-0e14-4dee-9909-9f0892037c23
langcode: en
status: true
dependencies:
module:
- user
id: update_test
label: 'Update Test'
module: views
description: ''
tag: ''
base_table: users_field_data
base_field: uid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'access user profiles'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: none
options:
items_per_page: 0
offset: 0
style:
type: default
row:
type: fields
fields:
name:
id: name
table: users_field_data
field: name
entity_type: user
entity_field: name
label: ''
alter:
alter_text: false
make_link: false
absolute: false
trim: false
word_boundary: false
ellipsis: false
strip_tags: false
html: false
hide_empty: false
empty_zero: false
plugin_id: field
relationship: none
group_type: group
admin_label: ''
exclude: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: true
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_alter_empty: true
click_sort_column: value
type: user_name
settings: { }
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
created:
id: created
table: users_field_data
field: created
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
date_format: long
custom_date_format: ''
timezone: Africa/Abidjan
entity_type: user
entity_field: created
plugin_id: date
created_1:
id: created_1
table: users_field_data
field: created
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
date_format: 'raw time ago'
custom_date_format: ''
timezone: ''
entity_type: user
entity_field: created
plugin_id: date
created_2:
id: created_2
table: users_field_data
field: created
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
date_format: 'time ago'
custom_date_format: ''
timezone: ''
entity_type: user
entity_field: created
plugin_id: date
filters: { }
sorts: { }
title: 'Update Test'
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- user.permissions
cacheable: false
block_1:
display_plugin: block
id: block_1
display_title: Block
position: 1
display_options:
display_extenders: { }
allow:
items_per_page: false
cache_metadata:
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- user.permissions
cacheable: false

View file

@ -5,7 +5,9 @@
* Batch callbacks for the Batch API tests.
*/
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Implements callback_batch_operation().
@ -86,10 +88,9 @@ function _batch_test_nested_batch_callback() {
* Provides a common 'finished' callback for batches 1 to 4.
*/
function _batch_test_finished_helper($batch_id, $success, $results, $operations) {
$messages = array("results for batch $batch_id");
if ($results) {
foreach ($results as $op => $op_results) {
$messages[] = 'op '. SafeMarkup::escape($op) . ': processed ' . count($op_results) . ' elements';
$messages[] = 'op '. Html::escape($op) . ': processed ' . count($op_results) . ' elements';
}
}
else {
@ -102,7 +103,20 @@ function _batch_test_finished_helper($batch_id, $success, $results, $operations)
$messages[] = t('An error occurred while processing @op with arguments:<br />@args', array('@op' => $error_operation[0], '@args' => print_r($error_operation[1], TRUE)));
}
drupal_set_message(SafeMarkup::set(implode('<br>', $messages)));
// Use item list template to render the messages.
$error_message = [
'#type' => 'inline_template',
'#template' => 'results for batch {{ batch_id }}{{ errors }}',
'#context' => [
'batch_id' => $batch_id,
'errors' => [
'#theme' => 'item_list',
'#items' => $messages,
],
],
];
drupal_set_message(\Drupal::service('renderer')->renderPlain($error_message));
}
/**
@ -123,6 +137,16 @@ function _batch_test_finished_1($success, $results, $operations) {
_batch_test_finished_helper(1, $success, $results, $operations);
}
/**
* Implements callback_batch_finished().
*
* Triggers 'finished' callback for batch 1.
*/
function _batch_test_finished_1_finished($success, $results, $operations) {
_batch_test_finished_helper(1, $success, $results, $operations);
return new RedirectResponse(Url::fromRoute('test_page_test.test_page', [], ['absolute' => TRUE])->toString());
}
/**
* Implements callback_batch_finished().
*

View file

@ -31,6 +31,14 @@ batch_test.no_form:
requirements:
_access: 'TRUE'
batch_test.finish_redirect:
path: '/batch-test/finish-redirect'
defaults:
_controller: '\Drupal\batch_test\Controller\BatchTestController::testFinishRedirect'
_title: 'Simple page with finish redirect call'
requirements:
_access: 'TRUE'
batch_test.test_form:
path: '/batch-test'
defaults:

View file

@ -72,6 +72,21 @@ class BatchTestController {
}
/**
* Fires a batch process without a form submission and a finish redirect.
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse|null
* A redirect response if the batch is progressive. No return value otherwise.
*/
public function testFinishRedirect() {
batch_test_stack(NULL, TRUE);
$batch = _batch_test_batch_1();
$batch['finished'] = '_batch_test_finished_1_finished';
batch_set($batch);
return batch_process('batch-test/redirect');
}
/**
* Submits the 'Chained' form programmatically.
*

View file

@ -1,6 +1,7 @@
common_test.l_active_class:
path: '/common-test/type-link-active-class'
defaults:
_title: 'Test active link class'
_controller: '\Drupal\common_test\Controller\CommonTestController::typeLinkActiveClass'
requirements:
_access: 'TRUE'

View file

@ -0,0 +1,6 @@
services:
main_content_renderer.json:
class: Drupal\common_test\Render\MainContent\JsonRenderer
arguments: ['@title_resolver', '@renderer']
tags:
- { name: render.main_content_renderer, format: json }

View file

@ -7,7 +7,7 @@
namespace Drupal\common_test\Controller;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\Response;
@ -89,7 +89,7 @@ class CommonTestController {
*/
public function destination() {
$destination = \Drupal::destination()->getAsArray();
$output = "The destination: " . SafeMarkup::checkPlain($destination['destination']);
$output = "The destination: " . Html::escape($destination['destination']);
return new Response($output);
}

View file

@ -0,0 +1,69 @@
<?php
/**
* @file
* Contains \Drupal\common_test\Render\MainContent\JsonRenderer.
*/
namespace Drupal\common_test\Render\MainContent;
use Drupal\Core\Cache\CacheableJsonResponse;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Controller\TitleResolverInterface;
use Drupal\Core\Render\MainContent\MainContentRendererInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Default main content renderer for JSON requests.
*/
class JsonRenderer implements MainContentRendererInterface {
/**
* The title resolver.
*
* @var \Drupal\Core\Controller\TitleResolverInterface
*/
protected $titleResolver;
/**
* The renderer service.
*
* @var \Drupal\Core\Render\RendererInterface
*/
protected $renderer;
/**
* Constructs a new JsonRenderer.
*
* @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver
* The title resolver.
* @param \Drupal\Core\Render\RendererInterface $renderer
* The renderer service.
*/
public function __construct(TitleResolverInterface $title_resolver, RendererInterface $renderer) {
$this->titleResolver = $title_resolver;
$this->renderer = $renderer;
}
/**
* {@inheritdoc}
*/
public function renderResponse(array $main_content, Request $request, RouteMatchInterface $route_match) {
$json = [];
$json['content'] = (string) $this->renderer->renderRoot($main_content);
if (!empty($main_content['#title'])) {
$json['title'] = (string) $main_content['#title'];
}
else {
$json['title'] = (string) $this->titleResolver->getTitle($request, $route_match->getRouteObject());
}
$response = new CacheableJsonResponse($json, 200);
$response->addCacheableDependency(CacheableMetadata::createFromRenderArray($main_content));
return $response;
}
}

View file

@ -7,7 +7,6 @@
namespace Drupal\database_test\Form;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\Entity\User;
@ -56,8 +55,8 @@ class DatabaseTestForm extends FormBase {
foreach (User::loadMultiple($uids) as $account) {
$options[$account->id()] = array(
'title' => array('data' => array('#title' => SafeMarkup::checkPlain($account->getUsername()))),
'username' => SafeMarkup::checkPlain($account->getUsername()),
'title' => array('data' => array('#title' => $account->getUsername())),
'username' => $account->getUsername(),
'status' => $account->isActive() ? t('active') : t('blocked'),
);
}

View file

@ -5,6 +5,8 @@
* Install, update and uninstall functions for the entity_test module.
*/
use Drupal\system\Tests\Update\DbUpdatesTrait;
/**
* Implements hook_install().
*/
@ -49,3 +51,6 @@ function entity_test_schema() {
);
return $schema;
}
DbUpdatesTrait::includeUpdates('entity_test', 'entity_definition_updates');
DbUpdatesTrait::includeUpdates('entity_test', 'status_report');

View file

@ -12,6 +12,7 @@ use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
@ -119,14 +120,25 @@ function entity_test_entity_base_field_info(EntityTypeInterface $entity_type) {
* Implements hook_entity_base_field_info_alter().
*/
function entity_test_entity_base_field_info_alter(&$fields, EntityTypeInterface $entity_type) {
if ($entity_type->id() == 'entity_test_mulrev' && ($names = \Drupal::state()->get('entity_test.field_definitions.translatable'))) {
$state = \Drupal::state();
if ($entity_type->id() == 'entity_test_mulrev' && ($names = $state->get('entity_test.field_definitions.translatable'))) {
foreach ($names as $name => $value) {
$fields[$name]->setTranslatable($value);
}
}
if ($entity_type->id() == 'node' && Drupal::state()->get('entity_test.node_remove_status_field')) {
if ($entity_type->id() == 'node' && $state->get('entity_test.node_remove_status_field')) {
unset($fields['status']);
}
if ($entity_type->id() == 'entity_test' && $state->get('entity_test.remove_name_field')) {
unset($fields['name']);
}
// In 8001 we are assuming that a new definition with multiple cardinality has
// been deployed.
// @todo Remove this if we end up using state definitions at runtime. See
// https://www.drupal.org/node/2554235.
if ($entity_type->id() == 'entity_test' && $state->get('entity_test.db_updates.entity_definition_updates') == 8001) {
$fields['user_id']->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
}
}
/**
@ -600,7 +612,7 @@ function entity_test_field_default_value(FieldableEntityInterface $entity, Field
* @param string $hook
* The hook name.
* @param mixed $data
* Arbitrary data associated to the hook invocation.
* Arbitrary data associated with the hook invocation.
*/
function _entity_test_record_hooks($hook, $data) {
$state = \Drupal::state();

View file

@ -4,7 +4,7 @@ entity.entity_test.canonical:
_entity_view: 'entity_test.full'
_title: 'Test full view mode'
requirements:
_access: 'TRUE'
_entity_access: 'entity_test.view'
entity.entity_test.render_options:
path: '/entity_test_converter/{foo}'
@ -15,7 +15,7 @@ entity.entity_test.render_options:
defaults:
_entity_view: 'entity_test.full'
requirements:
_access: 'TRUE'
_entity_access: 'foo.view'
entity.entity_test.render_no_view_mode:
path: '/entity_test_no_view_mode/{entity_test}'

View file

@ -23,7 +23,6 @@ use Drupal\Core\Entity\EntityTypeInterface;
* },
* base_table = "entity_test_update",
* revision_table = "entity_test_update_revision",
* fieldable = TRUE,
* persistent_cache = FALSE,
* entity_keys = {
* "id" = "id",

View file

@ -30,7 +30,7 @@ class EntityTestListBuilder extends EntityListBuilder {
* {@inheritdoc}
*/
public function buildRow(EntityInterface $entity) {
$row['label'] = $this->getLabel($entity);
$row['label'] = $entity->label();
$row['id'] = $entity->id();
return $row + parent::buildRow($entity);
}

View file

@ -7,7 +7,6 @@
namespace Drupal\entity_test;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityViewBuilder;
@ -36,7 +35,7 @@ class EntityTestViewBuilder extends EntityViewBuilder {
foreach ($entities as $id => $entity) {
$build[$id]['label'] = array(
'#weight' => -100,
'#markup' => SafeMarkup::checkPlain($entity->label()),
'#plain_text' => $entity->label(),
);
$build[$id]['separator'] = array(
'#weight' => -150,
@ -44,7 +43,7 @@ class EntityTestViewBuilder extends EntityViewBuilder {
);
$build[$id]['view_mode'] = array(
'#weight' => -200,
'#markup' => SafeMarkup::checkPlain($view_mode),
'#plain_text' => $view_mode,
);
}
}

View file

@ -7,7 +7,6 @@
namespace Drupal\entity_test;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Entity\EntityInterface;
/**
@ -20,7 +19,7 @@ class EntityTestViewBuilderOverriddenView extends EntityTestViewBuilder {
*/
public function view(EntityInterface $entity, $view_mode = 'full', $langcode = NULL) {
$build = [];
$build[$entity->id()]['#markup'] = SafeMarkup::checkPlain($entity->label());
$build[$entity->id()]['#plain_text'] = $entity->label();
return $build;
}

View file

@ -60,7 +60,8 @@ class EntityTestRoutes {
$routes["entity.$entity_type_id.admin_form"] = new Route(
"$entity_type_id/structure/{bundle}",
array('_controller' => '\Drupal\entity_test\Controller\EntityTestController::testAdmin'),
array('_permission' => 'administer entity_test content')
array('_permission' => 'administer entity_test content'),
array('_admin_route' => TRUE)
);
}
return $routes;

View file

@ -0,0 +1,43 @@
<?php
/**
* @file
* Defines the 8001 db update for the "entity_definition_updates" group.
*/
use Drupal\Core\Field\FieldStorageDefinitionInterface;
/**
* Makes the 'user_id' field multiple and migrate its data.
*/
function entity_test_update_8001() {
// To update the field schema we need to have no field data in the storage,
// thus we retrieve it, delete it from storage, and write it back to the
// storage after updating the schema.
$database = \Drupal::database();
// Retrieve existing field data.
$user_ids = $database->select('entity_test', 'et')
->fields('et', ['id', 'user_id'])
->execute()
->fetchAllKeyed();
// Remove data from the storage.
$database->update('entity_test')
->fields(['user_id' => NULL])
->execute();
// Update definitions and schema.
$manager = \Drupal::entityDefinitionUpdateManager();
$storage_definition = $manager->getFieldStorageDefinition('user_id', 'entity_test');
$storage_definition->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
$manager->updateFieldStorageDefinition($storage_definition);
// Restore entity data in the new schema.
$insert_query = $database->insert('entity_test__user_id')
->fields(['bundle', 'deleted', 'entity_id', 'revision_id', 'langcode', 'delta', 'user_id_target_id']);
foreach ($user_ids as $id => $user_id) {
$insert_query->values(['entity_test', 0, $id, $id, 'en', 0, $user_id]);
}
$insert_query->execute();
}

View file

@ -0,0 +1,41 @@
<?php
/**
* @file
* Defines the 8002 db update for the "entity_definition_updates" group.
*/
require_once 'entity_definition_updates_8001.inc';
/**
* Makes the 'user_id' field single and migrate its data.
*/
function entity_test_update_8002() {
// To update the field schema we need to have no field data in the storage,
// thus we retrieve it, delete it from storage, and write it back to the
// storage after updating the schema.
$database = \Drupal::database();
// Retrieve existing entity data.
$query = $database->select('entity_test__user_id', 'et')
->fields('et', ['entity_id', 'user_id_target_id']);
$query->condition('et.delta', 0);
$user_ids = $query->execute()->fetchAllKeyed();
// Remove data from the storage.
$database->truncate('entity_test__user_id')->execute();
// Update definitions and schema.
$manager = \Drupal::entityDefinitionUpdateManager();
$storage_definition = $manager->getFieldStorageDefinition('user_id', 'entity_test');
$storage_definition->setCardinality(1);
$manager->updateFieldStorageDefinition($storage_definition);
// Restore entity data in the new schema.
foreach ($user_ids as $id => $user_id) {
$database->update('entity_test')
->fields(['user_id' => $user_id])
->condition('id', $id)
->execute();
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* @file
* Defines the 8001 db update for the "status_report" group.
*/
/**
* Test update.
*/
function entity_test_update_8001() {
// Empty update, we just want to trigger an error in the status report.
}

View file

@ -0,0 +1,15 @@
<?php
/**
* @file
* Defines the 8002 db update for the "status_report" group.
*/
require_once 'status_report_8001.inc';
/**
* Test update.
*/
function entity_test_update_8002() {
// Empty update, we just want to trigger an update run.
}

View file

@ -6,3 +6,8 @@ services:
# Set up a service with a missing class dependency.
broken_class_with_missing_dependency:
class: Drupal\error_service_test\LonelyMonkeyClass
logger.broken:
class: Drupal\error_service_test\Logger\TestLog
tags:
- { name: logger }
- { name: backend_overridable }

View file

@ -0,0 +1,37 @@
<?php
/**
* @file
* Contains \Drupal\error_service_test\Logger\TestLog.
*/
namespace Drupal\error_service_test\Logger;
use Drupal\Core\Logger\RfcLoggerTrait;
use Psr\Log\LoggerInterface;
/**
* Throws an exception while logging an exception.
*
* @see \Drupal\system\Tests\System\UncaughtExceptionTest::testLoggerException()
*/
class TestLog implements LoggerInterface {
use RfcLoggerTrait;
/**
* {@inheritdoc}
*/
public function log($level, $message, array $context = array()) {
$trigger = [
'%type' => 'Exception',
'@message' => 'Deforestation',
'%function' => 'Drupal\error_service_test\MonkeysInTheControlRoom->handle()',
'severity_level' => 3,
'channel' => 'php',
];
if (array_diff_assoc($trigger, $context) === []) {
throw new \Exception('Oh, oh, frustrated monkeys!');
}
}
}

View file

@ -57,6 +57,10 @@ class MonkeysInTheControlRoom implements HttpKernelInterface {
throw new \Exception('Oh oh, bananas in the instruments.');
}
if (\Drupal::state()->get('error_service_test.break_logger')) {
throw new \Exception('Deforestation');
}
return $this->app->handle($request, $type, $catch);
}

View file

@ -51,8 +51,8 @@ class ErrorTestController extends ControllerBase {
$monkey_love = $bananas;
// This will generate a warning.
$awesomely_big = 1/0;
// This will generate a user error.
trigger_error("Drupal is awesome", E_USER_WARNING);
// This will generate a user error. Use & to check for double escaping.
trigger_error("Drupal & awesome", E_USER_WARNING);
return [];
}
@ -72,7 +72,7 @@ class ErrorTestController extends ControllerBase {
*/
public function triggerException() {
define('SIMPLETEST_COLLECT_ERRORS', FALSE);
throw new \Exception("Drupal is awesome");
throw new \Exception("Drupal & awesome");
}
/**

View file

@ -93,3 +93,14 @@ function form_test_user_register_form_rebuild($form, FormStateInterface $form_st
drupal_set_message('Form rebuilt.');
$form_state->setRebuild();
}
/**
* Implements hook_form_FORM_ID_alter() for form_test_vertical_tabs_access_form().
*/
function form_test_form_form_test_vertical_tabs_access_form_alter(&$form, &$form_state, $form_id) {
$form['vertical_tabs1']['#access'] = FALSE;
$form['vertical_tabs2']['#access'] = FALSE;
$form['tabs3']['#access'] = TRUE;
$form['fieldset1']['#access'] = FALSE;
$form['container']['#access'] = FALSE;
}

View file

@ -172,6 +172,14 @@ form_test.storage:
requirements:
_access: 'TRUE'
form_test.vertical_tabs_access:
path: '/form_test/vertical-tabs-access'
defaults:
_form: '\Drupal\form_test\Form\FormTestVerticalTabsAccessForm'
_title: 'Vertical tabs tests'
requirements:
_access: 'TRUE'
form_test.state_clean:
path: '/form_test/form-state-values-clean'
defaults:

View file

@ -142,6 +142,15 @@ class FormTestDisabledElementsForm extends FormBase {
'#date_timezone' => 'Europe/Berlin',
);
$form['disabled_container']['disabled_container_date'] = array(
'#type' => 'date',
'#title' => 'date',
'#default_value' => '2001-01-13',
'#expected_value' => '2001-01-13',
'#test_hijack_value' => '2013-01-01',
'#date_timezone' => 'Europe/Berlin',
);
// Try to hijack the email field with a valid email.
$form['disabled_container']['disabled_container_email'] = array(

View file

@ -33,7 +33,10 @@ class FormTestRequiredAttributeForm extends FormBase {
'#title' => $type,
);
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'Submit',
);
return $form;
}

View file

@ -7,7 +7,7 @@
namespace Drupal\form_test\Form;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
@ -139,7 +139,7 @@ class FormTestStorageForm extends FormBase {
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
drupal_set_message("Title: " . SafeMarkup::checkPlain($form_state->getValue('title')));
drupal_set_message("Title: " . Html::escape($form_state->getValue('title')));
drupal_set_message("Form constructions: " . $_SESSION['constructions']);
if ($form_state->has(['thing', 'changed'])) {
drupal_set_message("The thing has been changed.");

View file

@ -7,7 +7,6 @@
namespace Drupal\form_test\Form;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
@ -57,7 +56,7 @@ class FormTestStoragePageCacheForm extends FormBase {
*/
function form_test_storage_page_cache_old_build_id($form) {
if (isset($form['#build_id_old'])) {
$form['test_build_id_old']['#markup'] = SafeMarkup::checkPlain($form['#build_id_old']);
$form['test_build_id_old']['#plain_text'] = $form['#build_id_old'];
}
return $form;
}

View file

@ -0,0 +1,136 @@
<?php
/**
* @file
* Contains \Drupal\form_test\Form\FormTestVerticalTabsAccessForm.
*/
namespace Drupal\form_test\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class FormTestVerticalTabsAccessForm extends FormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'form_test_vertical_tabs_access_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['vertical_tabs1'] = array(
'#type' => 'vertical_tabs',
);
$form['tab1'] = array(
'#type' => 'fieldset',
'#title' => t('Tab 1'),
'#collapsible' => TRUE,
'#group' => 'vertical_tabs1',
);
$form['tab1']['field1'] = array(
'#title' => t('Field 1'),
'#type' => 'checkbox',
'#default_value' => TRUE,
);
$form['tab2'] = array(
'#type' => 'fieldset',
'#title' => t('Tab 2'),
'#collapsible' => TRUE,
'#group' => 'vertical_tabs1',
);
$form['tab2']['field2'] = array(
'#title' => t('Field 2'),
'#type' => 'textfield',
'#default_value' => 'field2',
);
$form['fieldset1'] = array(
'#type' => 'fieldset',
'#title' => t('Fieldset'),
);
$form['fieldset1']['field3'] = array(
'#type' => 'checkbox',
'#title' => t('Field 3'),
'#default_value' => TRUE,
);
$form['container'] = array(
'#type' => 'container',
);
$form['container']['field4'] = array(
'#type' => 'checkbox',
'#title' => t('Field 4'),
'#default_value' => TRUE,
);
$form['container']['subcontainer'] = array(
'#type' => 'container',
);
$form['container']['subcontainer']['field5'] = array(
'#type' => 'checkbox',
'#title' => t('Field 5'),
'#default_value' => TRUE,
);
$form['vertical_tabs2'] = array(
'#type' => 'vertical_tabs',
);
$form['tab3'] = array(
'#type' => 'fieldset',
'#title' => t('Tab 3'),
'#collapsible' => TRUE,
'#group' => 'vertical_tabs2',
);
$form['tab3']['field6'] = array(
'#title' => t('Field 6'),
'#type' => 'checkbox',
'#default_value' => TRUE,
);
$form['actions'] = array(
'#type' => 'actions',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
if (empty($values['field1'])) {
$form_state->setErrorByName('tab1][field1', t('This checkbox inside a vertical tab does not have its default value.'));
}
if ($values['field2'] != 'field2') {
$form_state->setErrorByName('tab2][field2', t('This textfield inside a vertical tab does not have its default value.'));
}
if (empty($values['field3'])) {
$form_state->setErrorByName('fieldset][field3', t('This checkbox inside a fieldset does not have its default value.'));
}
if (empty($values['field4'])) {
$form_state->setErrorByName('container][field4', t('This checkbox inside a container does not have its default value.'));
}
if (empty($values['field5'])) {
$form_state->setErrorByName('container][subcontainer][field5', t('This checkbox inside a nested container does not have its default value.'));
}
if (empty($values['field5'])) {
$form_state->setErrorByName('tab3][field6', t('This checkbox inside a vertical tab whose fieldset access is allowed does not have its default value.'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
drupal_set_message(t('The form submitted correctly.'));
}
}

View file

@ -7,7 +7,6 @@
namespace Drupal\form_test;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
@ -38,7 +37,7 @@ class FormTestArgumentsObject extends ConfigFormBase {
$form['bananas'] = array(
'#type' => 'textfield',
'#default_value' => SafeMarkup::checkPlain($arg),
'#default_value' => $arg,
'#title' => $this->t('Bananas'),
);

View file

@ -4,3 +4,9 @@ httpkernel_test.empty:
_controller: '\Drupal\httpkernel_test\Controller\TestController::get'
requirements:
_access: 'TRUE'
httpkernel_test.teapot:
path: '/httpkernel-test/teapot'
defaults:
_controller: '\Drupal\httpkernel_test\Controller\TestController::teapot'
requirements:
_access: 'TRUE'

View file

@ -21,4 +21,21 @@ class TestController {
return new Response();
}
/**
* Test special header and status code rendering.
*
* @return array
* A render array using features of the 'http_header' directive.
*/
public function teapot() {
$render = [];
$render['#attached']['http_header'][] = ['X-Test-Teapot-Replace', 'This value gets replaced'];
$render['#attached']['http_header'][] = ['X-Test-Teapot-Replace', 'Teapot replaced', TRUE];
$render['#attached']['http_header'][] = ['X-Test-Teapot-No-Replace', 'This value is not replaced'];
$render['#attached']['http_header'][] = ['X-Test-Teapot-No-Replace', 'This one is added', FALSE];
$render['#attached']['http_header'][] = ['X-Test-Teapot', 'Teapot Mode Active'];
$render['#attached']['http_header'][] = ['Status', "418 I'm a teapot."];
return $render;
}
}

View file

@ -1,7 +1,7 @@
# Schema for the configuration files of the Image Test module.
system.image.test_toolkit:
type: mapping
type: config_object
label: 'Image test toolkit'
mapping:
test_parameter:

View file

@ -1,7 +1,7 @@
# Schema for the configuration files of the Hook Menu Test module.
menu_test.menu_item:
type: mapping
type: config_object
label: 'Menu test'
mapping:
title:

View file

@ -27,9 +27,9 @@ function menu_test_menu_links_discovered_alter(&$links) {
}
/**
* Implements hook_menu_local_tasks().
* Implements hook_menu_local_tasks_alter().
*/
function menu_test_menu_local_tasks(&$data, $route_name) {
function menu_test_menu_local_tasks_alter(&$data, $route_name) {
if (in_array($route_name, array('menu_test.tasks_default'))) {
$data['tabs'][0]['foo'] = array(
'#theme' => 'menu_local_task',
@ -50,32 +50,6 @@ function menu_test_menu_local_tasks(&$data, $route_name) {
}
}
/**
* Implements hook_menu_local_tasks_alter().
*
* If the menu_test.settings configuration 'tasks.alter' has been set, adds
* several local tasks to menu-test/tasks.
*/
function menu_test_menu_local_tasks_alter(&$data, $route_name) {
if (!\Drupal::config('menu_test.settings')->get('tasks.alter')) {
return;
}
if (in_array($route_name, array('menu_test.tasks_default', 'menu_test.tasks_empty', 'menu_test.tasks_tasks'))) {
// Rename the default local task from 'View' to 'Show'.
// $data['tabs'] is expected to be keyed by link hrefs.
// The default local task always links to its parent path, which means that
// if the tab root path appears as key in $data['tabs'], then that key is
// the default local task.
$key = $route_name . '_tab';
if (isset($data['tabs'][0][$key])) {
$data['tabs'][0][$key]['#link']['title'] = 'Show it';
}
// Rename the 'foo' task to "Advanced settings" and put it last.
$data['tabs'][0]['foo']['#link']['title'] = 'Advanced settings';
$data['tabs'][0]['foo']['#weight'] = 110;
}
}
/**
* Page callback: Tests the theme negotiation functionality.
*

View file

@ -57,7 +57,7 @@ class TestControllers {
return ['#markup' => SafeMarkup::format("Sometimes there is a placeholder: '@placeholder'.", array('@placeholder' => $placeholder))];
}
else {
return ['#markup' => SafeMarkup::format('Sometimes there is no placeholder.')];
return ['#markup' => 'Sometimes there is no placeholder.'];
}
}

View file

@ -0,0 +1,7 @@
name: module handler test multiple
type: module
description: Test module used to test adding modules with child module.
package: Testing
version: VERSION
core: 8.x
hidden: true

View file

@ -0,0 +1,7 @@
name: module handler test multiple child
type: module
description: Child of test module used to test adding modules with child module.
package: Testing
version: VERSION
core: 8.x
hidden: true

View file

@ -10,6 +10,7 @@ namespace Drupal\system_test\Controller;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Render\SafeString;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Url;
use Symfony\Component\HttpFoundation\RedirectResponse;
@ -101,7 +102,7 @@ class SystemTestController extends ControllerBase {
public function drupalSetMessageTest() {
// Set two messages.
drupal_set_message('First message (removed).');
drupal_set_message('Second message (not removed).');
drupal_set_message(t('Second message with <em>markup!</em> (not removed).'));
// Remove the first.
unset($_SESSION['messages']['status'][0]);
@ -112,6 +113,23 @@ class SystemTestController extends ControllerBase {
drupal_set_message('Duplicated message', 'status', TRUE);
drupal_set_message('Duplicated message', 'status', TRUE);
// Add a SafeString message.
drupal_set_message(SafeString::create('SafeString with <em>markup!</em>'));
// Test duplicate SafeString messages.
drupal_set_message(SafeString::create('SafeString with <em>markup!</em>'));
// Ensure that multiple SafeString messages work.
drupal_set_message(SafeString::create('SafeString2 with <em>markup!</em>'));
// Test mixing of types.
drupal_set_message(SafeString::create('Non duplicate SafeString / string.'));
drupal_set_message('Non duplicate SafeString / string.');
drupal_set_message(SafeString::create('Duplicate SafeString / string.'), 'status', TRUE);
drupal_set_message('Duplicate SafeString / string.', 'status', TRUE);
// Test auto-escape of non safe strings.
drupal_set_message('<em>This<span>markup will be</span> escaped</em>.');
return [];
}

View file

@ -7,7 +7,7 @@
namespace Drupal\test_page_test\Controller;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Html;
/**
* Defines a test controller for page titles.
@ -54,19 +54,13 @@ class Test {
/**
* Defines a controller with a cached render array.
*
* @param bool $mark_safe
* Whether or not to mark the title as safe use SafeMarkup::checkPlain.
*
* @return array
* A render array
*/
public function controllerWithCache($mark_safe = FALSE) {
public function controllerWithCache() {
$build = [];
$build['#title'] = '<span>Cached title</span>';
if ($mark_safe) {
$build['#title'] = SafeMarkup::checkPlain($build['#title']);
}
$build['#cache']['keys'] = ['test_controller', 'with_title', $mark_safe];
$build['#cache']['keys'] = ['test_controller', 'with_title'];
return $build;
}

View file

@ -2,3 +2,7 @@ test_page_test.test_page:
route_name: test_page_test.test_page
title: 'Test front page link'
weight: 0
test_page_test.front_page:
title: 'Home'
route_name: '<front>'
menu_name: main

View file

@ -28,14 +28,6 @@ test_page_test.cached_controller:
requirements:
_access: 'TRUE'
test_page_test.cached_controller.safe:
path: '/test-page-cached-controller-safe'
defaults:
_controller: '\Drupal\test_page_test\Controller\Test::controllerWithCache'
mark_safe: true
requirements:
_access: 'TRUE'
test_page_test.dynamic_title:
path: '/test-page-dynamic-title'
defaults:

View file

@ -143,4 +143,25 @@ class ThemeTestController extends ControllerBase {
return new JsonResponse(['theme_initialized' => $theme_initialized]);
}
/**
* Controller for testing preprocess functions with theme suggestions.
*/
public function preprocessSuggestions() {
return [
[
'#theme' => 'theme_test_preprocess_suggestions',
'#foo' => 'suggestion',
],
[
'#theme' => 'theme_test_preprocess_suggestions',
'#foo' => 'kitten',
],
[
'#theme' => 'theme_test_preprocess_suggestions',
'#foo' => 'monkey',
],
['#theme' => 'theme_test_preprocess_suggestions__kitten__flamingo'],
];
}
}

View file

@ -0,0 +1,4 @@
<div class="suggestion">{{ foo }}</div>
{% if bar %}
<div class="suggestion">{{ bar }}</div>
{% endif %}

View file

@ -55,6 +55,12 @@ function theme_test_theme($existing, $type, $theme, $path) {
$info['test_theme_not_existing_function'] = array(
'function' => 'test_theme_not_existing_function',
);
$items['theme_test_preprocess_suggestions'] = [
'variables' => [
'foo' => '',
'bar' => '',
],
];
return $items;
}
@ -89,6 +95,27 @@ function theme_theme_test_function_template_override($variables) {
return 'theme_test_function_template_override test failed.';
}
/**
* Implements hook_theme_suggestions_HOOK().
*/
function theme_test_theme_suggestions_theme_test_preprocess_suggestions($variables) {
return ['theme_test_preprocess_suggestions__' . $variables['foo']];
}
/**
* Implements hook_preprocess_HOOK().
*/
function theme_test_preprocess_theme_test_preprocess_suggestions(&$variables) {
$variables['foo'] = 'Theme hook implementor=theme_theme_test_preprocess_suggestions().';
}
/**
* Tests a module overriding a default hook with a suggestion.
*/
function theme_test_preprocess_theme_test_preprocess_suggestions__monkey(&$variables) {
$variables['foo'] = 'Monkey';
}
/**
* Prepares variables for test render element templates.
*

View file

@ -103,3 +103,10 @@ theme_test.non_html:
_controller: '\Drupal\theme_test\ThemeTestController::nonHtml'
requirements:
_access: 'TRUE'
theme_test.preprocess_suggestions:
path: '/theme-test/preprocess-suggestions'
defaults:
_controller: '\Drupal\theme_test\ThemeTestController::preprocessSuggestions'
requirements:
_access: 'TRUE'

View file

@ -7,10 +7,13 @@
namespace Drupal\twig_extension_test;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Controller routines for Twig extension test routes.
*/
class TwigExtensionTestController {
use StringTranslationTrait;
/**
* Menu callback for testing Twig filters in a Twig template.
@ -19,6 +22,11 @@ class TwigExtensionTestController {
return array(
'#theme' => 'twig_extension_test_filter',
'#message' => 'Every animal is not a mineral.',
'#safe_join_items' => [
'<em>will be escaped</em>',
$this->t('<em>will be markup</em>'),
['#markup' => '<strong>will be rendered</strong>']
]
);
}

View file

@ -1,3 +1,6 @@
<div class="testfilter">
{{ message|testfilter }}
</div>
<div>
{{ safe_join_items|safe_join('<br/>') }}
</div>

View file

@ -11,7 +11,7 @@
function twig_extension_test_theme($existing, $type, $theme, $path) {
return array(
'twig_extension_test_filter' => array(
'variables' => array('message' => NULL),
'variables' => array('message' => NULL, 'safe_join_items' => NULL),
'template' => 'twig_extension_test.filter',
),
'twig_extension_test_function' => array(

View file

@ -0,0 +1,48 @@
<?php
/**
* @file
* Contains \Drupal\update_script_test\PathProcessor\BrokenInboundPathProcessor.
*/
namespace Drupal\update_script_test\PathProcessor;
use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
use Drupal\Core\State\StateInterface;
use Symfony\Component\HttpFoundation\Request;
/**
* Example path processor which breaks on inbound.
*/
class BrokenInboundPathProcessor implements InboundPathProcessorInterface {
/**
* The state.
*
* @var \Drupal\Core\State\StateInterface
*/
protected $state;
/**
* Constructs a new BrokenInboundPathProcessor instance.
*
* @param \Drupal\Core\State\StateInterface $state
* The state.
*/
public function __construct(StateInterface $state) {
$this->state = $state;
}
/**
* {@inheritdoc}
*/
public function processInbound($path, Request $request) {
if ($this->state->get('update_script_test_broken_inbound', FALSE)) {
throw new \RuntimeException();
}
else {
return $path;
}
}
}

View file

@ -0,0 +1,7 @@
services:
update_script_test.broken_path_processor:
class: Drupal\update_script_test\PathProcessor\BrokenInboundPathProcessor
arguments: ['@state']
tags:
- { name: path_processor_inbound, priority: 1000 }

View file

@ -35,7 +35,14 @@ if ($schema_version >= 8001) {
* Schema version 8001.
*/
function update_test_schema_update_8001() {
$table = [
'fields' => [
'a' => ['type' => 'int', 'not null' => TRUE],
'b' => ['type' => 'blob', 'not null' => FALSE],
],
];
// Add a column.
db_add_index('update_test_schema_table', 'test', ['a']);
db_add_index('update_test_schema_table', 'test', ['a'], $table);
}
}

View file

@ -0,0 +1,330 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\Extension\ModuleHandlerTest.
*/
namespace Drupal\Tests\system\Kernel\Extension;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use \Drupal\Core\Extension\ModuleUninstallValidatorException;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests ModuleHandler functionality.
*
* @group Extension
*/
class ModuleHandlerTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// @todo ModuleInstaller calls system_rebuild_module_data which is part of
// system.module, see https://www.drupal.org/node/2208429.
include_once $this->root . '/core/modules/system/system.module';
// Set up the state values so we know where to find the files when running
// drupal_get_filename().
// @todo Remove as part of https://www.drupal.org/node/2186491
system_rebuild_module_data();
}
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) {
parent::register($container);
// Put a fake route bumper on the container to be called during uninstall.
$container
->register('router.dumper', 'Drupal\Core\Routing\NullMatcherDumper');
}
/**
* The basic functionality of retrieving enabled modules.
*/
function testModuleList() {
$module_list = array();
$this->assertModuleList($module_list, 'Initial');
// Try to install a new module.
$this->moduleInstaller()->install(array('ban'));
$module_list[] = 'ban';
sort($module_list);
$this->assertModuleList($module_list, 'After adding a module');
// Try to mess with the module weights.
module_set_weight('ban', 20);
// Move ban to the end of the array.
unset($module_list[array_search('ban', $module_list)]);
$module_list[] = 'ban';
$this->assertModuleList($module_list, 'After changing weights');
// Test the fixed list feature.
$fixed_list = array(
'system' => 'core/modules/system/system.module',
'menu' => 'core/modules/menu/menu.module',
);
$this->moduleHandler()->setModuleList($fixed_list);
$new_module_list = array_combine(array_keys($fixed_list), array_keys($fixed_list));
$this->assertModuleList($new_module_list, t('When using a fixed list'));
}
/**
* Assert that the extension handler returns the expected values.
*
* @param array $expected_values
* The expected values, sorted by weight and module name.
* @param $condition
*/
protected function assertModuleList(Array $expected_values, $condition) {
$expected_values = array_values(array_unique($expected_values));
$enabled_modules = array_keys($this->container->get('module_handler')->getModuleList());
$this->assertEqual($expected_values, $enabled_modules, format_string('@condition: extension handler returns correct results', array('@condition' => $condition)));
}
/**
* Tests dependency resolution.
*
* Intentionally using fake dependencies added via hook_system_info_alter()
* for modules that normally do not have any dependencies.
*
* To simplify things further, all of the manipulated modules are either
* purely UI-facing or live at the "bottom" of all dependency chains.
*
* @see module_test_system_info_alter()
* @see https://www.drupal.org/files/issues/dep.gv__0.png
*/
function testDependencyResolution() {
$this->enableModules(array('module_test'));
$this->assertTrue($this->moduleHandler()->moduleExists('module_test'), 'Test module is enabled.');
// Ensure that modules are not enabled.
$this->assertFalse($this->moduleHandler()->moduleExists('color'), 'Color module is disabled.');
$this->assertFalse($this->moduleHandler()->moduleExists('config'), 'Config module is disabled.');
$this->assertFalse($this->moduleHandler()->moduleExists('help'), 'Help module is disabled.');
// Create a missing fake dependency.
// Color will depend on Config, which depends on a non-existing module Foo.
// Nothing should be installed.
\Drupal::state()->set('module_test.dependency', 'missing dependency');
drupal_static_reset('system_rebuild_module_data');
try {
$result = $this->moduleInstaller()->install(array('color'));
$this->fail(t('ModuleInstaller::install() throws an exception if dependencies are missing.'));
}
catch (\Drupal\Core\Extension\MissingDependencyException $e) {
$this->pass(t('ModuleInstaller::install() throws an exception if dependencies are missing.'));
}
$this->assertFalse($this->moduleHandler()->moduleExists('color'), 'ModuleHandler::install() aborts if dependencies are missing.');
// Fix the missing dependency.
// Color module depends on Config. Config depends on Help module.
\Drupal::state()->set('module_test.dependency', 'dependency');
drupal_static_reset('system_rebuild_module_data');
$result = $this->moduleInstaller()->install(array('color'));
$this->assertTrue($result, 'ModuleHandler::install() returns the correct value.');
// Verify that the fake dependency chain was installed.
$this->assertTrue($this->moduleHandler()->moduleExists('config') && $this->moduleHandler()->moduleExists('help'), 'Dependency chain was installed.');
// Verify that the original module was installed.
$this->assertTrue($this->moduleHandler()->moduleExists('color'), 'Module installation with dependencies succeeded.');
// Verify that the modules were enabled in the correct order.
$module_order = \Drupal::state()->get('module_test.install_order') ?: array();
$this->assertEqual($module_order, array('help', 'config', 'color'));
// Uninstall all three modules explicitly, but in the incorrect order,
// and make sure that ModuleHandler::uninstall() uninstalled them in the
// correct sequence.
$result = $this->moduleInstaller()->uninstall(array('config', 'help', 'color'));
$this->assertTrue($result, 'ModuleHandler::uninstall() returned TRUE.');
foreach (array('color', 'config', 'help') as $module) {
$this->assertEqual(drupal_get_installed_schema_version($module), SCHEMA_UNINSTALLED, "$module module was uninstalled.");
}
$uninstalled_modules = \Drupal::state()->get('module_test.uninstall_order') ?: array();
$this->assertEqual($uninstalled_modules, array('color', 'config', 'help'), 'Modules were uninstalled in the correct order.');
// Enable Color module again, which should enable both the Config module and
// Help module. But, this time do it with Config module declaring a
// dependency on a specific version of Help module in its info file. Make
// sure that Drupal\Core\Extension\ModuleHandler::install() still works.
\Drupal::state()->set('module_test.dependency', 'version dependency');
drupal_static_reset('system_rebuild_module_data');
$result = $this->moduleInstaller()->install(array('color'));
$this->assertTrue($result, 'ModuleHandler::install() returns the correct value.');
// Verify that the fake dependency chain was installed.
$this->assertTrue($this->moduleHandler()->moduleExists('config') && $this->moduleHandler()->moduleExists('help'), 'Dependency chain was installed.');
// Verify that the original module was installed.
$this->assertTrue($this->moduleHandler()->moduleExists('color'), 'Module installation with version dependencies succeeded.');
// Finally, verify that the modules were enabled in the correct order.
$enable_order = \Drupal::state()->get('module_test.install_order') ?: array();
$this->assertIdentical($enable_order, array('help', 'config', 'color'));
}
/**
* Tests uninstalling a module that is a "dependency" of a profile.
*/
function testUninstallProfileDependency() {
$profile = 'minimal';
$dependency = 'dblog';
$this->setSetting('install_profile', $profile);
// Prime the drupal_get_filename() static cache with the location of the
// minimal profile as it is not the currently active profile and we don't
// yet have any cached way to retrieve its location.
// @todo Remove as part of https://www.drupal.org/node/2186491
drupal_get_filename('profile', $profile, 'core/profiles/' . $profile . '/' . $profile . '.info.yml');
$this->enableModules(array('module_test', $profile));
drupal_static_reset('system_rebuild_module_data');
$data = system_rebuild_module_data();
$this->assertTrue(isset($data[$profile]->requires[$dependency]));
$this->moduleInstaller()->install(array($dependency));
$this->assertTrue($this->moduleHandler()->moduleExists($dependency));
// Uninstall the profile module "dependency".
$result = $this->moduleInstaller()->uninstall(array($dependency));
$this->assertTrue($result, 'ModuleHandler::uninstall() returns TRUE.');
$this->assertFalse($this->moduleHandler()->moduleExists($dependency));
$this->assertEqual(drupal_get_installed_schema_version($dependency), SCHEMA_UNINSTALLED, "$dependency module was uninstalled.");
// Verify that the installation profile itself was not uninstalled.
$uninstalled_modules = \Drupal::state()->get('module_test.uninstall_order') ?: array();
$this->assertTrue(in_array($dependency, $uninstalled_modules), "$dependency module is in the list of uninstalled modules.");
$this->assertFalse(in_array($profile, $uninstalled_modules), 'The installation profile is not in the list of uninstalled modules.');
}
/**
* Tests uninstalling a module that has content.
*/
function testUninstallContentDependency() {
$this->enableModules(array('module_test', 'entity_test', 'text', 'user', 'help'));
$this->assertTrue($this->moduleHandler()->moduleExists('entity_test'), 'Test module is enabled.');
$this->assertTrue($this->moduleHandler()->moduleExists('module_test'), 'Test module is enabled.');
$this->installSchema('user', 'users_data');
$entity_types = \Drupal::entityManager()->getDefinitions();
foreach ($entity_types as $entity_type) {
if ('entity_test' == $entity_type->getProvider()) {
$this->installEntitySchema($entity_type->id());
}
}
// Create a fake dependency.
// entity_test will depend on help. This way help can not be uninstalled
// when there is test content preventing entity_test from being uninstalled.
\Drupal::state()->set('module_test.dependency', 'dependency');
drupal_static_reset('system_rebuild_module_data');
// Create an entity so that the modules can not be disabled.
$entity = entity_create('entity_test', array('name' => $this->randomString()));
$entity->save();
// Uninstalling entity_test is not possible when there is content.
try {
$message = 'ModuleHandler::uninstall() throws ModuleUninstallValidatorException upon uninstalling a module which does not pass validation.';
$this->moduleInstaller()->uninstall(array('entity_test'));
$this->fail($message);
}
catch (ModuleUninstallValidatorException $e) {
$this->pass(get_class($e) . ': ' . $e->getMessage());
}
// Uninstalling help needs entity_test to be un-installable.
try {
$message = 'ModuleHandler::uninstall() throws ModuleUninstallValidatorException upon uninstalling a module which does not pass validation.';
$this->moduleInstaller()->uninstall(array('help'));
$this->fail($message);
}
catch (ModuleUninstallValidatorException $e) {
$this->pass(get_class($e) . ': ' . $e->getMessage());
}
// Deleting the entity.
$entity->delete();
$result = $this->moduleInstaller()->uninstall(array('help'));
$this->assertTrue($result, 'ModuleHandler::uninstall() returns TRUE.');
$this->assertEqual(drupal_get_installed_schema_version('entity_test'), SCHEMA_UNINSTALLED, "entity_test module was uninstalled.");
}
/**
* Tests whether the correct module metadata is returned.
*/
function testModuleMetaData() {
// Generate the list of available modules.
$modules = system_rebuild_module_data();
// Check that the mtime field exists for the system module.
$this->assertTrue(!empty($modules['system']->info['mtime']), 'The system.info.yml file modification time field is present.');
// Use 0 if mtime isn't present, to avoid an array index notice.
$test_mtime = !empty($modules['system']->info['mtime']) ? $modules['system']->info['mtime'] : 0;
// Ensure the mtime field contains a number that is greater than zero.
$this->assertTrue(is_numeric($test_mtime) && ($test_mtime > 0), 'The system.info.yml file modification time field contains a timestamp.');
}
/**
* Tests whether module-provided stream wrappers are registered properly.
*/
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 = \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
// \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 = \Drupal::service('stream_wrapper_manager')->getWrappers();
$this->assertTrue(isset($stream_wrappers['dummy']));
}
/**
* Tests whether the correct theme metadata is returned.
*/
function testThemeMetaData() {
// Generate the list of available themes.
$themes = \Drupal::service('theme_handler')->rebuildThemeData();
// Check that the mtime field exists for the bartik theme.
$this->assertTrue(!empty($themes['bartik']->info['mtime']), 'The bartik.info.yml file modification time field is present.');
// Use 0 if mtime isn't present, to avoid an array index notice.
$test_mtime = !empty($themes['bartik']->info['mtime']) ? $themes['bartik']->info['mtime'] : 0;
// Ensure the mtime field contains a number that is greater than zero.
$this->assertTrue(is_numeric($test_mtime) && ($test_mtime > 0), 'The bartik.info.yml file modification time field contains a timestamp.');
}
/**
* Returns the ModuleHandler.
*
* @return \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected function moduleHandler() {
return $this->container->get('module_handler');
}
/**
* Returns the ModuleInstaller.
*
* @return \Drupal\Core\Extension\ModuleInstallerInterface
*/
protected function moduleInstaller() {
return $this->container->get('module_installer');
}
}

View file

@ -0,0 +1,100 @@
<?php
/**
* @file
* Contains \Drupal\system\Tests\PhpStorage\PhpStorageFactoryTest.
*/
namespace Drupal\Tests\system\Kernel\PhpStorage;
use Drupal\Component\PhpStorage\MTimeProtectedFileStorage;
use Drupal\Core\PhpStorage\PhpStorageFactory;
use Drupal\Core\Site\Settings;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\system\PhpStorage\MockPhpStorage;
use Drupal\KernelTests\KernelTestBase;
/**
* Tests the PHP storage factory.
*
* @group PhpStorage
* @see \Drupal\Core\PhpStorage\PhpStorageFactory
*/
class PhpStorageFactoryTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
// Empty the PHP storage settings, as KernelTestBase sets it by default.
$settings = Settings::getAll();
unset($settings['php_storage']);
new Settings($settings);
}
/**
* Tests the get() method with no settings.
*/
public function testGetNoSettings() {
$php = PhpStorageFactory::get('test');
// This should be the default class used.
$this->assertTrue($php instanceof MTimeProtectedFileStorage, 'An MTimeProtectedFileStorage instance was returned with no settings.');
}
/**
* Tests the get() method using the 'default' settings.
*/
public function testGetDefault() {
$this->setSettings();
$php = PhpStorageFactory::get('test');
$this->assertTrue($php instanceof MockPhpStorage, 'A FileReadOnlyStorage instance was returned with default settings.');
}
/**
* Tests the get() method with overridden settings.
*/
public function testGetOverride() {
$this->setSettings('test');
$php = PhpStorageFactory::get('test');
// The FileReadOnlyStorage should be used from settings.
$this->assertTrue($php instanceof MockPhpStorage, 'A MockPhpStorage instance was returned from overridden settings.');
// Test that the name is used for the bin when it is NULL.
$this->setSettings('test', array('bin' => NULL));
$php = PhpStorageFactory::get('test');
$this->assertTrue($php instanceof MockPhpStorage, 'An MockPhpStorage instance was returned from overridden settings.');
$this->assertIdentical('test', $php->getConfigurationValue('bin'), 'Name value was used for bin.');
// Test that a default directory is set if it's empty.
$this->setSettings('test', array('directory' => NULL));
$php = PhpStorageFactory::get('test');
$this->assertTrue($php instanceof MockPhpStorage, 'An MockPhpStorage instance was returned from overridden settings.');
$this->assertIdentical(PublicStream::basePath() . '/php', $php->getConfigurationValue('directory'), 'Default file directory was used.');
// Test that a default storage class is set if it's empty.
$this->setSettings('test', array('class' => NULL));
$php = PhpStorageFactory::get('test');
$this->assertTrue($php instanceof MTimeProtectedFileStorage, 'An MTimeProtectedFileStorage instance was returned from overridden settings with no class.');
}
/**
* Sets the Settings() singleton.
*
* @param string $name
* The storage bin name to set.
* @param array $configuration
* An array of configuration to set. Will be merged with default values.
*/
protected function setSettings($name = 'default', array $configuration = array()) {
$settings['php_storage'][$name] = $configuration + array(
'class' => 'Drupal\system\PhpStorage\MockPhpStorage',
'directory' => 'tmp://',
'secret' => $this->randomString(),
'bin' => 'test',
);
new Settings($settings);
}
}

View file

@ -7,8 +7,10 @@
namespace Drupal\Tests\system\Unit\Breadcrumbs;
use Drupal\Core\Link;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Link;
use Drupal\Core\Access\AccessResultAllowed;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\TranslationInterface;
use Drupal\Core\Url;
@ -16,6 +18,7 @@ use Drupal\Core\Utility\LinkGeneratorInterface;
use Drupal\system\PathBasedBreadcrumbBuilder;
use Drupal\Tests\UnitTestCase;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RequestContext;
@ -117,6 +120,15 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
);
$this->builder->setStringTranslation($this->getStringTranslationStub());
$cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
->disableOriginalConstructor()
->getMock();
$cache_contexts_manager->expects($this->any())
->method('validate_tokens');
$container = new Container();
$container->set('cache_contexts_manager', $cache_contexts_manager);
\Drupal::setContainer($container);
}
/**
@ -129,8 +141,11 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
->method('getPathInfo')
->will($this->returnValue('/'));
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals(array(), $links);
$breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals([], $breadcrumb->getLinks());
$this->assertEquals(['url.path'], $breadcrumb->getCacheContexts());
$this->assertEquals([], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
}
/**
@ -143,8 +158,11 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
->method('getPathInfo')
->will($this->returnValue('/example'));
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals(array(0 => new Link('Home', new Url('<front>'))), $links);
$breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
$this->assertEquals(['url.path'], $breadcrumb->getCacheContexts());
$this->assertEquals([], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
}
/**
@ -175,8 +193,11 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
$this->setupAccessManagerToAllow();
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals(array(0 => new Link('Home', new Url('<front>')), 1 => new Link('Example', new Url('example'))), $links);
$breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals([0 => new Link('Home', new Url('<front>')), 1 => new Link('Example', new Url('example'))], $breadcrumb->getLinks());
$this->assertEquals(['url.path', 'user.permissions'], $breadcrumb->getCacheContexts());
$this->assertEquals([], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
}
/**
@ -213,14 +234,21 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
}
}));
$this->setupAccessManagerToAllow();
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals(array(
$this->accessManager->expects($this->any())
->method('check')
->willReturnOnConsecutiveCalls(
AccessResult::allowed()->cachePerPermissions(),
AccessResult::allowed()->addCacheContexts(['bar'])->addCacheTags(['example'])
);
$breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals([
new Link('Home', new Url('<front>')),
new Link('Example', new Url('example')),
new Link('Bar', new Url('example_bar')),
), $links);
], $breadcrumb->getLinks());
$this->assertEquals(['bar', 'url.path', 'user.permissions'], $breadcrumb->getCacheContexts());
$this->assertEquals(['example'], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
}
/**
@ -241,10 +269,13 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
->method('matchRequest')
->will($this->throwException(new $exception_class($exception_argument)));
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
// No path matched, though at least the frontpage is displayed.
$this->assertEquals(array(0 => new Link('Home', new Url('<front>'))), $links);
$this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
$this->assertEquals(['url.path'], $breadcrumb->getCacheContexts());
$this->assertEquals([], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
}
/**
@ -282,10 +313,13 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
->method('matchRequest')
->will($this->returnValue(array()));
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
// No path matched, though at least the frontpage is displayed.
$this->assertEquals(array(0 => new Link('Home', new Url('<front>'))), $links);
$this->assertEquals([0 => new Link('Home', new Url('<front>'))], $breadcrumb->getLinks());
$this->assertEquals(['url.path'], $breadcrumb->getCacheContexts());
$this->assertEquals([], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
}
/**
@ -329,8 +363,11 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
->with($this->anything(), $route_1)
->will($this->returnValue('Admin'));
$links = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals(array(0 => new Link('Home', new Url('<front>')), 1 => new Link('Admin', new Url('user_page'))), $links);
$breadcrumb = $this->builder->build($this->getMock('Drupal\Core\Routing\RouteMatchInterface'));
$this->assertEquals([0 => new Link('Home', new Url('<front>')), 1 => new Link('Admin', new Url('user_page'))], $breadcrumb->getLinks());
$this->assertEquals(['url.path', 'user.permissions'], $breadcrumb->getCacheContexts());
$this->assertEquals([], $breadcrumb->getCacheTags());
$this->assertEquals(Cache::PERMANENT, $breadcrumb->getCacheMaxAge());
}
/**
@ -339,7 +376,7 @@ class PathBasedBreadcrumbBuilderTest extends UnitTestCase {
public function setupAccessManagerToAllow() {
$this->accessManager->expects($this->any())
->method('check')
->willReturn(TRUE);
->willReturn((new AccessResultAllowed())->cachePerPermissions());
}
protected function setupStubPathProcessor() {

View file

@ -100,7 +100,10 @@ class MenuLinkTreeTest extends UnitTestCase {
*
* @dataProvider providerTestBuildCacheability
*/
public function testBuildCacheability($description, $tree, $expected_build) {
public function testBuildCacheability($description, $tree, $expected_build, $access, array $access_cache_contexts = []) {
if ($access !== NULL) {
$access->addCacheContexts($access_cache_contexts);
}
$build = $this->menuLinkTree->build($tree);
sort($expected_build['#cache']['contexts']);
$this->assertEquals($expected_build, $build, $description);
@ -175,13 +178,12 @@ class MenuLinkTreeTest extends UnitTestCase {
'description' => 'Empty tree.',
'tree' => [],
'expected_build' => $base_expected_build_empty,
'access' => NULL,
'access_cache_contexts' => [],
];
for ($i = 0; $i < count($access_scenarios); $i++) {
list($access, $access_cache_contexts) = $access_scenarios[$i];
if ($access !== NULL) {
$access->addCacheContexts($access_cache_contexts);
}
for ($j = 0; $j < count($links_scenarios); $j++) {
$links = $links_scenarios[$j];
@ -203,6 +205,8 @@ class MenuLinkTreeTest extends UnitTestCase {
'description' => "Single-item tree; access=$i; link=$j.",
'tree' => $tree,
'expected_build' => $expected_build,
'access' => $access,
'access_cache_contexts' => $access_cache_contexts,
];
// Single-level tree.
@ -221,6 +225,8 @@ class MenuLinkTreeTest extends UnitTestCase {
'description' => "Single-level tree; access=$i; link=$j.",
'tree' => $tree,
'expected_build' => $expected_build,
'access' => $access,
'access_cache_contexts' => $access_cache_contexts,
];
// Multi-level tree.
@ -251,6 +257,8 @@ class MenuLinkTreeTest extends UnitTestCase {
'description' => "Multi-level tree; access=$i; link=$j.",
'tree' => $tree,
'expected_build' => $expected_build,
'access' => $access,
'access_cache_contexts' => $access_cache_contexts,
];
}
}

View file

@ -0,0 +1,49 @@
<?php
/**
* @file
* Contains \Drupal\Tests\system\Unit\Plugin\migrate\source\MenuTest.
*/
namespace Drupal\Tests\system\Unit\Plugin\migrate\source;
use Drupal\Tests\migrate\Unit\MigrateSqlSourceTestCase;
/**
* Tests menu source plugin.
*
* @group system
*/
class MenuTest extends MigrateSqlSourceTestCase {
const PLUGIN_CLASS = 'Drupal\system\Plugin\migrate\source\Menu';
protected $migrationConfiguration = array(
'id' => 'test',
'source' => array(
'plugin' => 'menu',
),
);
protected $expectedResults = array(
array(
'menu_name' => 'menu-name-1',
'title' => 'menu custom value 1',
'description' => 'menu custom description value 1',
),
array(
'menu_name' => 'menu-name-2',
'title' => 'menu custom value 2',
'description' => 'menu custom description value 2',
),
);
/**
* {@inheritdoc}
*/
protected function setUp() {
$this->databaseContents['menu_custom'] = $this->expectedResults;
parent::setUp();
}
}

View file

@ -4,6 +4,7 @@
* @file
* Add hooks for tests to use.
*/
use Drupal\views\Plugin\views\cache\CachePluginBase;
use Drupal\views\ViewExecutable;
/**
@ -17,7 +18,7 @@ function test_basetheme_views_pre_render(ViewExecutable $view) {
/**
* Implements hook_views_post_render().
*/
function test_basetheme_views_post_render(ViewExecutable $view) {
function test_basetheme_views_post_render(ViewExecutable $view, &$output, CachePluginBase $cache) {
// We append the function name to the title for test to check for.
$view->setTitle($view->getTitle() . ":" . __FUNCTION__);
}

View file

@ -5,6 +5,7 @@
* Add hooks for tests to use.
*/
use Drupal\views\Plugin\views\cache\CachePluginBase;
use Drupal\views\ViewExecutable;
/**
@ -18,9 +19,12 @@ function test_subtheme_views_pre_render(ViewExecutable $view) {
/**
* Implements hook_views_post_render().
*/
function test_subtheme_views_post_render(ViewExecutable $view) {
function test_subtheme_views_post_render(ViewExecutable $view, &$output, CachePluginBase $cache) {
// We append the function name to the title for test to check for.
$view->setTitle($view->getTitle() . ":" . __FUNCTION__);
if ($view->id() == 'test_page_display') {
$output['#rows'][0]['#title'] = t('%total_rows items found.', array('%total_rows' => $view->total_rows));
}
}
/**

View file

@ -0,0 +1,4 @@
<div class="suggestion">{{ foo }}</div>
{% if bar %}
<div class="suggestion">{{ bar }}</div>
{% endif %}

View file

@ -0,0 +1 @@
<div class="suggestion">{{ foo }}</div>

View file

@ -105,3 +105,47 @@ function test_theme_theme_test_function_suggestions__module_override($variables)
function test_theme_theme_registry_alter(&$registry) {
$registry['theme_test_template_test']['variables']['additional'] = 'value';
}
/**
* Tests a theme overriding a suggestion of a base theme hook.
*/
function test_theme_theme_test_preprocess_suggestions__kitten__meerkat($variables) {
return 'Theme hook implementor=test_theme_theme_test__suggestion(). Foo=' . $variables['foo'];
}
/**
* Tests a theme overriding a default hook with a suggestion.
*
* Implements hook_preprocess_HOOK().
*/
function test_theme_preprocess_theme_test_preprocess_suggestions(&$variables) {
$variables['foo'] = 'Theme hook implementor=test_theme_preprocess_theme_test_preprocess_suggestions().';
}
/**
* Tests a theme overriding a default hook with a suggestion.
*/
function test_theme_preprocess_theme_test_preprocess_suggestions__suggestion(&$variables) {
$variables['foo'] = 'Suggestion';
}
/**
* Tests a theme overriding a default hook with a suggestion.
*/
function test_theme_preprocess_theme_test_preprocess_suggestions__kitten(&$variables) {
$variables['foo'] = 'Kitten';
}
/**
* Tests a theme overriding a default hook with a suggestion.
*/
function test_theme_preprocess_theme_test_preprocess_suggestions__kitten__flamingo(&$variables) {
$variables['bar'] = 'Flamingo';
}
/**
* Tests a preprocess function with suggestions.
*/
function test_theme_preprocess_theme_test_preprocess_suggestions__kitten__meerkat__tarsier__moose(&$variables) {
$variables['bar'] = 'Moose';
}